/* ═══════════════════════════════════════════════════════════════════
   MARSHA THEME CORE F10  —  PALANTIR GRADE
   Build : 2026.03.17_THEME_MARSHA_DEEP_SPACE
   Fuente única de verdad para variables y estilos base.
   index.html NO debe redefinir ninguna de estas variables.
═══════════════════════════════════════════════════════════════════ */

@font-face {
    font-family: 'Orbitron';
    src: url('/fonts/Orbitron.ttf') format('truetype');
    font-weight: 400 800;
    font-display: swap;
}
@font-face {
    font-family: 'Inter';
    src: url('/fonts/Inter.woff2') format('woff2');
    font-weight: 300 600;
    font-display: swap;
}

/* ── TOKENS — MARSHA SYSTEM · DEEP SPACE SOVEREIGN ─────────────── */
:root {
    /* Fondos — iceberg profundo */
    --bg:               #080c10;
    --bg-panel:         #0a0e14;
    --bg-surface:       #0d1117;
    --bg-glass:         rgba(8,12,16,0.92);

    /* Bordes — sistema unificado Palantir */
    --border:           rgba(255,255,255,0.07);
    --border-mid:       rgba(255,255,255,0.10);
    --border-active:    rgba(0,128,255,0.45);
    --border-glow:      rgba(0,128,255,0.12);

    /* Gradientes MARSHA — austeridad cromática */
    --marsha-gradient:  linear-gradient(135deg, #0080FF 0%, #0055CC 100%);
    --nexus-gradient:   linear-gradient(135deg, #0080FF 0%, #0055CC 100%);
    --vault-gradient:   linear-gradient(135deg, #0080FF 0%, #0055CC 100%);
    --herald-gradient:  linear-gradient(135deg, #0080FF 0%, #0055CC 100%);
    --neural-gradient:  radial-gradient(ellipse at center, rgba(0,128,255,0.08) 0%, transparent 70%);

    /* Colores principales — un solo acento azul steel */
    --mia-blue:         #0080FF;   /* azul steel — acento único MARSHA */
    --mia-magenta:      #0066DD;   /* gris slate — elementos secundarios */
    --mia-deep:         #0055CC;   /* azul profundo — hover states */

    /* Texto — jerarquía clara */
    --text-main:        #d4d8e0;   /* gris perla — texto principal */
    --text-muted:       #3a3d4a;   /* gris oscuro — texto silenciado */
    --text-secondary:   #6b7280;   /* gris medio — texto secundario */
    --text-data:        #4DA6FF;   /* azul steel — datos activos */

    /* Estados — colores solo para alertas reales */
    --green:            #3FB950;   /* verde real — presencia online */
    --red:              #e5534b;   /* rojo apagado — emergencia */
    --amber:            #d97706;   /* ámbar — advertencia */

    /* Glow — mínimo, solo en elementos críticos */
    --glow-cyan:        0 0 12px rgba(0,128,255,0.2);
    --glow-magenta:     0 0 12px rgba(0,128,255,0.15);
    --glow-green:       0 0 8px rgba(63,185,80,0.2);

    /* Layout */
    --panel-w:          240px;
    --rail-w:            40px;   /* FEAT-PANELS · ancho del riel de iconos */
    --rail-handle:       14px;   /* FEAT-PANELS · ancho de la lengueta de borde · la franja visible al plegar es riel+lengueta */
    --header-h:         64px;
    --radius:           2px;
    --radius-lg:        4px;
    --transition:       0.3s cubic-bezier(0.4,0,0.2,1);

    /* P1-10 · Transitions scale tokens · easing material standard
       --transition-fast (150ms) · microinteractions, hover, tap feedback
       --transition-base (220ms) · default UI transitions
       --transition-slow (320ms) · panel/modal open/close · page transitions
       Usar var(--transition-base) en lugar de transition: 0.3s ad-hoc */
    --transition-fast:  150ms cubic-bezier(0.4, 0, 0.2, 1);
    --transition-base:  220ms cubic-bezier(0.4, 0, 0.2, 1);
    --transition-slow:  320ms cubic-bezier(0.4, 0, 0.2, 1);

    /* ── SYNARIS VIVO · TOKENS DE MOVIMIENTO ──────────────────────────────────
     * Un solo sitio donde se decide como se mueve esta casa. No son sinonimos de
     * los --transition-* de arriba: aquellos son la transicion GENERICA heredada
     * (220-320ms, curva simetrica); estos son el lenguaje nuevo, mas corto y con
     * salida desacelerada, para lo que aparece y se coloca.
     * Se declaran como VARIABLES y no como valores sueltos por fichero justamente
     * para que la configuracion de abajo pueda apagarlos todos de una vez: si el
     * numero esta escrito a mano en veinte sitios, no hay interruptor posible.
     *
     * 120ms para lo que responde a un dedo (marcados, hover, foco) y 160ms para lo
     * que se desplaza. Curva de salida pura —arranca rapido y frena— porque lo que
     * se anima aqui ya ha empezado a existir: no hay nada que "coger carrerilla".
     * El deslizamiento es de 8px: suficiente para que el ojo lea "ha entrado desde
     * ahi" y demasiado poco para que haya que esperarlo. */
    --mov-rapido:       120ms;
    --mov-normal:       160ms;
    --mov-curva:        cubic-bezier(0, 0, 0.2, 1);   /* ease-out · decelerate */
    --mov-desliz:       8px;
    /* ALCANCE DEL PULSO · 0 apaga la animacion sin tener que preguntar por el modo
     * desde ninguna regla: la propia animacion se queda en nada. Sutil no es "medio
     * completo" por capricho: un aviso que apenas se insinua sigue siendo un aviso,
     * y quien elige sutil esta pidiendo enterarse sin que le interrumpan. */
    --pulso-alcance:    0.45;   /* SUTIL, el valor por defecto */
    /* ESCALA CALIBRABLE · alfa y duracion son ahora tokens DIRECTOS y no el resultado de
     * multiplicar por 0,55 dentro del fotograma. El motivo es practico: estos dos numeros
     * los fija el CEO mirando la pantalla con el boton PROBAR EFECTOS, y un valor que se
     * puede nombrar y sellar aqui vale mas que uno que hay que despejar de una formula.
     * SUELO: 0,247 es el sutil que se midio y se aprobo como minimo perceptible; no baja
     * de ahi. COMPLETO tiene que ser evidente, asi que sube en las DOS dimensiones —mas
     * alfa y mas tiempo en pantalla—, porque subir solo el alfa deja un destello igual de
     * corto y lo que falla de reojo es la duracion tanto como el brillo. */
    --pulso-alfa:       0.247;  /* SUTIL · suelo aprobado */
    --pulso-dur:        640ms;

    /* ── SYNARIS VIVO · TOKENS DE MATERIAL ────────────────────────────────────
     * El material de esta casa es OPACO. Los paneles ya lo eran (fondo solido,
     * sin desenfoque); lo que les faltaba era el filo que separa una superficie
     * de la siguiente sin pintar una raya gruesa.
     * La profundidad de lo que FLOTA se hace con sombra, no con desenfoque: una
     * sombra dice "esto esta por encima" sin tocar lo que hay debajo, y cuesta
     * un solo pintado en vez de un desenfoque por cuadro. */
    /* ESCALA DE PROFUNDIDAD · los tres peldanos NO son inventados: son los valores que la
     * hoja ya usaba a mano, medidos y puestos nombre. Nombrar lo que existe no cambia como
     * se ve nada hoy y da vocabulario para manana; estrenar una escala nueva habria
     * repintado media aplicacion en un commit que dice "tokens". */
    --sombra-1:         0 2px 6px  rgba(0,0,0,0.30);   /* controles pequenos que asoman */
    --sombra-2:         0 4px 12px rgba(0,0,0,0.40);   /* elementos flotantes (avisos)   */
    --sombra-3:         0 16px 48px rgba(0,0,0,0.60);  /* modales                        */

    /* Escala tipográfica soberana (legacy · usar --font-size-* nuevos) */
    --fs-brand:         0.75rem;   /* [M] SYNARIS sidebar */
    --fs-label:         0.38rem;   /* sección labels */
    --fs-meta:          0.42rem;   /* metadata secundaria */
    --fs-body:          0.6rem;    /* texto operativo */
    --fs-tab:           0.55rem;   /* tabs navegación */
    --fs-min-legible:   0.75rem;   /* COSMETIC · mínimo legible (12px) · cuerpo de diálogos + listas que se LEEN (no labels decorativos) · criterio único */

    /* P1-9 · Typography scale tokens · clamp() fluido · floor 12px legibility
       Uso recomendado: var(--font-size-base) en body · resto vía utility */
    --font-size-xs:    clamp(0.75rem, 1.8vw, 0.85rem);  /* 12-13.6px · labels metadata */
    --font-size-sm:    clamp(0.8125rem, 2.0vw, 0.95rem); /* 13-15.2px · UI body chico */
    --font-size-base:  clamp(0.875rem, 2.4vw, 1rem);    /* 14-16px · body principal */
    --font-size-lg:    clamp(1rem, 2.8vw, 1.15rem);     /* 16-18.4px · headings */
    --font-size-xl:    clamp(1.125rem, 3.4vw, 1.35rem); /* 18-21.6px · titles */
}

/* Modos por módulo — mismo acento, consistencia total */
body.p2p-active   { --mia-blue: #0080FF; }
body.vault-active { --mia-blue: #0080FF; }
body.mail-active  { --mia-blue: #0080FF; }

/* ── RESET ───────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* P1-17 · Disable iOS gray tap highlight globally · use :active for feedback (P1-12) */
* { -webkit-tap-highlight-color: transparent; }

/* ── SYNARIS VIVO · CONFIGURACION DE EFECTOS ──────────────────────────────────
 * Tres niveles sobre los MISMOS tokens, asi que cambiar de nivel no toca ni una
 * regla de componente: solo cambia lo que valen las variables.
 *   sutil (DEFECTO)  · movimiento si, pulso no. Es lo que hay sin atributo, para
 *                      que la aplicacion se comporte bien antes de que exista la
 *                      pantalla de ajustes y para que un fallo al leer la
 *                      preferencia no deje la interfaz muerta.
 *   apagado          · ni movimiento ni pulso. Duracion 1ms y no 0: a 0 algunos
 *                      navegadores no emiten transitionend, y hay codigo que
 *                      espera ese evento para limpiar.
 *   completo         · movimiento + pulso por evento real.
 * NADA DE ANIMACION AMBIENTAL: ningun token de aqui hace que algo se mueva solo.
 * Todos describen COMO se mueve algo cuando ya ha pasado una cosa. */
body[data-efectos="sutil"]    { --pulso-alcance: 0.45; --pulso-alfa: 0.247; --pulso-dur: 640ms; }
body[data-efectos="completo"] { --pulso-alcance: 1;    --pulso-alfa: 0.60;  --pulso-dur: 960ms; }
body[data-efectos="apagado"] {
    --mov-rapido: 1ms;
    --mov-normal: 1ms;
    --mov-desliz: 0px;
    --pulso-alcance: 0;
    --pulso-alfa: 0;
    --pulso-dur: 1ms;
}
/* La accesibilidad MANDA sobre la preferencia del usuario: si el sistema pide menos
 * movimiento, no hay modo "completo" que valga. Va despues del bloque de arriba a
 * proposito —misma especificidad, gana el ultimo— y ademas dentro de una consulta de
 * medio, que no se puede desactivar desde la aplicacion. */
@media (prefers-reduced-motion: reduce) {
    body, body[data-efectos="sutil"], body[data-efectos="completo"] {
        --mov-rapido: 1ms;
        --mov-normal: 1ms;
        --mov-desliz: 0px;
        --pulso-alcance: 0;
        --pulso-alfa: 0;
        --pulso-dur: 1ms;
    }
}

/* P1-11 · Honor prefers-reduced-motion · accessibility (WCAG 2.1 SC 2.3.3) */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
    /* SEC-AURA-RESTORE 2026-05-11 · carve-out AURA biometric scanner
     * Functional feedback (process-state indicator), NOT decorative.
     * WCAG SC 2.3.3 explicitly permits essential motion that conveys meaning.
     * Selectors: pre-login rotating rings · wolf eyes pulse · scan line descent. */
    .wolf-scan-line {
        animation-duration: 1.9s !important;
        animation-iteration-count: 2 !important;
    }
    .wolf-eyes {
        animation-duration: 1.6s !important;
        animation-iteration-count: infinite !important;
    }
    #auth-shield.scanning .wolf-eyes {
        animation-duration: 0.5s !important;
    }
    .wolf-container::before,
    .wolf-container::after {
        animation-iteration-count: infinite !important;
    }
    .wolf-container::before { animation-duration: 4s !important; }
    .wolf-container::after  { animation-duration: 7s !important; }
}


/* ─────────────────────────────────────────────────────────────────────
 * FEAT-PANELS · PLEGADO DE PANELES LATERALES + RIEL DE 40px
 * El mecanismo (.collapsed + margen negativo) ya existia en el arbol pero era
 * INALCANZABLE: ui_core enganchaba el toggle a #btn-toggle-l/#btn-toggle-r y esos
 * botones nunca existieron en index.html. Aqui se completa.
 * ESCRITORIO-ONLY EXPLICITO: en <=768px los dos paneles son display:none (regla
 * .left-panel/.right-panel del breakpoint), asi que chevron y riel se ocultan por
 * regla propia en vez de depender de que gane el !important ajeno.
 * ───────────────────────────────────────────────────────────────────── */
/* CHEVRON = LENGUETA DE BORDE · vive en la LINEA DIVISORIA, centrada verticalmente, nunca dentro
   del panel: asi no puede pisar el contenido de ningun modulo (lo pisaba con 'TÚNEL NEXO').
   Ocupa el canal del padding lateral del aside (16px), pegada al borde interior. */
.panel-chevron {
    position: absolute;
    top: 50%; transform: translateY(-50%);
    width: 14px; height: 44px;
    display: flex; align-items: center; justify-content: center;
    background: rgba(0,128,255,0.08);
    border: 1px solid rgba(77,166,255,0.4);
    color: #4DA6FF;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.6rem; line-height: 1;
    cursor: pointer; z-index: 30; padding: 0;
    transition: border-color 0.15s, background 0.15s;   /* patron .nx-btn */
}
.panel-chevron:hover { background: rgba(0,128,255,0.16); border-color: rgba(77,166,255,0.7); }
/* BLANCO DE CLIC · la lengueta MIDE 14x44px: un objetivo de 14px de ancho se falla con
   facilidad, y fallarlo se siente como 'el boton no responde' (el clic cae en el panel, que no
   escucha). Se amplia el area SIN tocar el aspecto aprobado: un ::before invisible que extiende
   el blanco 12px a cada lado y 8px arriba y abajo -> 38x60px reales. El dibujo no cambia. */
.panel-chevron::before {
    content: ''; position: absolute;
    top: -8px; bottom: -8px; left: -12px; right: -12px;
}
/* EL BLANCO CRECE HACIA FUERA, NUNCA HACIA EL TEXTO DE SU PROPIO PANEL.
   Ensanchar 12px por los DOS lados hacia que la lengueta derecha se comiera 10px de la
   columna de texto del panel: medido con la pagina poblada, pinchando a 4px dentro de la
   columna, elementFromPoint devolvia btn-toggle-r y no el contenido. O sea que el punto de
   estado de la sala y la primera letra de cada titulo no respondian al clic.
   No pasaba a la izquierda por un accidente de maquetacion: los hijos del panel izquierdo
   llevan padding propio (.logo-area va a 20px) y su texto acaba en 212, con 14px de aire;
   los del derecho arrancan clavados en el borde del canal, con 2px. Misma lengueta,
   colchon siete veces menor.
   Cada lengueta vive pegada al borde INTERIOR de su panel, asi que su lado "de fuera" es el
   que da al centro: por ahi puede crecer sin quitarle nada a nadie.
   COSTE DECLARADO: el blanco pasa de 38x60 a 26x60. Sigue por encima del minimo de 24px y
   el alto no se toca. Un objetivo de 26x60 se acierta; un texto que no responde parece una
   aplicacion rota. */
#btn-toggle-l::before { left: 0;     right: -12px; }
#btn-toggle-r::before { left: -12px; right: 0;     }
/* la lengueta se pega al borde interior: redondeada solo por fuera, como una pestania */
/* la lengueta izquierda baja media cabecera: #panel-R arranca 64px mas abajo (F16.10), asi que
   dos lenguetas a top:50% de su propio panel quedaban a 32px de diferencia. Mismo eje que el riel. */
#btn-toggle-l { right: 0; top: calc(50% + var(--header-h) / 2); border-radius: 4px 0 0 4px; border-right: none; }
#btn-toggle-r { left: 0;  border-radius: 0 4px 4px 0; border-left: none;  }

/* DIRECCION DINAMICA por CSS · el glifo no vive en el HTML, asi no puede desincronizarse del
   estado. Izquierda: '<' abierto (pliega hacia dentro) / '>' plegado (despliega). Derecha, espejo. */
.panel-chevron { font-size: 0; }
.panel-chevron::after { font-size: 0.6rem; }
#btn-toggle-l::after { content: '\2039'; }
#btn-toggle-r::after { content: '\203A'; }
.left-panel.collapsed  #btn-toggle-l::after { content: '\203A'; }
.right-panel.collapsed #btn-toggle-r::after { content: '\2039'; }

/* plegado · se ocultan los hijos normales y quedan lengueta + riel + marca */
.left-panel.collapsed  > *:not(.panel-chevron):not(.panel-rail):not(.panel-brand),
.right-panel.collapsed > *:not(.panel-chevron):not(.panel-rail) { display: none !important; }

/* LA M, PLEGADO · al plegar desaparecia toda la identidad y el riel quedaba anonimo.
   Es la MISMA imagen que .sidebar-brand-img usa abierto, con su mismo tamano (40px) y
   su mismo halo; lo unico que se va es la palabra SYNARIS.
   MEDIDO: abierta, la imagen ocupa y=12..52 dentro de una .logo-area de 64px. La marca
   plegada se ancla en top:12px con el mismo alto, asi que cae EXACTAMENTE donde estaba
   y al plegar no da un salto.
   Va anclada al panel y no dentro del riel a proposito: el riel arranca en y=128 (por
   la compensacion de las dos cabeceras) y meterla ahi la habria bajado 116px o habria
   empujado los iconos, que estan donde el CEO los aprobo.
   right:0 porque con el panel plegado la franja visible es el borde DERECHO del aside
   (medido: el panel va de -186 a 54, y lo que se ve es 0..54). El ancho es el de la
   franja completa —riel + lengueta— que es el mismo con el que se centran los iconos. */
.panel-brand { display: none; }
.left-panel.collapsed .panel-brand {
    display: block;
    position: absolute;
    top: 12px;
    right: 0;
    width: calc(var(--rail-w) + var(--rail-handle));
    height: 40px;
    object-fit: contain;
    opacity: 0.88;
    filter: drop-shadow(0 0 6px rgba(0,128,255,0.3));
    pointer-events: none;   /* decorativa · el blanco de clic sigue siendo la lengueta */
    z-index: 20;
}
.left-panel.collapsed,
.right-panel.collapsed { padding-left: 0; padding-right: 0; overflow: visible; }

/* RIEL · bloque de iconos CENTRADO verticalmente (los dos lados a la misma altura porque ambos
   rieles miden el alto completo y su grupo va con margin auto). SALIR abajo del todo: es salida,
   no navegacion, y por eso se descuelga del grupo. */
.panel-rail { display: none; }
/* AJUSTE CEO · el carril ocupa la FRANJA COMPLETA (riel + lengueta) para que el centrado
   horizontal sea real: antes los iconos se centraban en los 40px del riel y quedaban a la
   izquierda del ancho visible de 54. Un icono de 26px centrado en 54 va de 14 a 40; la lengueta
   empieza en 40 -> centrado exacto y sin solape. */
.left-panel.collapsed .panel-rail,
.right-panel.collapsed .panel-rail {
    display: flex; flex-direction: column; align-items: center;
    width: calc(var(--rail-w) + var(--rail-handle)); height: 100%; position: relative;
}
/* el riel deja libre el canal de la lengueta: franja visible = riel + lengueta, sin solape */
.left-panel.collapsed  .panel-rail { margin-left: auto; }
.right-panel.collapsed .panel-rail { margin-right: auto; }
/* AJUSTE CEO · 'los dos lados a la misma altura'. #panel-R arranca 64px mas abajo que #panel-L
   por la regla F16.10 (margin-top:var(--header-h), para quedar bajo el header). Sin compensar,
   dos bloques centrados en marcos desplazados quedan a 32px de diferencia. El riel izquierdo
   adopta el mismo marco, asi ambos grupos comparten eje. Se aplica siempre — tambien donde no
   hay panel derecho — para que los iconos no salten al cambiar de pestania. */
/* ALTURA · el riel DERECHO es la referencia. Su panel ya arranca 64px abajo (F16.10) y el riel
   suma otros 64 -> primer icono en y=128. El panel izquierdo arranca en 0, asi que su riel
   necesita las DOS cabeceras para caer a la misma altura. Medido antes: izq 64 · dcha 128. */
.right-panel.collapsed .panel-rail { margin-top: var(--header-h); height: calc(100% - var(--header-h)); }
.left-panel.collapsed  .panel-rail { margin-top: calc(var(--header-h) * 2); height: calc(100% - var(--header-h) * 2); }
/* AJUSTE CEO · bloque ARRIBA, no centrado: arranca donde arranca el contenido del panel
   (marco --header-h, el mismo que usa #panel-R por F16.10). SALIR sigue descolgado abajo. */
.rail-group { display: flex; flex-direction: column; align-items: center; gap: 10px; margin: 0; }
.rail-btn[data-rail="logout"] { position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%); }

.rail-btn {
    position: relative;
    width: 26px; height: 26px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    background: transparent; border: 1px solid rgba(77,166,255,0.25); border-radius: 4px;
    color: #4DA6FF; cursor: pointer;
    transition: border-color 0.15s, background 0.15s;   /* patron .nx-btn */
}
.rail-btn:hover { background: rgba(0,128,255,0.16); border-color: rgba(77,166,255,0.7); }
/* ── PILOTO SYNARIS VIVO · el riel ────────────────────────────────────────────
 * Primera superficie que consume los tokens de movimiento. Tres cosas, todas
 * atadas a algo que el usuario acaba de hacer o que acaba de pasar de verdad:
 *   1. el marcado de seccion entra desacelerado en vez de saltar
 *   2. el icono se desplaza sus 8px al aparecer, no cuando le apetece
 *   3. no hay NADA que se mueva solo
 * Se anima color/fondo/borde/transform y NO width/height ni margin: lo primero lo
 * resuelve el compositor, lo segundo obliga a recalcular la maquetacion en cada
 * cuadro, que es exactamente de donde salen los tirones que el gate busca. */
.rail-btn {
    transition: border-color var(--mov-rapido) var(--mov-curva),
                background   var(--mov-rapido) var(--mov-curva),
                color        var(--mov-rapido) var(--mov-curva),
                transform    var(--mov-rapido) var(--mov-curva);
}
/* El icono que APARECE (contextual: retícula y chincheta en HUB, camara en IRIS)
 * entra deslizando desde el borde exterior de su riel. Se declara por riel para que
 * cada uno entre desde SU lado y no los dos desde el mismo. */
@keyframes railEntraDesdeIzq { from { opacity: 0; transform: translateX(calc(-1 * var(--mov-desliz))); } }
@keyframes railEntraDesdeDcha { from { opacity: 0; transform: translateX(var(--mov-desliz)); } }
#rail-l .rail-btn { animation: railEntraDesdeIzq var(--mov-normal) var(--mov-curva) both; }

/* ── EL PULSO · SOLO POR EVENTOS REALES ───────────────────────────────────────
 * No hay nada aqui que se mueva solo. La clase la pone el JS cuando LLEGA algo —un
 * mensaje de otro, alguien que entra a una sala, un sello de la cadena— y se quita
 * al terminar. Sin evento no hay clase, sin clase no hay animacion, y en reposo no
 * queda ni un temporizador corriendo.
 * TODO va escalado por --pulso-alcance: a 0 el fotograma existe pero no mueve nada,
 * asi que en modo apagado la regla es inerte sin que ninguna condicion pregunte por
 * el modo. Y se anima box-shadow y no width/height: el filo se enciende, la caja no
 * se mueve, y nada de alrededor tiene que recolocarse. */
/* GRANDE Y OPACO A LA VEZ. El fotograma anterior iba de (alfa alta, radio 0) a
   (alfa 0, radio 6px): la opacidad caia mientras el anillo crecia, asi que era mas
   brillante cuando era mas pequeno y nunca coincidian las dos cosas. Medido: pico de
   0,157 en sutil A LOS 50ms, con 2,2px de radio — funcionaba y no se veia.
   Ahora la opacidad se SOSTIENE durante todo el crecimiento (0 a 45%, radio 0 -> 5px)
   y solo se apaga en el tramo final. El anillo se ve entero antes de irse. */
@keyframes pulsoSenal {
    0%   { box-shadow: 0 0 0 0px rgba(77,166,255,var(--pulso-alfa)); }
    45%  { box-shadow: 0 0 0 5px rgba(77,166,255,var(--pulso-alfa)); }
    100% { box-shadow: 0 0 0 7px rgba(77,166,255,0); }
}
/* EL PULSO VIVE EN UN PSEUDO-ELEMENTO PROPIO, y no en el boton, por un motivo medido:
   los iconos del riel ya llevan su animacion de entrada (#rail-l .rail-btn, del piloto)
   y esa regla tiene MAS especificidad que una clase suelta. El primer intento puso el
   pulso en .pulso-senal y no corria nunca — la animacion computada seguia siendo la de
   entrada. Dos animaciones peleando por la misma propiedad del mismo elemento es una
   pelea que alguien pierde en silencio; en su propio ::after no hay con quien pelear.
   inset:-2px lo saca justo por fuera del borde, para que el anillo se lea como un halo
   alrededor del icono y no como un relleno. */
/* ── SONAR · doble pulso de fondo con eco ────────────────────────────────────────
 * Va sobre el ITEM del departamento, en el momento en que llega ahi un mensaje que no
 * estas mirando. Dos ondas: la segunda sale a mitad de la primera y llega mas lejos,
 * que es lo que hace que se lea como un eco y no como un parpadeo doble.
 * Se pinta con box-shadow sobre el propio item —no con un pseudo— porque el item ya usa
 * ::before/::after para el @ y el punto, y pelearse por ellos es como se rompio el
 * anillo la primera vez. box-shadow admite DOS sombras a la vez: una por onda. */
@keyframes sonarDept {
    0%   { box-shadow: 0 0 0 0 rgba(77,166,255,var(--pulso-alfa)), 0 0 0 0 rgba(77,166,255,0); }
    40%  { box-shadow: 0 0 0 6px rgba(77,166,255,0),               0 0 0 0 rgba(77,166,255,var(--pulso-alfa)); }
    100% { box-shadow: 0 0 0 6px rgba(77,166,255,0),               0 0 0 12px rgba(77,166,255,0); }
}
.hub-dept-item.sonar {
    animation: sonarDept calc(var(--pulso-dur) * 1.4) var(--mov-curva) 1;
}
/* ── IGNICION DE FILA · el feed de actividad ─────────────────────────────────────
 * La fila nueva entra encendida y se enfria. Sustituye al resalte tenue: aquel decia
 * "mira aqui" con la misma intensidad durante 900ms; este dice "esto acaba de pasar" y
 * se apaga solo, que es lo que hace una brasa.
 * Se anima background y color —no box-shadow— porque una fila de texto sin borde no
 * tiene donde poner un anillo: lo que se enciende es la propia linea. */
/* LUZ Y MOVIMIENTO DE LLEGADA, no solo destello. La fila entra deslizandose mientras se
   enciende y se enfria: el desplazamiento dice "acabo de llegar" y la luz dice "esto es
   lo nuevo". Un destello sin movimiento se confunde con un repintado.
   El deslizamiento termina ANTES que la luz (a 35%, cuando el color aun esta alto): la
   fila se coloca y luego se enfria en su sitio, que es como llega algo — no se enfria
   mientras todavia se esta moviendo.
   6px y no los 8 de --mov-desliz: aquel es para bloques (los iconos del riel); una fila
   de texto de 16px de alto con 8 de recorrido se lee como un salto.
   En apagado no hay que hacer nada: --pulso-dur vale 1ms y la animacion entera no da
   tiempo a verse, deslizamiento incluido. */
@keyframes ignicionFila {
    0%   { background: rgba(77,166,255,calc(var(--pulso-alfa) * 0.9)); color: #e6ebf5;
           transform: translateX(6px); }
    35%  { background: rgba(77,166,255,calc(var(--pulso-alfa) * 0.7)); color: #e6ebf5;
           transform: translateX(0); }
    100% { background: rgba(77,166,255,0); transform: translateX(0); }
}
.ignicion {
    animation: ignicionFila calc(var(--pulso-dur) * 1.6) var(--mov-curva) 1;
    border-radius: 2px;
}
.pulso-senal::after {
    content: "";
    position: absolute;
    inset: -2px;
    border-radius: 6px;
    pointer-events: none;
    animation: pulsoSenal var(--pulso-dur) var(--mov-curva) 1;
}
/* La nav YA NO lleva anillo (decision CEO tras la galeria): en una pestana el aviso es
   el CONTADOR, que ademas dice CUANTOS y se queda hasta que entras. Un anillo que pasa
   y un numero que espera compiten por lo mismo, y el que informa es el numero.
   El fotograma se conserva porque lo usa el riel, que no tiene sitio para un contador. */
#rail-r .rail-btn { animation: railEntraDesdeDcha var(--mov-normal) var(--mov-curva) both; }
/* SECCION ACTIVA · mismo tratamiento que la nav principal (.mode-btn.active, arriba): el mismo
   azul, el mismo halo y el mismo fondo al 6%. No se estrena un lenguaje de "seleccionado" para
   el riel: si la nav ya dice "estas aqui" de una manera, el riel lo dice igual. Lo unico que
   cambia es que aqui el marco es un borde y no un subrayado, porque el boton es cuadrado. */
.rail-btn.active {
    color: var(--mia-blue);
    border-color: var(--mia-blue);
    background: rgba(0,128,255,0.06);
    filter: drop-shadow(0 0 6px rgba(0,128,255,0.45));
}
.rail-btn[data-rail="logout"] { color: #FF4466; border-color: rgba(255,68,102,0.25); }
.rail-btn[data-rail="logout"]:hover { background: rgba(255,68,102,0.10); border-color: rgba(255,68,102,0.7); }
.rail-dot {
    position: absolute; top: -3px; right: -3px;
    width: 7px; height: 7px; border-radius: 50%;
    background: #5a6378; border: 1px solid #0a0e14;
}
.rail-badge {
    position: absolute; top: -6px; right: -6px;
    min-width: 13px; height: 13px; padding: 0 3px;
    display: none; align-items: center; justify-content: center;
    background: #FF4466; color: #0a0e14; border-radius: 7px;
    font-family: 'Share Tech Mono', monospace; font-size: 0.4rem; line-height: 1;
}
.rail-badge.on { display: flex; }

@media (max-width: 768px) {
    .panel-chevron, .panel-rail { display: none !important; }
}
}

/* ─────────────────────────────────────────────────────────────────────
   COSMETIC-BTN-1 2026-08-03 · PATRON UNICO DE INTERACCION
   Origen medido: el par LLAMAR (#hub-call-header-btn) / + PROGRAMAR
   (#btn-cal-new-meeting). Comparten reposo (fondo rgba(0,128,255,0.08),
   borde rgba(77,166,255,0.4)) y ambos declaran
   `transition: border-color .15s, background .15s`.
   HALLAZGO: solo PROGRAMAR implementa el hover (via handlers JS
   mouseenter/mouseleave, soul.js:20128). LLAMAR declara la transicion y
   NO la dispara nadie — ni CSS ni JS. Es decir: el patron de plataforma
   estaba a medio escribir en el propio boton que lo define.
   Aqui se escribe entero y UNA vez, en CSS, para que el proximo boton
   nazca con el sin depender de que alguien recuerde cablear dos listeners.
   Valores: NINGUNO nuevo · rgba(0,128,255,0.16) es el hover de PROGRAMAR ·
   rgba(255,68,102,0.10) es el relleno rojo que ya usa el toggle CANCELADAS.
   ───────────────────────────────────────────────────────────────────── */
#hub-call-header-btn,
.nx-btn {
    transition: border-color 0.15s, background 0.15s;
}
#hub-call-header-btn:hover,
.nx-btn:hover {
    background: rgba(0,128,255,0.16);
    border-color: rgba(77,166,255,0.7);
}
.nx-btn-danger:hover {
    background: rgba(255,68,102,0.10);
    border-color: rgba(255,68,102,0.7);
}

/* COSMETIC-5 2026-08-03 · FEEDBACK DE PULSACION EN ESCRITORIO · el :active que existia vivia
   DENTRO de @media (max-width:768px), asi que en escritorio no habia ninguno: pulsabas GENERAR,
   ACEPTAR o REVOCAR y el boton no acusaba el golpe. Patron global (no por boton, para que el
   proximo nazca ya con el): 1px hacia abajo + borde encendido. Sin cambiar color de fondo ni
   texto, para no pisar el estado de cada boton (verde/rojo/azul se conservan).
   currentColor en el borde = cada boton se enciende en SU color, no en uno impuesto. */
@media (min-width: 769px) {
    button:active,
    [role="button"]:active,
    .mode-btn:active,
    .vault-chip:active,
    .vault-view-btn:active,
    .mpx-folder:active,
    .iris-ctrl-btn:active {
        transform: translateY(1px);
        border-color: currentColor;
        transition: transform 60ms ease-out, border-color 60ms ease-out;
    }
}

/* P1-12 · Tap feedback for interactive elements (mobile pattern) */
@media (max-width: 768px) {
    button:active,
    [role="button"]:active,
    a:active,
    .mode-btn:active,
    .vault-chip:active,
    .vault-view-btn:active,
    .mpx-folder:active,
    .iris-ctrl-btn:active,
    .iris-ticker-mode-btn:active {
        transform: scale(0.97);
        opacity: 0.85;
        transition: transform 80ms ease-out, opacity 80ms ease-out;
    }
}

/* P1-15 · DEPT chips horizontal mobile · hide tree-pane stack */
@media (max-width: 768px) {
    #vault-tree-pane { display: none !important; }
    #vault-dept-mobile-chips { display: flex !important; }
}
#vault-dept-mobile-chips::-webkit-scrollbar { display: none; }

/* P0-mobile-F1 · hide #vault-preview-pane mobile · 320px stack-vertical robaba
   ~50vh con empty state. F3 · cuando file selected, pane se vuelve fullscreen
   overlay via class .mobile-overlay-active (toggled by _vaultSelectFile). */
@media (max-width: 768px) {
    #vault-preview-pane { display: none !important; }
    #vault-preview-pane.mobile-overlay-active {
        display: flex !important;
        position: fixed !important;
        inset: 0 !important;
        z-index: 9999 !important;
        background: rgba(8, 12, 20, 0.98) !important;
        backdrop-filter: blur(8px) !important;
        -webkit-backdrop-filter: blur(8px) !important;
        border-left: none !important;
        flex-direction: column !important;
    }
    #vault-preview-pane.mobile-overlay-active #vault-preview-mobile-header {
        display: flex !important;
    }
    /* Inside overlay · thumb expands more · meta scrolls · actions sticky bottom */
    #vault-preview-pane.mobile-overlay-active #vault-preview-thumb {
        height: 240px !important;
    }
    #vault-preview-pane.mobile-overlay-active #vault-preview-actions {
        padding: 14px !important;
        padding-bottom: max(14px, env(safe-area-inset-bottom)) !important;
    }
}

/* P0-mobile-F5 · status-bar centered balanced mobile · supersedes F2 stack-left.
   "8 FILES · 1.5 MB ENCRYPTED" centered row 1 · 5 chips justify-content center
   wrap auto a 3+2 (or 2+2+1 narrow). Visualmente balanced · NO apilado feo.
   F2v2 · margin-top/bottom 12 + padding 14 balanceado · status-bar respira
   entre file-list end y bottom-nav (wall padding-bottom 72 da clearance). */
@media (max-width: 768px) {
    #vault-status-bar {
        flex-direction: column !important;
        align-items: center !important;
        gap: 8px !important;
        padding: 14px !important;
        margin-top: 12px !important;
        margin-bottom: 12px !important;
    }
    #vault-status-bar > div:first-child {
        text-align: center;
    }
    #vault-status-bar > div:last-child {
        flex-wrap: wrap !important;
        justify-content: center !important;
        gap: 6px 12px !important;
    }
}
/* ITEM-VAULT-UI 2026-06-15 · compresión progresiva del footer técnico en estrecho · los 5 badges
   (AES-256-GCM · Ed25519 · SHA-256 · AV-SCANNED · AUDIT) refluyen con flex-wrap y encogen font/gap ·
   NINGUNO se elimina (regla CEO · señales de seguridad). Reemplaza el grid 3-col rígido (borrado @3180). */
@media (max-width: 480px) {
    #vault-status-bar > div:last-child { gap: 4px 8px !important; }
    #vault-status-bar > div:last-child > span { font-size: 0.45rem !important; }
}
@media (max-width: 320px) {
    #vault-status-bar { padding: 10px 8px !important; }
    #vault-status-bar > div:last-child > span { font-size: 0.42rem !important; }
}

/* P1-16 · Modal full-screen mobile pattern · ≥440px max-width existente
   redundante en mobile · expandir a 100vw 100dvh para mejor UX. Honor
   safe-area-inset (P1-14 ya cubre padding).
   ITEM-C 2026-06-15 · #iris-create-modal / #iris-join-modal EXCLUIDOS de este
   patrón: a diferencia de #mpx-compose-modal (un único wrapper = flujo full-screen
   legítimo), los modales IRIS son diálogos de elección con VARIOS hijos directos
   (título · nombre-sala · contenedor de botones). El `> div { height:100dvh }`
   estiraba cada hijo a pantalla completa → botones VIDEO+AUDIO/SOLO AUDIO/CANCELAR
   como columnas gigantes. Sin estas reglas vuelven al overlay centrado (inline
   align-items:center) con contenido a su altura natural. Desktop ≥769 no afectado. */
@media (max-width: 768px) {
    #mpx-compose-modal {
        align-items: stretch !important;
        justify-content: stretch !important;
    }
    #mpx-compose-modal > div {
        width: 100vw !important;
        max-width: 100vw !important;
        height: 100dvh !important;
        max-height: 100dvh !important;
        border-radius: 0 !important;
    }
    /* Sovereign confirm + ledger NO van full-screen (son confirms, no flows) */
    /* ITEM-C 2026-06-15 · contenedor de botones de los modales IRIS de elección
       (:last-child = la fila de botones VIDEO+AUDIO/SOLO AUDIO/CANCELAR) · permite
       envolver en pantallas muy angostas (320-375) en vez de desbordar horizontal ·
       altura natural por padding · centrado. NO toca desktop ≥769 ni la lógica de join. */
    #iris-create-modal > div:last-child,
    #iris-join-modal > div:last-child {
        flex-wrap: wrap !important;
        justify-content: center !important;
    }
}

/* P1-14 · Safe-area-inset extension · scrollable surfaces + modals */
@media (max-width: 768px) {
    /* HUB chat scroll · respeta home-indicator + bottom-nav stack */
    #hub-chat-wall {
        padding-bottom: max(72px, calc(72px + env(safe-area-inset-bottom))) !important;
    }
    /* VAULT file list scroll · idem */
    #vault-file-list {
        padding-bottom: max(16px, calc(16px + env(safe-area-inset-bottom))) !important;
    }
    /* MIPOST list y viewer body · safe-area bottom para no tapar contenido con home indicator */
    #mpx-list,
    #mpx-viewer-body {
        padding-bottom: max(8px, env(safe-area-inset-bottom)) !important;
    }
    /* Modals fullscreen · safe-area horizontal + top */
    #mpx-compose-modal,
    #iris-create-modal,
    #iris-join-modal,
    #sovereign-confirm-modal,
    #ledger-modal {
        padding: max(8px, env(safe-area-inset-top)) max(8px, env(safe-area-inset-right)) max(8px, env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-left)) !important;
    }
}

body {
    font-family: 'Inter', system-ui, sans-serif;
    background: var(--bg);
    color: var(--text-main);
    height: 100vh;
    overflow: hidden;
    display: flex;
    /* Atmósfera espacial MARSHA — nebulosa de fondo */
    /* M1 · haz de luz reposicionado al recorrido del ojo (entra arriba-izq → cierra abajo-der) ·
       mismo color/opacidad robustos rgb(45,125,235) · g3 reubicado mas tenue · centro limpio. */
    background-image:
        radial-gradient(ellipse 70% 50% at 15% -5%,  rgba(45,125,235,0.05) 0%, transparent 60%),
        radial-gradient(ellipse 55% 40% at 90% 108%, rgba(45,125,235,0.035) 0%, transparent 60%),
        radial-gradient(ellipse 45% 35% at 70% 25%,  rgba(45,125,235,0.018) 0%, transparent 55%);
}

:focus-visible {
    outline: 1px solid var(--mia-blue);
    outline-offset: 2px;
    border-radius: var(--radius);
}

/* ── BIOMETRIC SHIELD ────────────────────────────────────────────── */
#auth-shield {
    position: fixed; inset: 0;
    background: #010103;
    z-index: 999999;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    transition: 1.5s cubic-bezier(0.7,0,0.3,1);
}
#auth-shield.unlocked {
    opacity: 0; pointer-events: none;
}
.wolf-container {
    position: relative; width: 300px; height: 300px;
    display: flex; align-items: center; justify-content: center;
    cursor: pointer;
    transition: transform 0.5s cubic-bezier(0.23,1,0.32,1);
}
.cam-circle {
    position: absolute; width: 200px; height: 200px;
    border-radius: 50%; overflow: hidden;
    opacity: 0.1; filter: grayscale(1); z-index: -1;
}
#auth-video { width: 100%; height: 100%; object-fit: cover; transform: scaleX(-1); }
.auth-status {
    opacity: 0; pointer-events: none;
    font-family: 'Orbitron'; font-size: 0.55rem;
    letter-spacing: 8px; color: var(--mia-blue);
    text-transform: uppercase; transition: 0.3s;
    margin-top: 24px;
}
.wolf-svg {
    pointer-events: none; width: 100%; height: 100%;
    filter: drop-shadow(0 0 12px rgba(79,172,254,0.35));
    transition: all 0.5s ease;
}
.wolf-path {
    stroke: var(--mia-blue); stroke-width: 0.9;
    fill: rgba(79,172,254,0.015);
    stroke-linejoin: round;
    stroke-dasharray: 2500; stroke-dashoffset: 2500;
    animation: draw 3s cubic-bezier(0.4,0,0.2,1) forwards;
}
@keyframes draw { to { stroke-dashoffset: 0; } }
.wolf-eyes {
    fill: var(--mia-blue); stroke: none; opacity: 0.85;
    filter: drop-shadow(0 0 3px var(--mia-blue));
    animation: eyePulse 1.6s infinite alternate ease-in-out;
}
@keyframes eyePulse {
    0%   { opacity: 0.55; filter: drop-shadow(0 0 2px var(--mia-blue)); }
    100% { opacity: 1;    filter: drop-shadow(0 0 5px var(--mia-blue)); }
}
.scan-node {
    will-change: transform;
    fill: #fff; filter: blur(1px) drop-shadow(0 0 8px #fff);
    offset-path: path("M100,15 L130,30 L170,20 L140,60 L160,70 L180,90 L165,130 L145,150 L125,165 L115,195 L100,200 L85,195 L75,165 L55,150 L35,130 L20,90 L40,70 L60,60 L30,20 L70,30 Z");
    animation: nodeTravel 3s infinite linear; opacity: 0;
}
.scanning .scan-node { opacity: 1; }
@keyframes nodeTravel { 0% { offset-distance: 0%; } 100% { offset-distance: 100%; } }
.authorized .wolf-svg  { filter: drop-shadow(0 0 32px var(--green)); }
.authorized .wolf-path { stroke: var(--green) !important; fill: rgba(74,222,128,0.08) !important; }
.authorized .wolf-eyes { fill: var(--green) !important; filter: drop-shadow(0 0 16px var(--green)); }

/* ── ROOT LAYOUT ─────────────────────────────────────────────────── */
#root-layout {
    display: flex !important;
    width: 100vw; height: 100vh;
    opacity: 0; transition: opacity 1.2s ease;
    position: relative; z-index: 1;
}
#root-layout.visible { opacity: 1; }

#p2p-canvas {
    position: absolute; inset: 0;
    pointer-events: none; opacity: 0; transition: 1s; z-index: 0;
}
body.p2p-active #p2p-canvas,
body.vault-active #p2p-canvas { opacity: 0.3; }

/* ── LEFT PANEL ──────────────────────────────────────────────────── */
.left-panel {
    padding: 0 16px 20px;
    width: var(--panel-w);
    border-right: none;
    display: flex; flex-direction: column;
    background: var(--bg-panel);
    z-index: 10; flex-shrink: 0;
    /* FEAT-PANELS · FIX APRENDIDO · estaba en static, asi que el chevron (absolute) se anclaba a
       #root-layout y su right:9px lo mandaba al borde derecho de la PANTALLA. .right-panel ya era
       relative; esto iguala los dos y de paso activa el z-index:10 que en static era inerte. */
    position: relative;
    /* BUG-TOGGLE 2026-08-03 · era `transition: margin`. Animar el ATAJO hacia un calc() con var()
       deja el margen SIN llegar al destino: la clase .collapsed entra, el panel no se mueve, y el
       clic parece no responder. Medido: con transition:none aplica -186px al instante; con el
       atajo se queda en 0px indefinidamente. Se anima la LONGHAND concreta. */
    transition: margin-left var(--transition);
}
.left-panel.collapsed { margin-left: calc(-1 * var(--panel-w) + var(--rail-w) + var(--rail-handle)); }

.collapse-btn {
    position: absolute; top: 50%; transform: translateY(-50%);
    background: var(--bg-panel);
    border: 1px solid var(--border);
    color: var(--text-muted); cursor: pointer;
    width: 20px; height: 44px;
    display: flex; align-items: center; justify-content: center;
    font-size: 0.6rem; z-index: 100;
    transition: color var(--transition), background var(--transition);
}
.collapse-btn:hover { color: var(--mia-blue); background: rgba(79,172,254,0.06); }
.collapse-btn-left  { right: -20px; border-radius: 0 3px 3px 0; border-left: none; }
.collapse-btn-right { left:  -20px; border-radius: 3px 0 0 3px; border-right: none; }

.logo-area {
    height: var(--header-h);
    display: flex; align-items: center;
    padding: 0 20px;
    border-bottom: 1px solid rgba(255,255,255,0.08);
    flex-shrink: 0;
}
/* Identidad sidebar — micro wordmark */
.sidebar-brand {
    display: flex; align-items: center; gap: 12px;
}
.sidebar-brand-img {
    height: 40px; width: 40px; object-fit: contain;
    opacity: 0.88; flex-shrink: 0;
    filter: drop-shadow(0 0 6px rgba(0,128,255,0.3));
}
.sidebar-brand-name {
    font-family: 'Orbitron', monospace;
    font-size: 1.1rem; letter-spacing: 4px;
    font-weight: 700;
    display: inline-block;
    background: linear-gradient(135deg, #0080FF 0%, #0055CC 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    color: transparent;
}
.logo-img { height: 52px; width: auto; }

.hist-container { flex: 1; padding: 20px; overflow-y: auto; }

.sidebar-title {
    font-family: 'Orbitron'; font-size: 0.52rem;
    letter-spacing: 3px; color: rgba(255,255,255,0.18);
    margin-bottom: 10px; font-weight: 600;
    text-transform: uppercase;
    display: flex; justify-content: space-between; align-items: center;
    padding-bottom: 6px;
    border-bottom: 1px solid rgba(255,255,255,0.04);
}
.hist-item {
    font-size: 0.7rem; padding: 8px 0;
    border-bottom: 1px solid rgba(255,255,255,0.03);
    color: var(--text-secondary); cursor: pointer;
    transition: color 0.2s, padding-left 0.2s;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hist-item:hover { color: var(--mia-blue); padding-left: 4px; }

/* ── HEADER ──────────────────────────────────────────────────────── */
.main-view {
    flex: 1; display: flex; flex-direction: column;
    min-width: 0; z-index: 20; position: relative;
    /* BUG-D · raise main-view stacking context above .left-panel and .right-panel
     * (both z-index 10) so #mpx-compose-modal (z-index 99998 nested inside main-view)
     * paints above the activity sidebars when display:flex · resolves modal-cropped-
     * under-sidebar at narrow widths. Kept under .collapse-btn (z-index 100) so the
     * panel toggle stays visible. */
}
header {
    height: var(--header-h); flex-shrink: 0;
    padding: 0 32px;
    display: flex; justify-content: space-between; align-items: center;
    border-bottom: 1px solid rgba(255,255,255,0.08);
    background: var(--bg-panel);
    position: relative; z-index: 5;
    box-shadow: 0 4px 32px rgba(0,0,0,0.4);
}

/* Scanline sutil en header — efecto terminal espacial */
header::after {
    content: '';
    position: absolute; bottom: 0; left: 0; right: 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--mia-blue), transparent);
    opacity: 0.4;
    animation: scanHeader 4s ease-in-out infinite;
}
@keyframes scanHeader {
    0%, 100% { opacity: 0.2; transform: scaleX(0.3); }
    50%       { opacity: 0.6; transform: scaleX(1); }
}
/* .mia-title → reemplazada por .sidebar-brand-name */
.header-controls { display: flex; gap: 20px; align-items: center; margin-left: auto; }

.lang-switch {
    font-family: 'Orbitron'; font-size: 0.45rem; color: var(--text-muted);
    cursor: pointer; display: flex; gap: 6px; align-items: center;
    border: 1px solid var(--border); padding: 4px 10px;
    border-radius: var(--radius); transition: border-color 0.2s;
}
.lang-switch:hover { border-color: var(--border-active); }
.lang-switch span.active { color: var(--mia-blue); }

.sys-switch { display: flex; align-items: center; gap: 10px; cursor: pointer; }
#sys-txt {
    font-family: 'Orbitron'; font-size: 0.5rem;
    letter-spacing: 1px; font-weight: 600;
    color: var(--green); transition: color 0.3s;
}
.sw-track {
    width: 36px; height: 18px;
    background: rgba(255,255,255,0.04);
    border-radius: 9px; position: relative;
    border: 1px solid var(--border);
}
.sw-thumb {
    width: 12px; height: 12px; background: white;
    border-radius: 50%; position: absolute;
    top: 2px; left: 20px; transition: left 0.25s;
}


/* ── MI POST — selector destinatario solo mobile ─────── */
/* removed F16 */

/* ── MODULE TABS ─────────────────────────────────────────────────── */
.selector-area {
    display: flex; gap: 0;
    border-bottom: 1px solid rgba(255,255,255,0.08);
    padding: 0 32px;
    background: var(--bg-panel);
    flex-shrink: 0; z-index: 4;
}
.mode-btn {
    font-family: 'Orbitron'; font-size: var(--fs-tab); letter-spacing: 2px;
    color: var(--text-muted); font-weight: 600; cursor: pointer;
    padding: 14px 20px;
    border-bottom: 2px solid transparent;
    transition: color 0.2s, border-color 0.2s, background 0.2s;
    white-space: nowrap; position: relative;
    outline: none; border-radius: 0;
    -webkit-tap-highlight-color: transparent;
}
.mode-btn:focus { outline: none; }
.mode-btn:focus-visible { outline: none; }
.mode-btn:hover { color: var(--text-main); }
/* (d) · badge GLOBAL de cabecera · paleta SYNARIS · esquina sup-der · data-kind de _moduleBadge:
   dot=punto azul (mensajes/actividad) · mention=número ámbar (menciones) · count=número azul (MI POST/IRIS) */
.mode-btn .tab-badge {
    display: none; align-items: center; justify-content: center;
    position: absolute; top: 5px; right: 6px;
    min-width: 15px; height: 15px; padding: 0 4px; border-radius: 8px;
    font-family: 'Share Tech Mono', monospace; font-size: 0.5rem; font-weight: 700; letter-spacing: 0;
    line-height: 1; box-shadow: 0 0 0 1px #080c10; pointer-events: none;
}
.mode-btn .tab-badge[data-kind="dot"] {
    width: 6px; height: 6px; min-width: 0; padding: 0; border-radius: 50%;
    background: #4DA6FF; color: transparent;
}
.mode-btn .tab-badge[data-kind="mention"] { background: #FFB400; color: #08121e; }
.mode-btn .tab-badge[data-kind="count"]   { background: #4DA6FF; color: #fff; }
.mode-btn.active {
    color: var(--mia-blue);
    border-bottom-color: var(--mia-blue);
    text-shadow: 0 0 12px rgba(0,128,255,0.6);
    background: rgba(0,128,255,0.06);
}

/* ── WORK ZONES ──────────────────────────────────────────────────── */
#chat-wall, #teams-wall {
    flex: 1; overflow-y: auto;
    padding: 16px 4% 16px;
    scroll-behavior: smooth;
}
/* MOBILE: sin padding lateral desktop */
@media (max-width: 768px) {
    #chat-wall, #teams-wall {
        padding: 0 !important;
    }
}
/* ── MIA CHAT · BURBUJAS (rediseño · SOLO MIA · HUB usa .hub-bubble, intacto) ── */
.msg { display: flex; gap: 10px; align-items: flex-start; margin-top: 4px; }
.msg.row-user { flex-direction: row-reverse; }
.msg.run-start { margin-top: 18px; }                 /* ritmo: ~18px entre turnos */
#chat-wall .msg:first-child { margin-top: 0; }
.msg.cont.row-mia  { padding-left: 0; }               /* MIA sin avatar → sin indent (alinea con el run-start) */
.msg.cont.row-user { padding-right: 42px; }          /* user avatar sigue 32 + gap 10 */
.msg-col { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.msg.row-user .msg-col { align-items: flex-end; }
.msg.row-mia  .msg-col { align-items: flex-start; }   /* MIA fit-content (tope 76% por max-width) · no estira */
.msg-label {
    font-family: 'Orbitron'; font-size: 0.62rem;
    color: var(--text-muted); margin-bottom: 5px;
    letter-spacing: 1.5px; text-transform: uppercase;
}
.bubble-mia, .bubble-user { max-width: 76%; padding: 10px 13px; line-height: 1.55; font-size: 0.8rem; word-wrap: break-word; }
/* MIA · AZUL PROFUNDO (navy/acero · familia del sistema · se diferencia del usuario por PROFUNDIDAD, no por color ·
   cero teal/verde) · esquinas asimétricas iguales. (Tono exacto a elegir por el CEO en el preview.) */
.bubble-mia  { background: rgba(40,75,130,0.16); border: 0.5px solid rgba(77,166,255,0.20);
    border-radius: 4px 13px 13px 13px; color: #d4e4f7; }
.bubble-user { background: rgba(0,128,255,0.13); border: 0.5px solid rgba(0,128,255,0.32);
    border-radius: 13px 4px 13px 13px; color: #e8f4ff; }
.msg-content { white-space: pre-wrap; }
/* SHIMMER · línea azul que recorre el borde superior mientras MIA habla (se quita al asentar el mensaje) */
.bubble-shimmer { position: relative; overflow: hidden; }
.bubble-shimmer::before { content: ''; position: absolute; top: 0; left: -35%; width: 55%; height: 2px;
    background: linear-gradient(90deg, transparent, #0080FF 50%, transparent);
    animation: mia-shimmer 1.6s linear infinite; pointer-events: none; }
@keyframes mia-shimmer { from { left: -35%; } to { left: 100%; } }
/* typing MIA · los 3 puntos dentro de la burbuja MIA */
.mia-typing-mia .mia-typing-dots { display: inline-flex; gap: 4px; }
/* ECUALIZADOR · 5 barras (indicador "procesando" MIA · reemplaza los 3 puntos · dentro de la burbuja) */
.mia-eq { display: inline-flex; align-items: flex-end; gap: 3px; height: 16px; }
.mia-eq i { width: 3px; height: 30%; border-radius: 2px;
    background: linear-gradient(to top, #0080FF, #2dd4bf); animation: mia-eqbar 0.9s ease-in-out infinite; }
.mia-eq i:nth-child(1) { animation-delay: 0s; }
.mia-eq i:nth-child(2) { animation-delay: 0.12s; }
.mia-eq i:nth-child(3) { animation-delay: 0.24s; }
.mia-eq i:nth-child(4) { animation-delay: 0.36s; }
.mia-eq i:nth-child(5) { animation-delay: 0.48s; }
@keyframes mia-eqbar { 0%, 100% { height: 28%; opacity: 0.7; } 50% { height: 100%; opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .bubble-shimmer::before { animation: none; opacity: 0.45; }
    .mia-typing-dots i { animation: none !important; }
    .mia-eq i { animation: none !important; height: 55%; }
}

/* ── MIA ORB · OVERLAY flotante · fuera del flujo (NO empuja layout) · solo vista MIA ── */
.input-zone { position: relative; }    /* ancla para el orbe flotante (no afecta a HUB: stage display:none) */
#mia-orb-stage { display: none; }
/* flota JUSTO encima del disclaimer (bottom:100% = sobre el borde superior de .input-zone) · centrado ·
   pointer-events:none → nunca tapa/estorba clicks al input ni al chat */
body.ia-active #mia-orb-stage {
    display: flex; align-items: center; justify-content: center;
    position: absolute; left: 50%; bottom: 100%; transform: translateX(-50%);
    margin-bottom: 4px; z-index: 5; pointer-events: none;
    opacity: 1; transition: opacity 1400ms ease;     /* FADE-IN lento (reaparición · 1400ms) por defecto */
}
/* PRESENCIA · fade-out lento (clase .orb-faded la gestiona _miaPresence en soul.js) */
body.ia-active #mia-orb-stage.orb-faded { opacity: 0; transition: opacity 1500ms ease; }
#mia-orb-canvas { display: block; flex-shrink: 0;
    /* FIX Safari/iOS · el composite del canvas alpha diverge por motor (getContextAttributes IDENTICO ·
       ningun flag/shader/blend lo arregla, probado). Solucion POST-composite via mask CSS (compositor,
       igual en todos los motores). FUNDIDO LARGO 12%->72% = efecto PORTAL sin borde: en vez de cortar un
       disco nitido (borde duro visible en Safari), el orbe se funde gradualmente al fondo -> sin borde
       perceptible en ningun navegador. -webkit- requerido por Safari. */
    -webkit-mask-image: radial-gradient(circle at center, #000 12%, transparent 72%);
            mask-image: radial-gradient(circle at center, #000 12%, transparent 72%);
}
/* SOLO Safari/WebKit · iguala el tamano aparente a Chrome: Safari pinta el halo exterior mas brillante
 * (compositing del motor) -> se ve ~10px mas grande. (-webkit-hyphens:none) = false en Chrome (verificado
 * en Chrome real M4) -> Chrome NO se entera, queda 110px intacto. Safari -> 100px (-10px). !important para
 * ganar al width:110px inline que pone applySize. La mascara (en %) escala sola con el canvas. */
@supports (-webkit-hyphens: none) {
  #mia-orb-canvas { width: 100px !important; height: 100px !important; }
}
/* BIENVENIDA · fade-in suave del canvas al entrar a MIA (clase .orb-enter la pone setMode · se auto-reinicia) */
@keyframes orb-enter-fade { from { opacity: 0; transform: scale(0.92); } to { opacity: 1; transform: scale(1); } }
#mia-orb-stage.orb-enter #mia-orb-canvas { animation: orb-enter-fade 0.4s ease-out both; }
/* ── fallback CSS plasma (solo si WebGL falla) · mismo tamaño fijo 88px ── */
.orb-fallback-css { display: none; }
#mia-orb-stage.orb-fallback #mia-orb-canvas { display: none; }
#mia-orb-stage.orb-fallback .orb-fallback-css {
    display: block; position: relative; width: 88px; height: 88px; border-radius: 50%;
    overflow: hidden; background: #06223f;
}
.orb-fallback-css span { position: absolute; inset: -30%; border-radius: 50%; mix-blend-mode: screen; filter: blur(14px); }
.orb-fallback-css .l1 { background: radial-gradient(circle at 35% 40%, #0080FF, transparent 55%); animation: orbfb 13s ease-in-out infinite alternate; }
.orb-fallback-css .l2 { background: radial-gradient(circle at 65% 60%, #2dd4bf, transparent 55%); animation: orbfb 17s ease-in-out infinite alternate-reverse; }
.orb-fallback-css .l3 { background: radial-gradient(circle at 50% 30%, #7cc0ff, transparent 50%); animation: orbfb 21s ease-in-out infinite alternate; }
.orb-fallback-css .l4 { background: radial-gradient(circle at 50% 75%, #0a3a6e, transparent 60%); animation: orbfb 23s ease-in-out infinite alternate-reverse; }
@keyframes orbfb { from { transform: translate(-6%,-4%) scale(1.0); } to { transform: translate(7%,5%) scale(1.15); } }
/* reduced-motion · orbe VISIBLE y estático · sin fade de entrada, sin fade de presencia, sin fallback anim */
@media (prefers-reduced-motion: reduce) {
    #mia-orb-stage.orb-enter #mia-orb-canvas { animation: none; }
    body.ia-active #mia-orb-stage,
    body.ia-active #mia-orb-stage.orb-faded { opacity: 1 !important; transition: none !important; }
    .orb-fallback-css span { animation: none !important; }
}

#vault-wall {
    display: none; flex: 1; flex-direction: column;
    padding: 12px 0 0 0;
}
#mail-wall {
    display: none; flex: 1; flex-direction: column;
    overflow-y: auto; padding: 0;
}
.dept-tabs {
    display: flex; gap: 8px; margin-bottom: 24px;
    justify-content: flex-start;
    border-bottom: 1px solid var(--border);
    padding-bottom: 12px; overflow-x: auto;
}
.dept-tab {
    font-family: 'Orbitron'; font-size: 0.45rem; letter-spacing: 1px;
    padding: 6px 14px; border: 1px solid var(--border);
    color: var(--text-muted); cursor: pointer; border-radius: var(--radius);
    transition: all 0.2s; white-space: nowrap;
}
.dept-tab:hover  { border-color: var(--border-active); color: var(--mia-blue); }
.dept-tab.active { border-color: var(--mia-blue); color: var(--mia-blue); background: rgba(79,172,254,0.06); }

.drop-zone {
    width: 60%; max-width: 500px; height: 260px;
    border: 1px dashed rgba(245,158,11,0.25);
    border-radius: var(--radius);
    display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 12px;
    transition: all 0.2s; background: rgba(255,215,0,0.01);
    cursor: pointer;
}
.drop-zone:hover, .drop-zone.drag-over {
    background: rgba(245,158,11,0.05);
    border-color: var(--amber);
}
.drop-zone.secure-state {
    border-color: var(--green);
    background: rgba(74,222,128,0.04);
    box-shadow: 0 0 16px rgba(74,222,128,0.08);
}
.vault-title {
    font-family: 'Orbitron'; font-size: 0.65rem;
    letter-spacing: 3px; color: var(--amber);
}

/* ── INPUT ZONE ──────────────────────────────────────────────────── */
.input-zone {
    padding: 16px 32px 20px;
    border-top: 1px solid rgba(255,255,255,0.08);
    background: var(--bg-panel);
    flex-shrink: 0;
}
body.vault-active .input-zone { display: none !important; }
body.mail-active  .input-zone { display: none !important; }

.bar-container {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    padding: 12px 20px; border-radius: var(--radius);
    display: flex; gap: 16px; align-items: center;
    transition: border-color 0.2s;
}
.bar-container:focus-within { border-color: var(--border-active); }

input[type="text"]:not(.auth-input):not(.guest-input), #user-input {
    flex: 1; background: transparent; border: none;
    color: var(--text-main); font-size: 0.875rem; outline: none;
    font-family: 'Inter', sans-serif;
    resize: none; line-height: 1.5;
    min-height: 24px; max-height: 120px;
    overflow-y: auto; width: 100%;
}
input[type="text"]::placeholder { color: var(--text-muted); }
/* COSMETIC · modal scheduler (NEW MEETING) · placeholders a mínimo legible WCAG AA
   (~5.2:1 sobre #0a0e14 · el default var(--text-muted) #3a3d4a daba ~1.9:1). Mismo orden/
   especificidad que la regla de arriba → gana por orden. Cubre input + textarea del modal. */
.cal-sched-modal input::placeholder,
.cal-sched-modal textarea::placeholder { color: #8892a4; }
/* COSMETIC-NEXO-13 2026-08-02 · placeholders del panel de emparejamiento · el texto de sugerencia
   pesaba lo mismo que el TECLEADO (0.8125rem) y competia con el encabezado de zona (Orbitron
   0.5rem). Toma #8892a4, el mismo token de placeholder que ya usa el modal del calendario
   (C.hint · AA ~5.2:1).
   El texto ESCRITO por el usuario NO se toca: sigue a 0.8125rem = 13px, sellado.
   COSMETIC-NEXO-17 2026-08-02 · RECALIBRADO 0.45rem -> 0.6rem: el 0.45 fue sobrecorreccion y
   dejaba la sugerencia ilegible (~7.2px). La jerarquia se sostiene igual — dato tecleado 13px >
   placeholder 9.6px — y el encabezado de zona sigue mandando por COLOR (#4DA6FF) y tracking
   (Orbitron 2px), no por tamano. */
#nexo-pair-admin input::placeholder,
#nexo-pair-admin textarea::placeholder {
    font-size: 0.6rem;
    letter-spacing: 1px;
    /* COSMETIC-NEXO-F1 2026-08-03 · el placeholder pasa a EJEMPLO apagado (#5a6378, 2.90:1):
       desde que cada campo tiene su label caps encima, la sugerencia ya no tiene que explicar
       nada — solo ilustrar el formato. Por debajo de AA a proposito: es texto efimero que
       desaparece al teclear, no contenido. El tamano 0.6rem NO se toca (sellado). */
    color: #5a6378;
}
/* COSMETIC punto 3 · Custom·minutes · ocultar spinner nativo (control propio · valida en JS) */
.cal-sched-modal input[type=number] { -moz-appearance: textfield; appearance: textfield; }
.cal-sched-modal input[type=number]::-webkit-outer-spin-button,
.cal-sched-modal input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.btn-exec {
    background: transparent;
    border: 1px solid rgba(0,128,255,0.3);
    color: rgba(77,166,255,0.7); padding: 8px 20px;
    font-family: 'Orbitron'; font-size: 0.45rem;
    letter-spacing: 2px;
    cursor: pointer; font-weight: 600;
    border-radius: var(--radius);
    transition: all 0.2s;
    white-space: nowrap; flex-shrink: 0;
}
.btn-exec:hover {
    background: rgba(0,128,255,0.06);
    border-color: rgba(0,128,255,0.6);
    color: #4DA6FF;
}

.e2e-bar { display: none; }
.nodes-flow {
    margin-top: 10px; display: flex;
    justify-content: center; align-items: center;
}
.node-dot {
    width: 4px; height: 4px;
    background: rgba(0,128,255,0.15);
    border-radius: 50%; animation: led-sequence 2.8s infinite;
}
.node-line {
    width: 32px; height: 1px;
    background: rgba(255,255,255,0.04);
    position: relative; overflow: hidden;
}
.node-line::after {
    content: ""; position: absolute; top: 0; left: -100%;
    width: 50%; height: 100%;
    background: linear-gradient(90deg, transparent, rgba(74,222,128,0.4), transparent);
    animation: data-spark 2.8s infinite linear;
}
.nodes-flow .node-dot:nth-child(1)       { animation-delay: 0s; }
.nodes-flow .node-line:nth-child(2)::after { animation-delay: 0.4s; }
.nodes-flow .node-dot:nth-child(3)       { animation-delay: 0.8s; }
.nodes-flow .node-line:nth-child(4)::after { animation-delay: 1.2s; }
.nodes-flow .node-dot:nth-child(5)       { animation-delay: 1.6s; }
.nodes-flow .node-line:nth-child(6)::after { animation-delay: 2.0s; }
.nodes-flow .node-dot:nth-child(7)       { animation-delay: 2.4s; }
@keyframes data-spark   { 0% { left: -100%; } 100% { left: 200%; } }
@keyframes led-sequence {
    0%, 80%   { background: rgba(0,128,255,0.15); box-shadow: none; }
    90%, 100% { background: var(--mia-blue); box-shadow: 0 0 8px var(--mia-blue); }
}

/* ── RIGHT PANEL ─────────────────────────────────────────────────── */
.right-panel {
    width: var(--panel-w); background: var(--bg-panel);
    border-left: none;
    /* CANAL IZQUIERDO A 22px · aire para la lengueta.
       La lengueta derecha se dibuja pegada al borde interior del panel (left:0), ocupando
       1200..1214 de un canal de 16px: dejaba 2px hasta donde arranca el texto. El panel
       izquierdo no tiene ese problema y no se toca — sus hijos llevan padding propio
       (.logo-area va a 20px) y ya dan 14px de aire.
       6px mas de canal dan 8px EXACTOS de aire, medidos. El ancho util baja de 208 a 202.
       POR QUE ESTO Y NO SACAR LA LENGUETA FUERA (que era la idea bonita): auditado antes de
       tocar, sacarla exige levantar DOS cosas a la vez —el overflow:hidden de este panel, que
       recorta lo que se dibuje fuera, y el z-index:20 de .main-view, que esta por encima de
       este panel a proposito (BUG-D, para que el modal de MI POST no quede bajo las
       sidebars)—. Y con las dos levantadas la lengueta pasa a pintar POR ENCIMA del
       #iris-share-stage: es fixed y z:950, pero vive DENTRO de main-view, asi que su 950 es
       local. Medido: elementsFromPoint devolvia btn-toggle-r sobre iris-share-video con el
       share activo. Una pestana decorativa tapando la pantalla compartida no es una opcion, y
       apagarla por JS en llamada/share/modales acoplaria un adorno a media aplicacion.
       LOS 202 SIGUEN ENTRANDO: la fila mas ancha del panel —UNIRSE + FORZAR CIERRE en
       espanol— pide 196px medidos, asi que quedan 6px de margen. */
    padding: 0 16px 20px 22px;
    transition: margin-right var(--transition);   /* BUG-TOGGLE · longhand, ver .left-panel */
    position: relative; display: flex; flex-direction: column;
    flex-shrink: 0; z-index: 10;
}
.right-panel.collapsed { margin-right: calc(-1 * var(--panel-w) + var(--rail-w) + var(--rail-handle)); }
/* ── LOS PANELES DE SYNARIS NO LLEVAN FILO ────────────────────────────────────
 * DECISION DE DISENO SELLADA (CEO, 2026-08-06). No es un pendiente ni un olvido:
 * es una regla, y se escribe aqui para que nadie la deshaga de buena fe.
 *
 * Se probo un hairline de 1px y se retiro. Lo que dijo la medicion:
 *   · en el diseno original NO habia ninguna linea vertical en el limite del panel
 *     — cero elementos con borde o sombra en x=240 —, asi que un filo no restauraba
 *     nada: lo estrenaba;
 *   · la banda superior (logo de 0 a 64 a la izquierda, header de 0 a 64 a la
 *     derecha, cerrados por la horizontal de .logo-area) se lee como UNA sola pieza,
 *     y una vertical de arriba abajo la partia en dos;
 *   · arrancarla por debajo de esa banda respetaba la composicion, pero seguia
 *     anadiendo una raya donde el diseno no queria ninguna.
 *
 * El limite entre panel y contenido se define POR CONTRASTE DE FONDOS. Eso es
 * suficiente y es lo que estaba. */

.progress-bg   {
    height: 2px; background: rgba(0,128,255,0.06);
    margin-bottom: 20px; border-radius: 1px;
}
.progress-fill {
    height: 100%; background: var(--marsha-gradient);
    transition: width 1s; border-radius: 1px;
    box-shadow: 0 0 8px rgba(0,128,255,0.3);
}

.key-box {
    margin-top: 20px; padding-top: 16px;
    border-top: 1px solid var(--border); display: none;
}
body.p2p-active .key-box { display: block; }

.key-display {
    font-family: monospace; font-size: 0.45rem; color: var(--mia-blue);
    background: rgba(0,0,0,0.3); padding: 10px; display: block;
    margin: 8px 0; border: 1px dashed rgba(79,172,254,0.2);
    word-break: break-all; border-radius: var(--radius);
}
.key-input-styled {
    width: 100%; background: var(--bg-surface);
    border: 1px solid var(--border);
    color: var(--text-main); font-family: 'Orbitron'; font-size: 0.45rem;
    padding: 10px; margin-bottom: 8px; outline: none;
    border-radius: var(--radius); transition: border-color 0.2s;
}
.key-input-styled:focus { border-color: var(--border-active); }

.key-btn {
    width: 100%; padding: 9px; font-family: 'Orbitron'; font-size: 0.42rem;
    letter-spacing: 1px;
    background: transparent; border: 1px solid var(--mia-blue);
    color: var(--mia-blue); cursor: pointer;
    transition: background 0.2s; border-radius: var(--radius);
    margin-bottom: 6px;
}
.key-btn:hover { background: rgba(79,172,254,0.08); }

.dep-link-header  { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; gap: 8px; }
.dep-link-title   { font-family: 'Orbitron'; font-size: 0.5rem; font-weight: 700; color: var(--mia-blue); letter-spacing: 1.5px; text-transform: uppercase; }
.dep-link-status  { color: var(--text-muted); font-size: 0.42rem; white-space: nowrap; }
.status-online    { color: var(--green) !important; font-weight: 700; }

.footer-right {
    margin-top: auto; padding-top: 16px; text-align: center;
    border-top: 1px solid var(--border);
}
.web-link  { font-family: 'Orbitron'; font-size: 0.58rem; color: #6b7280; text-decoration: none; letter-spacing: 1px; transition: color 0.2s; }
.web-link:hover { color: var(--mia-blue); }
.copy-txt  { font-family: 'Orbitron'; font-size: 0.44rem; color: rgba(255,255,255,0.15); margin-top: 6px; }

/* ── SYSTEM INDICATOR ────────────────────────────────────────────── */
#top1-indicator {
    position: fixed; bottom: 16px; right: 16px;
    font-family: 'Orbitron'; font-size: 0.5rem; letter-spacing: 1px;
    padding: 6px 14px; border-radius: var(--radius);
    border: 1px solid rgba(0,128,255,0.2);
    background: rgba(2,5,9,0.92);
    color: var(--green); z-index: 9999;
    pointer-events: none;
    backdrop-filter: blur(8px);                           /* techo E2 · era 12 */
    box-shadow: var(--glow-cyan), inset 0 0 20px rgba(0,128,255,0.03);
}
.status-critical { color: var(--red)   !important; border-color: rgba(248,113,113,0.3) !important; animation: pulse-red 1.5s infinite; }
.status-degraded { color: var(--amber) !important; border-color: rgba(245,158,11,0.3) !important; }
.status-top1     { color: var(--green) !important; border-color: rgba(74,222,128,0.3) !important; }
@keyframes pulse-red { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }

/* ── ANALYSIS ZONE ───────────────────────────────────────────────── */
#analysis-zone {
    display: none; position: fixed; inset: 0;
    flex-direction: column; align-items: center; justify-content: center;
    padding: 40px; z-index: 100000;
    background: rgba(5,5,8,0.96);
    backdrop-filter: blur(8px);                           /* techo E2 · era 12 */
}
#viewer-container {
    width: 80%; height: 78%; background: #000;
    border: 1px solid var(--border);
    border-radius: var(--radius); overflow: hidden;
    display: flex; align-items: center; justify-content: center;
}

/* ── VIDEO GRID (TEAMS) ──────────────────────────────────────────── */
#video-grid {
    display: none; flex-wrap: wrap;
    gap: 12px; padding: 20px;
    justify-content: center; align-items: center;
    flex: 1;
}
#video-grid video {
    width: 320px; border-radius: var(--radius);
    border: 1px solid var(--border-active);
    background: #000;
}

/* ── INACTIVE STATE ──────────────────────────────────────────────── */
body.inactive-sys { filter: grayscale(0.15) contrast(0.92); }
body.inactive-sys .btn-exec     { opacity: 0.3; cursor: not-allowed; pointer-events: none; }
body.inactive-sys .sw-thumb     { left: 3px !important; background: var(--red); }
body.inactive-sys #sys-txt      { color: var(--red); }
body.inactive-sys input         { pointer-events: none; opacity: 0.3; }
body.inactive-sys .node-dot,
body.inactive-sys .node-line::after { animation-play-state: paused !important; opacity: 0.1; }

/* ── HUB — INDICADOR DE MENSAJE NUEVO ──────────────────────── */
/* F30-FIX-2 · cleanup completo · ::after blue dot + position:relative + pulse-new keyframes ELIMINADOS
   · numeric badge .unread-count es SSOT · evita visual redundante (dot + badge coexistían) */
/* F30-FIX-4 · base estructural · styling visual (background/color/size) viene inline desde
   renderHubDepts con dept.color · permite color-per-dept dinámico (cyan TEC, naranja OPS, etc) */
.hub-dept-item .unread-count { display: none; }
.hub-dept-item.has-new .unread-count { display: inline-flex; }
/* (c) · contador sidebar · paleta SYNARIS: PUNTO azul=mensajes (actividad) · NÚMERO ámbar=menciones (te mencionaron) · rojo reservado a bloqueo */
.hub-dept-item .unread-count[data-kind="mention"] {
    display: inline-flex !important; align-items: center; justify-content: center;
    min-width: 15px; height: 15px; padding: 0 4px; border-radius: 8px;
    background: #FFB400 !important; color: #08121e; box-shadow: none !important;
    font-family: 'Share Tech Mono', monospace; font-size: 0.5rem; font-weight: 700; letter-spacing: 0;
    flex-shrink: 0; animation: none !important;
}
/* PELOTITA NUMERICA · el mismo cuerpo que la de la nav (.tab-badge[data-kind=count]):
   azul #4DA6FF, texto oscuro, Share Tech Mono 0,5rem, radio 8, alto 15. Se escribe aqui
   porque el badge del sidebar no puede heredar del de la pestana —viven en arboles
   distintos— pero los valores son los MISMOS, no unos parecidos. */
.hub-dept-item .unread-count[data-kind="count"] {
    display: inline-flex !important; align-items: center; justify-content: center;
    min-width: 15px; height: 15px; padding: 0 4px; border-radius: 8px;
    background: #4DA6FF !important; color: #08121e; box-shadow: none !important;
    font-family: 'Share Tech Mono', monospace; font-size: 0.5rem; font-weight: 700; letter-spacing: 0;
    flex-shrink: 0;
    animation: badgePop var(--mov-rapido) var(--mov-curva) 1;
}
/* POP DE APARICION · el contador entra creciendo desde el 70%. No escala con el nivel de
   EFECTOS a proposito: el contador es INFORMACION, no ambiente, y tiene que comportarse
   igual en sutil que en completo. En apagado dura 1ms como todo lo demas y no se ve. */
@keyframes badgePop {
    from { transform: scale(0.7); }
    to   { transform: scale(1); }
}
.mode-btn .tab-badge[data-kind="count"],
.mode-btn .tab-badge[data-kind="mention"] {
    animation: badgePop var(--mov-rapido) var(--mov-curva) 1;
}
.hub-dept-item .unread-count[data-kind="dot"] {
    /* H-D D2 · donut (anillo hueco) · !important en size para vencer el inline
       min-width/height:14px de renderHubDepts (L187: el dot REAL renderiza ~14px, no 6px). */
    display: inline-flex !important; box-sizing: border-box !important;
    width: 6px !important; height: 6px !important; min-width: 6px !important;
    padding: 0 !important; border-radius: 50% !important;
    background: transparent !important; border: 1.5px solid #4DA6FF !important;
    box-shadow: none !important; flex-shrink: 0; animation: none !important;
}

@keyframes pulse-badge {
    0%, 100% { transform: scale(1);    opacity: 1; }
    50%       { transform: scale(1.08); opacity: 0.85; }
}

/* F30-FIX-4 Gap 4 · mobile responsive · viewport < 768px badge 14x14 sin pulse */
@media (max-width: 768px) {
    .hub-dept-item.has-new .unread-count {
        min-width: 14px !important;
        height: 14px !important;
        font-size: 0.45rem !important;
        animation: none !important;
    }
}

/* F30-FIX · .has-mention · cross-canal mention indicator (separable de .has-new) */
.hub-dept-item.has-mention { position: relative; }
.hub-dept-item.has-mention::before {
    content: "@";
    position: absolute;
    left: 4px;
    top: 50%;
    transform: translateY(-50%);
    color: #FFB400;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.55rem;
    font-weight: 700;
    text-shadow: 0 0 6px rgba(255,180,0,0.45);
    animation: pulse-mention 1.5s infinite;
}
@keyframes pulse-mention {
    0%, 100% { opacity: 1; }
    50%       { opacity: 0.5; }
}

/* ── FOCUS MODE — transparencia escritorio ──────────────────── */



/* B-27 tier1: #iris-grid base · evita cam push footer · focus mode override en bloque F14 */
/* Sprint 3.1 step 3 v3 2026-05-13 · add height:100% (mirror guest #guest-video-grid pattern)
   Single-peer collapse fix: flex:1 alone left iris-grid 0-height in some parent chain states ·
   tile inside renders at intrinsic minimum · visual = mostly iris-grid bg (#020509 near-black).
   When focus mode escapes stacking (position:fixed) the bg-92%-black filled viewport hiding
   tiles · root cause for "dim covers presenter" CEO observation 2026-05-13. */
#iris-grid {
    flex: 1;
    min-height: 0;
    height: 100%;
    display: grid;
    gap: 6px;
    padding: 8px;
    box-sizing: border-box;
    background: #020509;
    overflow: hidden;
}
#iris-grid > .iris-tile {
    min-height: 0;
    min-width: 0;
    overflow: hidden;
}
/* GATE C · grid uniforme · n=3 centra el tile huérfano (3 tiles en 2×2 dejan celda vacía).
   El 3º abarca ambas columnas pero limita ancho al de una columna (gap 6px) y se centra.
   Clase puesta por _updateGridLayout · scope normal mode (focus/presenting limpian la clase). */
#iris-grid.grid-n3 > .iris-tile:nth-child(3) {
    grid-column: 1 / -1;
    justify-self: center;
    width: calc(50% - 3px);
}

/* ── IRIS CONTROLS — enterprise buttons ───────────────────────── */
.iris-ctrl-btn {
    background: rgba(0,128,255,0.06);
    border: 1px solid rgba(0,128,255,0.25);
    color: #4DA6FF;
    font-family: 'Orbitron', monospace;
    font-size: 0.4rem;
    letter-spacing: 2px;
    padding: 8px 18px;
    cursor: pointer;
    border-radius: 2px;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    gap: 6px;
}
.iris-ctrl-btn:hover {
    background: rgba(0,128,255,0.12);
    border-color: rgba(0,128,255,0.6);
}
.iris-ctrl-btn.muted,
.iris-ctrl-btn.cam-off,
.iris-ctrl-btn.noise-off {
    background: rgba(229,83,75,0.1);
    border-color: rgba(229,83,75,0.5);
    color: #e5534b;
}
/* F1/F3 · botón anti-ruido · label más largo ("ANTI-RUIDO") → letter-spacing + padding
   más ajustados que el resto para que su ancho NO domine la fila de controles (queda a la
   par de MIC/CAM/SCREEN). #id > .clase → gana sin !important. Responsive mantiene su 44px. */
#btn-iris-noise {
    letter-spacing: 1px;
    padding-left: 13px;
    padding-right: 13px;
}

/* ── UI-CONTRAST — jerarquía visual títulos > texto ───────────── */
.right-panel-section-title,
.hub-section-label,
.vault-section-label,
.iris-section-label {
    color: #d4d8e0;
    font-family: 'Orbitron', monospace;
    letter-spacing: 3px;
    font-size: 0.42rem;
}


/* ══════════════════════════════════════════════════════════════
   MOBILE RESPONSIVE — SYNARIS
   Breakpoints: 768px (tablet) · 480px (phone)
   ══════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

    /* ══ MOBILE APP LAYOUT — Palantir style ══════════════════ */

    /* Ocultar left panel — nav ya está en selector-area tabs  */
    .left-panel  { display: none !important; }
    .right-panel { display: none !important; }

    #root-layout {
        flex-direction: column;
        overflow: hidden;
        height: 100dvh;
        height: 100svh;
    }

    /* Main view ocupa todo el viewport como app nativa */
    .main-view {
        width: 100% !important;
        min-width: 0 !important;
        height: 100dvh !important;
        height: 100svh !important;
        display: flex !important;
        flex-direction: column !important;
        overflow: hidden !important;
    }

    /* ── HEADER compacto ─────────────────────────────────── */
    header {
        height: 52px !important;
        padding: 0 16px !important;
        flex-shrink: 0 !important;
        border-bottom: 1px solid rgba(0,128,255,0.12) !important;
        background: rgba(10,14,20,0.99) !important;
        /* SIN DESENFOQUE · medido: el fondo de este header esta al 0,99 de opacidad, asi
           que un blur(20px) dejaba pasar el 1% de la imagen de detras. No decoraba nada y
           obligaba al compositor a rehacer el desenfoque en CADA scroll de movil, que es
           justo donde menos sobra. Se retira entero, no se baja. */
    }

    /* .mia-title → reemplazada por .sidebar-brand-name */


    /* ── NAV TABS — BOTTOM BAR enterprise mobile ─────────── */
    .selector-area {
        position: fixed !important;
        bottom: 0 !important;
        left: 0 !important;
        right: 0 !important;
        top: auto !important;
        height: 56px !important;
        display: flex !important;
        flex-direction: row !important;
        align-items: stretch !important;
        justify-content: space-around !important;
        padding: 0 0 env(safe-area-inset-bottom) 0 !important;
        gap: 0 !important;
        border-top: 1px solid rgba(0,128,255,0.15) !important;
        border-bottom: none !important;
        background: rgba(8,12,18,0.98) !important;
        backdrop-filter: blur(8px) !important;            /* techo E2 · era 12 */
        -webkit-backdrop-filter: blur(8px) !important;
        z-index: 9999 !important;
        overflow: visible !important;
        flex-shrink: 0 !important;
        flex-wrap: nowrap !important;
        white-space: nowrap !important;
    }
    .selector-area::-webkit-scrollbar { display: none; }

    /* MI POST select — visible solo mobile */
    #mail-to-select {
        display: block !important;
    }

    .mode-btn {
        flex: 1 !important;
        font-size: 0.58rem !important;
        padding: 6px 2px 4px !important;
        letter-spacing: 1px !important;
        height: 56px !important;
        display: flex !important;
        flex-direction: column !important;
        align-items: center !important;
        justify-content: center !important;
        border-bottom: none !important;
        border-top: 2px solid transparent !important;
        min-width: 0 !important;
        white-space: nowrap !important;
    }
    .mode-btn.active {
        color: #0080FF !important;
        border-top-color: #0080FF !important;
        border-bottom: none !important;
        background: rgba(0,128,255,0.06) !important;
        text-shadow: 0 0 10px rgba(0,128,255,0.5) !important;
    }

    /* ── CONTENT PANELS — flex:1 sin tocar display (JS lo controla) ── */
    #chat-wall,
    #teams-wall,
    #mail-wall,
    #vault-wall,
    #iris-wall,
    #nexo-wall {
        flex: 1 !important;
        flex-direction: column !important;
        min-height: 0 !important;
        overflow: hidden !important;
        padding-bottom: 72px !important;
    }

    /* ── COMPACTACIÓN MÓVIL · carpetas a CHIPS horizontales (espejo VAULT #vault-dept-mobile-chips) · 2026-06-14 ──
       Antes: #mpx-folders apilaba 7 carpetas + casilla + etiquetas a max-height:35vh (~233px)
       → empujaba la lista de correos abajo. Ahora: las 7 carpetas (#mpx-folder-chips) pasan a
       fila scrollable-x → la lista queda arriba sin scroll previo (~110px). El CASILLA FILTER
       (switch de buzones admin) sigue VISIBLE como dropdown compacto. ETIQUETAS/labels ocultas
       en móvil (no son carpetas · CEO). COMPOSE compacto. Solo CSS+1 wrapper · cero lógica. */
    #mpx-folders {
        flex: 0 0 auto !important;
        max-height: none !important;                 /* era 35vh · ya no apila vertical */
        padding: 8px 0 4px !important;
        border-right: none !important;
        border-bottom: 1px solid rgba(0,128,255,0.08);
    }
    /* COMPOSE compacto */
    #mpx-compose-btn { margin: 0 12px 8px !important; padding: 7px 0 !important; }
    /* fila de chips horizontal scrollable (las 7 carpetas) */
    #mpx-folder-chips {
        flex-direction: row !important;
        flex-wrap: nowrap !important;
        overflow-x: auto !important;
        gap: 6px !important;
        padding: 0 12px 2px !important;
        scrollbar-width: none !important;
        -webkit-overflow-scrolling: touch;
    }
    #mpx-folder-chips::-webkit-scrollbar { display: none !important; }
    /* cada carpeta = chip · !important vence los estilos inline + los inline-JS de active-state */
    #mpx-folder-chips .mpx-folder {
        flex: 0 0 auto !important;
        border: 1px solid rgba(255,255,255,0.10) !important;
        border-radius: 2px !important;
        padding: 0 12px !important;
        min-height: 40px !important;
        justify-content: center !important;
        gap: 6px !important;
        white-space: nowrap !important;
        background: transparent !important;
        color: rgba(255,255,255,0.6) !important;
    }
    #mpx-folder-chips .mpx-folder.active {
        border-color: rgba(0,128,255,0.5) !important;
        background: rgba(0,128,255,0.12) !important;
        color: #4DA6FF !important;
    }
    /* badges vacíos (counts no cableados hoy) no deben añadir gap asimétrico al chip ·
       si en el futuro se pueblan, :empty deja de aplicar y el count aparece */
    #mpx-folder-chips .mpx-badge:empty { display: none !important; }
    /* CASILLA FILTER · switch de buzones · queda VISIBLE como dropdown compacto */
    #mpx-alias-filter {
        margin: 0 12px !important;
        width: calc(100% - 24px) !important;
    }
    /* En móvil, dentro de EXTERNAL solo se muestran chips + casilla · ocultar headers,
       label CASILLA FILTER, ETIQUETAS, labels y hr (no son carpetas) */
    #mpx-section-external > *:not(#mpx-folder-chips):not(#mpx-alias-filter) {
        display: none !important;
    }
    #mpx-list-pane {
        flex: 1 1 auto !important;
        min-height: 200px;
        border-right: none !important;
    }
    /* MIPOST-MOBILE-GMAIL-PATTERN · mpx-viewer-pane HIDDEN default en mobile ·
       reveal solo en .mpx-mobile-overlay-active state (fullscreen overlay z9999).
       JS add/remove de la class ya implementado: loadMpxMessage soul.js L10225
       (add) · _mpxCloseMessageMobile L10191 (remove) · ESC + BACK button ya bound.
       Patrón Gmail/Outlook mobile: list 100% viewport · viewer fullscreen on tap. */
    #mpx-viewer-pane {
        display: none !important;
    }

    /* ── C1 · VAULT 2-pane stack-vertical · override inline flex-row sizing ── */
    #vault-tree-pane {
        flex: 0 0 auto !important;
        max-height: 30vh;
        border-right: none !important;
        border-bottom: 1px solid rgba(255,255,255,0.06);
    }
    #vault-center-pane {
        flex: 1 1 auto !important;
        min-height: 0;
    }

    /* ────────────────────────────────────────────────────────────
       C2-1 · CSS sweep mobile · 8 bugs + GLOBAL header padding
       ──────────────────────────────────────────────────────────── */

    /* C2-1.a · scroll bars chromeless (bugs 3, 10) */
    *::-webkit-scrollbar { width: 0 !important; height: 0 !important; display: none !important; }
    * { scrollbar-width: none !important; -ms-overflow-style: none !important; }

    /* C2-1.b · header padding mobile (bug 1, GLOBAL)
       NOTE · header rule moved to @media block @ ~L1520 (cascade winner)
       to defeat specificity collision · see commit 178de85 followup. */
    #mobile-brand { padding-right: 8px; }
    #admin-panel-anchor { margin-left: 8px; }

    /* C2-1.c · VAULT toolbar polish (bugs 4, 5 partial) */
    #drop-zone {
        height: auto !important;
        padding: 12px 16px !important;
    }
    #drop-zone .vault-title + div { display: none !important; }   /* hide "DRAG & DROP" subtext */
    #vault-filter-chips {
        flex-wrap: wrap !important;
        padding: 8px 12px !important;
        gap: 0 4px !important;
    }
    #vault-filter-chips > .vault-chip {
        padding: 8px 14px !important;
        flex: 0 0 auto !important;
    }
    #vault-filter-chips > div[style*="flex:1"] {   /* spacer between chips and search */
        flex-basis: 100% !important;
        height: 0 !important;
    }
    #vault-search-box {
        flex: 1 1 auto !important;
        padding: 0 !important;
        margin-top: 8px !important;
    }
    #vault-search { width: 100% !important; }
    #vault-view-toggle { margin-top: 8px !important; }

    /* C2-1.d · MIPOST sidebar polish (bugs 7, 8, 12) */
    #mpx-section-internal {
        margin-top: 16px !important;
        padding-top: 16px !important;
        border-top: 1px solid rgba(0,128,255,0.08);
    }
    #mpx-tenant-filter {
        max-width: 180px !important;
        align-self: flex-start !important;
        margin-left: 14px !important;
    }
    #mpx-toolbar { padding: 12px 14px !important; }   /* breathing header → list */
    #mpx-list > * { padding: 14px 14px !important; }   /* row vertical rhythm */

    #teams-wall {
        flex-direction: column !important;
        flex: 1 !important;
        min-height: 0 !important;
        overflow: hidden !important;
    }

    #hub-chat-wall {
        flex: 1 !important;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch !important;
        padding: 10px 12px !important;
        min-height: 0 !important;
    }

    #mia-output {
        flex: 1 !important;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch !important;
        min-height: 0 !important;
    }

    /* ── INPUT ZONE — fijo al bottom con safe area iOS ──── */
    .input-zone {
        flex-shrink: 0 !important;
        padding: 10px 14px !important;
        padding-bottom: max(10px, env(safe-area-inset-bottom)) !important;
        border-top: 1px solid rgba(0,128,255,0.12) !important;
        background: rgba(10,14,20,0.99) !important;
        /* SIN DESENFOQUE · mismo criterio que el header y misma medida: el fondo esta al
           0,99 de opacidad, asi que el desenfoque dejaba pasar el 1% de lo que hay detras.
           Y lo que hay detras es el muro de conversacion, que scrollea: cada cuadro del
           scroll obligaba a rehacer un desenfoque que no se ve. */
        margin-bottom: 56px !important;
    }

    .bar-container {
        gap: 8px !important;
    }

    #user-input {
        font-size: 13px !important;
        min-height: 36px !important;
        max-height: 120px !important;
        padding: 8px 10px !important;
        line-height: 1.4 !important;
    }

    #btn-send {
        height: 36px !important;
        padding: 0 14px !important;
        font-size: 0.38rem !important;
        flex-shrink: 0 !important;
    }

    /* Ocultar e2e-bar y nodes-flow en móvil */
    .e2e-bar    { display: none !important; }
    .nodes-flow { display: none !important; }

    /* ── HUB BUBBLES ─────────────────────────────────────── */
    .hub-bubble {
        max-width: 80% !important;
        font-size: 0.7rem !important;
        padding: 10px 14px !important;
        line-height: 1.5 !important;
    }

    /* ── MI POST ─────────────────────────────────────────── */
    #email-inbox-container {
        flex: 1 !important;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch !important;
        padding: 8px !important;
        margin: 6px 0 4px !important;
    }

    #mail-body {
        font-size: 0.65rem !important;
    }

    /* ── VAULT ───────────────────────────────────────────── */
    /* ITEM-VAULT-TYPO 2026-06-15 · este `> div` golpea el zoneHeader (+zoneBody), NO las filas de
       archivo (nietas dentro de zoneBody). El 0.48rem !important bajaba el label de zona por debajo del
       piso del estándar (MI POST=0.5rem · "diminuto" reportado). Subido a 0.5rem = piso del sistema.
       Las filas de archivo conservan su inline (nombre 0.55 · size/fecha 0.5 · Share Tech Mono tabular). */
    #vault-file-list > div {
        grid-template-columns: 36px 1fr 50px !important;
        font-size: 0.5rem !important;
    }

    #vault-file-list > div > div:nth-child(4),
    #vault-file-list > div > div:nth-child(5) {
        display: none !important;
    }

    /* ── IRIS ────────────────────────────────────────────── */
    #iris-grid {
        flex: 1 !important;
        padding: 8px !important;
        gap: 6px !important;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch !important;
    }

    /* GATE C · grid uniforme · los tiles llenan su celda (grid stretch). Se elimina
       flex:0 1 100% (inerte · #iris-grid es display:grid) y aspect-ratio:16/9 (forzaba
       proporción que peleaba con gridTemplateRows:1fr → celdas desiguales + franjas). */
    #iris-grid > div[data-peer] {
        min-width: 0 !important;
        min-height: 0 !important;
    }

    .iris-ctrl-btn {
        padding: 8px 14px !important;
        font-size: 0.38rem !important;
    }

    /* ── FLOAT WINDOW ────────────────────────────────────── */
    #iris-float-win {
        width: 150px !important;
        /* H10 · el `bottom: 80px !important` se retira: la posicion vertical inicial la fija
           _irisFloatPosicionInicial midiendo el subtab, y un !important aqui le ganaria al
           inline que pone el JS. El horizontal si se queda aqui. */
        right: 10px !important;
    }
    /* H10 · los minimos se bajan con ESPECIFICIDAD (`body #id` gana a `#id`), no con otro
       !important ni fiandolo al orden del fichero. El bloque base vive mas abajo —junto a las
       variables de IRIS— y por orden le ganaria a este; con un id + una etiqueta, no. Se
       probaron las dos alternativas: mover la regla base delante fallo dos veces porque
       "@media" aparece tambien dentro de comentarios y el ancla no era fiable. */
    body #iris-float-win {
        min-width: 150px;
        min-height: 120px;
    }

    /* ── MODALES ─────────────────────────────────────────── */
    #vault-share-modal > div > div {
        min-width: 90vw !important;
        max-width: 96vw !important;
    }

    #iris-pre-invite-modal > div,
    #iris-invite-modal > div {
        width: 92vw !important;
        padding: 16px !important;
    }
}

@media (max-width: 480px) {

    /* ── TIPOGRAFÍA PHONE ────────────────────────────────── */
    body { font-size: 13px; }

    /* .mia-title → reemplazada por .sidebar-brand-name */


    header { height: 48px !important; }

    .mode-btn {
        font-size: 0.58rem !important;
        padding: 4px 9px !important;
    }

    /* Badge IRIS compacto */
    #iris-call-badge        { padding: 3px 6px !important; }
    #iris-call-badge-label  { font-size: 0.34rem !important; }

    /* ── IRIS grid — stack vertical 1 columna (grid-template hereda del bloque @768) ──────
       FIX solapamiento ≤480px · ELIMINADO el #iris-grid { flex-direction:column;
       align-items:center }: flex-direction era inerte (es display:grid) y align-items:center
       mataba el stretch por defecto → los tiles se dimensionaban a min-height:180px y rebasaban
       su celda pisando al de arriba. Sin la regla, vuelve align-items:stretch → cada tile llena
       su fila auto-generada. */
    #iris-grid > div[data-peer] {
        width: 100% !important;
        max-width: 100% !important;
    }

    /* ── VAULT phone ─────────────────────────────────────── */
    #vault-file-list > div {
        grid-template-columns: 32px 1fr 48px !important;
    }

    /* ── INPUT phone — 16px mínimo para evitar autozoom iOS ── */
    #user-input,
    #mail-body,
    input,
    textarea {
        font-size: 16px !important;
    }
}

/* ── MIA TYPING INDICATOR — Design System v12 ────────────────── */
.mia-typing-indicator {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    margin: 6px 0;
    font-family: 'Share Tech Mono', monospace;
    color: #4DA6FF;
    opacity: 0.85;
    border-left: 2px solid #4DA6FF;
    background: rgba(0, 128, 255, 0.04);
    border-radius: 0 2px 2px 0;
    width: fit-content;
}
.mia-typing-label {
    font-size: 0.55rem;
    letter-spacing: 2px;
    font-weight: 600;
}
.mia-typing-dots {
    display: inline-flex;
    gap: 3px;
}
.mia-typing-dots i {
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: #4DA6FF;
    animation: miaTypingPulse 1.2s infinite ease-in-out;
}
.mia-typing-dots i:nth-child(2) { animation-delay: 0.2s; }
.mia-typing-dots i:nth-child(3) { animation-delay: 0.4s; }
@keyframes miaTypingPulse {
    0%, 60%, 100% { opacity: 0.25; transform: scale(0.85); }
    30%           { opacity: 1;    transform: scale(1); }
}

/* ═══════════════════════════════════════════════════════════════
   IRIS TICKER · Inline chat + notes bar bajo video grid
   ═══════════════════════════════════════════════════════════════ */
.iris-ticker {
    position: absolute;
    left: 50%;
    bottom: 60px;
    width: 72%;
    max-width: 760px;
    min-width: min(320px, 92vw);
    height: auto;
    max-height: 50vh;
    z-index: 8;
    background: rgba(8, 12, 18, 0.94);
    backdrop-filter: blur(8px);                           /* techo E2 · era 14 */
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid rgba(0, 128, 255, 0.22);
    border-radius: 8px 8px 0 0;
    box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.7);
    transform: translate(-50%, calc(100% + 60px));
    transition: transform 620ms cubic-bezier(0.16, 0.84, 0.30, 1), opacity 420ms ease;
    opacity: 0;
    pointer-events: none;
    display: flex;
    flex-direction: column;
    padding: 12px 16px 12px 16px;
    gap: 10px;
    overflow: hidden;
}
.iris-ticker-stream {
    max-height: 34vh;
    min-height: 120px;
}


.iris-ticker.iris-ticker-open {
    transform: translate(-50%, 0);
    opacity: 1;
    pointer-events: auto;
}
@media (max-width: 768px) {
    .iris-ticker {
        width: 94%;
        max-width: 94%;
        min-width: 0;
        height: 50vh;
        bottom: 60px;
    }
}

.iris-ticker-row-top {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.iris-ticker-row-top .iris-ticker-mode {
    align-self: flex-start;
}

.iris-ticker-mode {
    display: flex;
    gap: 2px;
    flex-shrink: 0;
}
.iris-ticker-mode-btn {
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.06);
    color: #6b7280;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.42rem;
    letter-spacing: 2px;
    padding: 4px 10px;
    cursor: pointer;
    border-radius: 2px;
    transition: all 140ms;
}
.iris-ticker-mode-btn:hover {
    color: rgba(230,235,245,0.82);
    background: rgba(255, 255, 255, 0.03);
}
.iris-ticker-mode-btn.active {
    color: #4DA6FF;
    border-color: rgba(0, 128, 255, 0.4);
    background: rgba(0, 128, 255, 0.06);
}

.iris-ticker-stream {
    height: 100px;
    overflow-y: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
    overflow-x: hidden;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.62rem;
    color: rgba(230,235,245,0.82);
    letter-spacing: 0.3px;
    line-height: 1.4;
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 6px 8px;
    background: rgba(0, 0, 0, 0.25);
    border-radius: 2px;
    border: 1px solid rgba(255,255,255,0.03);
}
.iris-ticker-empty {
    color: #4b5563;
    font-size: 0.65rem;
    letter-spacing: 2px;
}
.iris-ticker-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 3px 8px;
    border-left: 2px solid rgba(255,255,255,0.08);
    border-radius: 2px;
    line-height: 1.4;
    word-break: break-word;
    background: rgba(255,255,255,0.015);
}
.iris-ticker-item.mine {
    background: rgba(0, 128, 255, 0.09);
    border-left-color: #4DA6FF;
}
.iris-ticker-item.mine .iris-ticker-item-sender {
    color: #4DA6FF;
}
.iris-ticker-item-tag {
    font-size: 0.55rem;
    letter-spacing: 2px;
    font-weight: 600;
}
.iris-ticker-item-sender {
    color: #8a94a6;
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 1px;
    flex-shrink: 0;
    min-width: 140px;
}
.iris-ticker-item-body {
    color: rgba(230,235,245,0.82);
    white-space: normal;
    word-break: break-word;
    flex: 1;
}

.iris-ticker-tags {
    display: flex;
    gap: 4px;
}
.iris-ticker-tag {
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.06);
    color: #6b7280;
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.52rem;
    letter-spacing: 2px;
    padding: 3px 8px;
    cursor: pointer;
    border-radius: 2px;
    transition: all 140ms;
}
.iris-ticker-tag:hover { background: rgba(255,255,255,0.03); color: rgba(230,235,245,0.82); }
.iris-ticker-tag.active { font-weight: 600; }

.iris-ticker-input-row {
    display: flex;
    gap: 6px;
    align-items: flex-end;
}
.iris-ticker-input {
    flex: 1;
    background: rgba(0, 128, 255, 0.04);
    border: 1px solid rgba(0, 128, 255, 0.15);
    border-radius: 2px;
    padding: 5px 10px;
    color: rgba(230,235,245,0.82);
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.62rem;
    line-height: 1.4;
    outline: none;
    resize: none;
    overflow-y: auto;
    min-height: 22px;
    max-height: 48px;
    transition: border-color 140ms;
}
.iris-ticker-input:focus { border-color: rgba(0, 128, 255, 0.45); }
.iris-ticker-input::placeholder { color: #4b5563; letter-spacing: 1px; }
.iris-ticker-send {
    background: rgba(0, 128, 255, 0.08);
    border: 1px solid rgba(0, 128, 255, 0.3);
    color: #4DA6FF;
    font-family: 'Orbitron', monospace;
    font-size: 0.62rem;
    padding: 0 12px;
    cursor: pointer;
    border-radius: 2px;
    transition: all 140ms;
    min-height: 44px;
    min-width: 44px;
}
.iris-ticker-send:hover { background: rgba(0, 128, 255, 0.16); color: rgba(230,235,245,0.82); }

@media (max-width: 768px) {
    .iris-ticker { padding: 6px 10px 8px 10px; }
    .iris-ticker-item { max-width: 200px; }
    .iris-ticker-item-body { max-width: 140px; }
}

.iris-ticker-stream::-webkit-scrollbar { display: none; }

/* IRIS ticker · ocultar scrollbars nativos (input + stream) */
.iris-ticker-input,
.iris-ticker-stream {
    scrollbar-width: none !important;
    -ms-overflow-style: none !important;
}
.iris-ticker-input::-webkit-scrollbar,
.iris-ticker-input::-webkit-scrollbar-track,
.iris-ticker-input::-webkit-scrollbar-thumb,
.iris-ticker-input::-webkit-scrollbar-corner,
.iris-ticker-stream::-webkit-scrollbar,
.iris-ticker-stream::-webkit-scrollbar-track,
.iris-ticker-stream::-webkit-scrollbar-thumb,
.iris-ticker-stream::-webkit-scrollbar-corner {
    display: none !important;
    width: 0 !important;
    height: 0 !important;
    background: transparent !important;
}





/* ── SOVEREIGN CONFIRM MODAL · enterprise grade ────────────── */
#sovereign-confirm-overlay {
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.75);
    backdrop-filter: blur(4px);
    display: flex; align-items: center; justify-content: center;
    z-index: 99999;
    animation: sovConfirmFadeIn 0.15s ease-out;
}
@keyframes sovConfirmFadeIn { from { opacity: 0; } to { opacity: 1; } }
#sovereign-confirm-modal {
    background: #0a0d14;
    border: 1px solid rgba(74,144,217,0.25);
    border-radius: 2px;
    min-width: min(380px, 92vw);
    max-width: min(520px, 92vw);
    padding: 28px 32px 22px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.03) inset;
}
#sovereign-confirm-title {
    font-family: 'Orbitron', monospace;
    font-size: 0.62rem;
    letter-spacing: 4px;
    color: #4DA6FF;
    margin-bottom: 14px;
    text-transform: uppercase;
}
#sovereign-confirm-msg {
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.72rem;
    color: rgba(255,255,255,0.75);
    line-height: 1.6;
    letter-spacing: 0.5px;
    margin-bottom: 24px;
    white-space: pre-line;
}
#sovereign-confirm-btns {
    display: flex; gap: 10px; justify-content: flex-end;
}
.sov-btn {
    font-family: 'Orbitron', monospace;
    font-size: 0.55rem;
    letter-spacing: 3px;
    padding: 9px 20px;
    border-radius: 2px;
    cursor: pointer;
    background: transparent;
    transition: all 0.15s;
    text-transform: uppercase;
}
.sov-btn-cancel {
    border: 1px solid rgba(255,255,255,0.12);
    color: rgba(255,255,255,0.5);
}
.sov-btn-cancel:hover {
    border-color: rgba(255,255,255,0.3);
    color: rgba(255,255,255,0.9);
}
.sov-btn-ok {
    border: 1px solid rgba(255,68,102,0.4);
    color: #ff4466;
}
.sov-btn-ok:hover {
    background: rgba(255,68,102,0.08);
    border-color: #ff4466;
    color: #ff6b85;
}

/* ══ SOVEREIGN SCROLLBAR GLOBAL · OCULTO TOTAL ═══════════════════════════════════
   CEO 2026-06-10 · "el sistema es autoscrolleable" · CERO barra azul en NINGÚN sitio.
   Reemplaza ISSUE-8 (invisible→azul al hover) por ocultamiento real (mismo patrón
   ratificado .synaris-scroll-hidden 2026-05-19). El !important NEUTRALIZA por override
   TODAS las reglas que reintroducían barra/azul (soberanas, .synaris-scroll + sus 4
   usuarios, selector-atributo [style*=overflow], evWrap calendario) SIN editarlas.
   Scroll rueda/touch INTACTO · NO overflow:hidden en ningún sitio. */
*::-webkit-scrollbar { width: 0 !important; height: 0 !important; display: none !important; }
*::-webkit-scrollbar-track,
*::-webkit-scrollbar-thumb,
*::-webkit-scrollbar-corner { background: transparent !important; }
* { scrollbar-width: none !important; -ms-overflow-style: none !important; }
html { scrollbar-color: transparent transparent !important; }


/* ══ HUB-FIRSTMSG fix · prevent word-per-line on first render ══ */
.hub-bubble {
    min-width: 60px !important;
    white-space: pre-line !important;  /* FIX burbujas · era pre-wrap · colapsaba fit-content y partia texto corto en espacios. pre-line preserva saltos reales del usuario y deja fluir */
    overflow-wrap: break-word !important;   /* E1 · solo parte palabras que no caben solas (URLs), no normales */
    word-break: normal !important;
    display: block; /* FIX burbujas · era inline-block · conflictuaba con el flex-item del .hub-msg-wrap */
}


/* ══ MOBILE-BRAND header · visible solo ≤768px ══════════════ */
#mobile-brand { display: none; }
@media (max-width: 768px) {
    #mobile-brand {
        display: flex;
        align-items: center;
        gap: 10px;
        height: 100%;
    }
    #mobile-brand img {
        height: 28px; width: 28px;
        object-fit: contain;
        filter: drop-shadow(0 0 6px rgba(0,128,255,0.3));
    }
    #mobile-brand span {
        font-family: 'Orbitron', monospace;
        font-size: 0.85rem;
        letter-spacing: 3px;
        font-weight: 700;
        background: linear-gradient(135deg, #0080FF 0%, #0055CC 100%);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
    }
    /* P0-F1v2 · header + .header-controls + #btn-profile rules movidos a
       @media (max-width: 1024px) al final del archivo · cobertura tablet
       (cliff 769→767 anterior · iPad portrait/landscape recibía desktop layout). */
}

/* ═══════════════════════════════════════════════════════════════════
   IRIS PRESENTER FOCUS MODE (F14) · Tier 1
   Escala z-index: --z-iris-overlay 900 · --z-iris-grid 950
                   --z-iris-palette 960 · --z-iris-modal 1000
   ═══════════════════════════════════════════════════════════════════ */

:root {
    --z-iris-overlay: 900;
    --z-iris-grid:    950;
    --z-iris-palette: 960;
    --z-iris-modal:   1000;
    --iris-transition: 280ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* H10 · minimos de #iris-float-win. Estaban INLINE (soul.js showIrisFloat) y por eso la
   regla movil llevaba muerta desde que se escribio: ninguna hoja sin !important vence a un
   inline, y ademas min-width y width son propiedades DISTINTAS — el minimo actua de suelo,
   asi que `width:150px !important` no podia con `min-width:240px` inline. Medido: el
   flotante ocupaba 240px en una pantalla de 480, media pantalla.
   Aqui, en la hoja y por delante de los @media, si se pueden bajar por breakpoint. */
#iris-float-win {
    min-width: 240px;
    min-height: 180px;
}

/* Badge PRESENTING */
body.iris-focus-active #iris-grid > div[data-peer][data-presenter="true"] .iris-presenter-badge,
.iris-presenter-badge {
    position: absolute;
    top: 8px;
    left: 8px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: Orbitron, monospace;
    font-size: 0.55rem;
    letter-spacing: 2px;
    color: #fff;
    background: #3FB950;
    padding: 4px 10px;
    border-radius: 2px;
    z-index: calc(var(--z-iris-grid) + 10);
    pointer-events: none;
}
.iris-presenter-badge::before {
    content: '';
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: #fff;
    animation: iris-pulse 1.2s infinite;
}
@keyframes iris-pulse {
    0%,100% { opacity: 1; }
    50%     { opacity: 0.35; }
}

/* Sprint 3.2 Deploy 2 2026-05-14 · badge unification (Option C) + specificity fix
   Self-view of local tile shows "COMPARTIENDO PANTALLA" only (2nd-person direct).
   Peers viewing the presenter's tile still see "PRESENTING · USER" (3rd-person
   factual) since they don't have .local-presenting class on their side · clean
   role differentiation matches Zoom/Meet pattern.
   !important required because L1731 chain (body.iris-focus-active #iris-grid >
   div[data-peer][data-presenter="true"] .iris-presenter-badge) has specificity
   0,1,4,3 which beats this rule's 0,0,3,0 when focus mode is active on sharer. */
.iris-tile.local-presenting .iris-presenter-badge {
    display: none !important;
}
/* Sprint IRIS-SHARE-V2 A2 2026-05-14 · badge unification cierre · oculta también
   el lower userid label (anonymous div pre-A1 · ahora .iris-tile-userid via A1
   refactor). Self-tile sharing muestra solo COMPARTIENDO PANTALLA verde via
   .local-presenting::after (existente L1778). Defense in depth + soul.js skip
   badge creation cuando tile ya tiene .local-presenting. */
.iris-tile.local-presenting .iris-tile-userid {
    display: none !important;
}

/* Sprint 3.2 P0 2026-05-14 · anti-loop · "COMPARTIENDO PANTALLA" overlay badge on
   local tile during own screen-share. Matches Zoom/Meet/Teams/Webex unanimous
   pattern: presenter's local tile keeps camera, overlay text tells them share
   is active. JS toggles .local-presenting class on/off via _start/_stopScreenShareFlow. */
.iris-tile.local-presenting::after {
    content: 'COMPARTIENDO · ' attr(data-share-surface);
    position: absolute;
    bottom: 8px;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(63, 185, 80, 0.92);
    color: #fff;
    font-family: Orbitron, monospace;
    font-size: 0.55rem;
    letter-spacing: 2px;
    padding: 4px 12px;
    border-radius: 2px;
    pointer-events: none;
    z-index: 5;
    white-space: nowrap;
}

/* ── FOCUS MODE · clean rewrite · no stacking traps ─────────────── */

/* Sprint 3.1 step 3 v5 2026-05-14 · DIM OVERLAY REMOVED
   Original step 3.A 2026-05-13 body::before z=900 rgba(0,0,0,0.8) ·
   CEO Browser MCP runtime 2026-05-14 confirmed the dim was painting
   OVER iris-grid children despite z=900 < z=950 math. Stacking spec
   says iris-grid (z=950 root via position:fixed) should win, but real
   render had video element pixels obscured · empirical override.
   Rationale for safe removal:
     · body bg already dark (rgb(8,12,16))
     · header/nav/panel-R have own opaque backgrounds
     · iris-grid (position:fixed inset:0) fully covers viewport during focus
     · ::before was redundant + caused the bug
   Resize-handle hide (step 3.B) retained · valuable independent of dim. */

/* DIM ::before ELIMINADO 2026-05-15 · root cause pantalla negra · L0 confirmado MANU rgba(0,0,0,0.96) z:900 cubría video compositor · regresión F2-D1 cc06434 · historia 556bee3 */
/* Sprint IRIS-SHARE-V2 A4 2026-05-14 · VERIFY focus mode peer integration post-A3
   Coexistencia confirmada por inspección estructura CSS (curl + grep smoke):
     · body.iris-focus-active::before (A3 dim · z:900) DEBAJO del grid
     · body.iris-focus-active #iris-grid (L1828+ · position:fixed z:950) ARRIBA del dim
     · iris-grid background:transparent (L1841) preservado · padding 20px muestra dim
     · Hero presenter (L1847+) · strip thumbnails (L1881+) · mobile MQ (L1910+) intactos
   Sin código nuevo · solo doc verification + bump cache buster para refresh client.
   Comportamiento esperado peer view post-A3+A4: chrome (header/sidebar/panel) dimmed
   atrás · iris-grid completo con hero+strip ARRIBA · share con protagonismo visual. */

/* Backdrop dentro del grid (evita stacking context global)
   Step 3 v2 2026-05-13 · position:fixed inset:0 ESCAPES main-view z=20 stacking
   trap · iris-grid now paints at ROOT z=950 ABOVE body::before dim z=900.
   L0 audit: main.main-view (theme_core.css:442) creates context @ root z=20.
   Without fixed, iris-grid z=950 is LOCAL to main-view context, painting at
   root z=20 (trapped under dim). Fixed escapes ancestor stacking entirely. */
/* IRIS FOCUS MODE · F2-D1 · consolidado state-driven · hero izquierda + strip derecha (Zoom/Teams pattern) */
body.iris-focus-active #iris-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 200px;
    grid-template-rows: minmax(0, 1fr);
    gap: 12px;
    /* GATE FIX-1 2026-06-01 · padding-top 20 → 87 · clear elevated tab bars (selector-area 42 + subtab 45 = 87) que tapaban hero · scoped focus-active */
    padding: calc(87px + env(safe-area-inset-top)) calc(20px + env(safe-area-inset-right)) calc(20px + env(safe-area-inset-bottom)) calc(20px + env(safe-area-inset-left));
    position: fixed;
    inset: 0;
    z-index: var(--z-iris-grid);
    /* IRIS-GRID-BG · container opaco oculta desk SYNARIS al peer · NO es ::before global (L1819 root cause) · grid z:950 nunca cubre video (tile z:2 interno) */
    background: #020509;
    overflow: hidden;
    transition: background var(--iris-transition);
}
/* Hero presenter · columna 1 · ocupa todo el alto */
body.iris-focus-active #iris-grid > div[data-peer][data-presenter="true"] {
    grid-column: 1;
    grid-row: 1;
    position: relative;
    z-index: 2;
    width: 100%;
    height: 100%;
    min-width: 0;
    min-height: 0;
    max-width: 100%;
    max-height: none;
    aspect-ratio: auto;
    background: #000;
    border: 1px solid rgba(63, 185, 80, 0.35);
    border-radius: 3px;
    overflow: hidden;
    transition: opacity var(--iris-transition);
    opacity: 1;
}
/* F-IRIS-LAYOUT-STD R6 · default video object-fit (post-R4 quitó inline JS) */
.iris-tile video {
    object-fit: cover;
}
body.iris-focus-active #iris-grid > div[data-peer][data-presenter="true"] video {
    width: 100%;
    height: 100%;
    object-fit: contain !important;
    background: #000;
    transform: none;
    -webkit-transform: none;
}
/* Strip · columna 2 · scroll vertical · cámaras participantes */
body.iris-focus-active #iris-grid > div[data-peer]:not([data-presenter="true"]) {
    grid-column: 2;
    width: 200px;
    min-width: 200px;
    max-width: 200px;
    height: 133px;
    aspect-ratio: 3/2;
    border-radius: 2px;
    overflow: hidden;
    position: relative;
    z-index: 2;
    align-self: start;
}
body.iris-focus-active #iris-grid > div[data-peer]:not([data-presenter="true"]) video {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* A5-FIX 2026-05-26 · PEER-LOCK · el viewer en focus-active tenía los controles (#iris-controls)
   y el ticker (#iris-ticker) atrapados bajo #iris-grid (position:fixed inset:0 z:950) → no clicables.
   Los elevamos a position:fixed sobre el grid (mirror de .iris-stop-share-toolbar z:1010 y de la
   defensa pointer-events de body.iris-presenting). Scope solo peer/viewer · normal mode intacto. */
body.iris-focus-active #iris-controls,
body.iris-share-staged #iris-controls {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: calc(var(--z-iris-grid) + 20);   /* 970 · sobre grid 950 */
    pointer-events: auto;
    justify-content: center;
    background: rgba(2, 5, 9, 0.88);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
/* C1.d · presenting NO puede heredar el fixed de la regla de arriba. En focus-active y
   share-staged da igual que la barra ocupe el viewport entero, porque el overlay ya tapa
   los paneles; en presenting la UI es navegable y los paneles estan a la vista, asi que
   `fixed left:0 right:0` pisaba el riel del panel izquierdo.
   Se ancla por CONSTRUCCION, no con offsets laterales fijos que se romperian al plegar o
   desplegar un panel: `absolute` resuelve contra el ancestro posicionado mas cercano.
   CORREGIDO 2026-08-05 · aqui ponia que ese ancestro era main.main-view y que #iris-wall
   era `static`. Es FALSO: #iris-wall computa `position: relative`, asi que la referencia
   real es EL, no main-view. El error salio de medir sobre un banco que reconstruia el DOM
   a mano —el #iris-wall de aquel banco no tenia CSS— y lo destapo el banco que extrae la
   pagina real (scripts/smoke/iris_banco_render.js). La conducta era y sigue siendo
   correcta, porque el box de #iris-wall coincide con el area util; lo que estaba mal era
   la explicacion, que es peor: el siguiente que lea esto se la cree.
   Resto de valores identicos a la regla compartida. */
body.iris-presenting #iris-controls {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: calc(var(--z-iris-grid) + 20);   /* 970 · mismo nivel que en los otros estados */
    pointer-events: auto;
    justify-content: center;
    background: rgba(2, 5, 9, 0.88);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
body.iris-focus-active #iris-ticker,
body.iris-share-staged #iris-ticker {
    position: fixed;                           /* escapa main-view · pinta sobre grid */
    bottom: 52px;                              /* sobre la barra de controles */
    z-index: calc(var(--z-iris-grid) + 20);    /* 970 · sobre grid 950 */
    background: rgba(2, 5, 9, 0.82);
    /* Bug A (2026-05-26) · el A5-FIX previo añadía left:0/right:0/pointer-events:auto aquí.
       (a) left:0/right:0 chocaban con el transform:translate(-50%) base → el chat abierto
           quedaba corrido ~50% de su ancho fuera del borde izquierdo (oculto en el share).
       (b) pointer-events:auto incondicional hacía que el ticker CERRADO (opacity:0) capturara
           clicks sobre el share. position:fixed + z:970 ya lo elevan; el centrado (left:50% +
           translateX(-50%)) y el toggle open/closed (.iris-ticker-open → auto · cerrado → none)
           los controla la base · por eso aquí NO se redeclara left/right/transform/pointer-events. */
}

/* Bug E (2026-05-26) · viewer en focus-active veía el share pero NO los tabs.
   .selector-area (nav módulos MIA/HUB/VAULT/IRIS/NEXO) y #iris-subtab-bar (AHORA/
   CALENDARIO/AGENDA) viven DENTRO de main.main-view (stacking context z:20). El
   #iris-grid usa position:fixed para escapar a root z:950 y los tapaba. Mismo patrón
   que el A5-FIX de #iris-controls/#iris-ticker: los elevamos con position:fixed para
   escapar main-view y pintarlos SOBRE el grid · navegables durante el share.
   CEO-approved (opción "elevar nav sobre el grid"). z:975 < toast 990 < modal 1000. */
body.iris-focus-active .selector-area,
body.iris-share-staged .selector-area {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: calc(var(--z-iris-grid) + 25);    /* 975 · sobre grid 950 y controls 970 */
    background: var(--bg-panel);
    pointer-events: auto;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
}
body.iris-focus-active #iris-subtab-bar,
body.iris-share-staged #iris-subtab-bar {
    position: fixed;
    top: 48px;                                 /* debajo de la barra de módulos */
    left: 0;
    right: 0;
    z-index: calc(var(--z-iris-grid) + 25);
    pointer-events: auto;
    background: rgba(8, 12, 16, 0.97);
}

/* Mobile */
@media (max-width: 768px) {
    body.iris-focus-active #iris-grid {
        grid-template-rows: minmax(0, 1fr) 90px;
        padding: calc(8px + env(safe-area-inset-top)) calc(8px + env(safe-area-inset-right)) calc(8px + env(safe-area-inset-bottom)) calc(8px + env(safe-area-inset-left));
        /* GATE FIX-1 2026-06-01 · override L1214 base mobile `padding:8px !important` para top only · clear selector-area 42px en focus-active móvil · NO toca el !important compartido de L1214 */
        padding-top: calc(42px + env(safe-area-inset-top)) !important;
        gap: 6px;
    }
    body.iris-focus-active #iris-grid > div[data-peer]:not([data-presenter="true"]) {
        width: 120px;
        min-width: 120px;
        max-width: 120px;
        height: 80px;
    }
}

/* ═══════════════════════════════════════════════════════════════════
   Sprint IRIS-SHARE-V2 A5 (rev de D2 inicial) · body.iris-presenting · plano presentador
   Zoom thumbnail bottom-right · CEO ajuste D-1 (top-right colisiona con avatar).
   Tamaños 240/160/120 desktop/768/480.
   El bloque body.iris-focus-active (L1820-1922) NO se toca · solo aplica a peer.
   A6 añade floating Stop Sharing toolbar mounted aquí.
   ═══════════════════════════════════════════════════════════════════ */
body.iris-presenting #iris-grid {
    /* C1.d · la miniatura clavada la sustituye #iris-float-win, que ya es arrastrable,
       redimensionable y trae MIC/CAM: un solo mecanismo en la plataforma en vez de dos.
       display:none y NO se desmonta: los tiles siguen en el DOM, asi que _findPresenterTile
       y _renderShareStrip los siguen encontrando, y _syncFloatGrid no los usa (se alimenta
       de IrisController.streams). El resto del bloque se conserva para poder volver atras
       quitando una linea. */
    display: none !important;
    /* A5 · Zoom thumbnail bottom-right · NO ocupa viewport · UI navegable preservada */
    position: fixed !important;
    bottom: 20px;
    right: 20px;
    top: auto;
    /* C1.b FIX A · aqui vivia `inset: auto`. inset es shorthand de top/right/bottom/left:
       declarado DESPUES de bottom/right los reseteaba a auto, el fixed se quedaba sin
       offsets y caia en su posicion estatica (arriba-izquierda). La intencion era resetear
       ANTES de poner los offsets; el orden estaba invertido. Se elimina, no se reordena. */
    width: 240px;
    height: 180px;
    z-index: var(--z-iris-grid, 950);
    background: #000;
    border: 1px solid rgba(0, 128, 255, 0.3);
    border-radius: 4px;
    overflow: hidden;
    pointer-events: auto;
    /* Override base #iris-grid display:grid hereda · sin grid-template explicito · single tile fills container */
}
body.iris-presenting #iris-grid > .iris-tile {
    /* C1.b FIX B · el badge .local-presenting::after es position:absolute, pero el tile no
       declaraba `position` en ninguna parte (#iris-grid > .iris-tile, L1215, solo pone
       min-height/min-width/overflow). Sin ancestro posicionado, el ::after se anclaba al
       #iris-grid —que en presenting SI es fixed— y aterrizaba a 8px del fondo de la
       miniatura entera, o sea encima del tile del OTRO participante.
       No era desbordamiento: el tile ya tiene overflow:hidden, medido. Era falta de
       contexto de posicionamiento. Una linea, scopeada a presenting. */
    position: relative;
    width: 100%;
    pointer-events: auto;
}
/* UI principal navegable durante share · defensa explícita pointer-events */
body.iris-presenting .main-view,
body.iris-presenting .sidebar-title,
body.iris-presenting .vault-chip,
body.iris-presenting .hub-files-tab {
    pointer-events: auto;
    opacity: 1;
}
/* A5 mobile breakpoints · CEO ajuste D-1: bottom:80px evita mobile bottom navbar */
@media (max-width: 768px) {
    body.iris-presenting #iris-grid {
        bottom: 80px;
        right: 10px;
        width: 160px;
        height: 120px;
    }
}
@media (max-width: 480px) {
    body.iris-presenting #iris-grid {
        bottom: 80px;
        right: 10px;
        width: 120px;
        height: 90px;
    }
}

/* ═══════════════════════════════════════════════════════════════════
   F-IRIS-SHARE-V2 S3.1 · CSS state-driven · 2026-05-15
   body[data-iris-mode="focus-peer"] · presenter remoto fullscreen +
   thumbnails strip col-2 200px. Renderer iris_share_v2.js setea
   body.dataset.irisMode + tile setAttribute('data-v2-presenter','true').
   Aplica SOLO cuando window._IRIS_V2 === true (default OFF · CEO activa
   manual via DevTools post-S3 rollback). Coexiste con bloque legacy
   body.iris-focus-active (se mantiene para path con flag OFF).
   ═══════════════════════════════════════════════════════════════════ */
body[data-iris-mode="focus-peer"] #iris-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 200px;
    grid-template-rows: minmax(0, 1fr);
    gap: 12px;
    padding: 20px;
    position: fixed;
    inset: 0;
    background: #000;
    z-index: var(--z-iris-grid);
    overflow: hidden;
}
body[data-iris-mode="focus-peer"] #iris-grid > [data-peer] {
    position: relative;
    background: #000;
    border: 1px solid rgba(63, 185, 80, 0.35);
    border-radius: 3px;
    overflow: hidden;
    min-width: 0;
    min-height: 0;
}
body[data-iris-mode="focus-peer"] #iris-grid > [data-peer][data-v2-presenter="true"] {
    grid-column: 1;
    grid-row: 1;
    width: 100%;
    height: 100%;
}
body[data-iris-mode="focus-peer"] #iris-grid > [data-peer]:not([data-v2-presenter="true"]) {
    grid-column: 2;
    width: 200px;
    height: 133px;
    align-self: start;
}
body[data-iris-mode="focus-peer"] #iris-grid > [data-peer] video {
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: #000;
}
body[data-iris-mode="focus-peer"] #iris-grid > [data-peer]:not([data-v2-presenter="true"]) video {
    object-fit: cover;
}

/* Sprint IRIS-SHARE-V2 A6 2026-05-14 · floating Stop Sharing toolbar
   bottom-right (a la izquierda del iris-grid thumbnail · z:1010 arriba modal).
   Draggable via JS (call_controller.js _makeStopShareDraggable). */
.iris-stop-share-toolbar {
    position: fixed;
    /* C1.d · antes: `right: 280px`, calculado como "240 (ancho del grid) + 20 + 20". Ese 240
       era el ancho de la miniatura, que ya no existe: la videollamada vive en #iris-float-win,
       que ademas es redimensionable, asi que cualquier anclaje a su ancho seria falso. Se
       centra y se olvida de ella: menos acoplamiento.
       CENTRADO SIN `transform` a proposito. _makeStopShareDraggable (call_controller.js:1414)
       lee getBoundingClientRect —que ya incluye el transform— y luego fija `left`, asi que un
       translateX(-50%) residual daria un salto en el primer clic. left/right 0 + margen auto
       centra igual y el arrastre lo sobreescribe limpiamente.
       H2 · el `bottom` ya NO sale de una constante. --iris-controls-h se midio a 1440px y en
       768/480 los controles miden otra cosa: la barra solapaba 37 y 35 px. Un numero medido en
       un punto y usado en todos los anchos es el fallo, y ponerle nombre solo lo hacia
       localizable. Ahora lo deriva _posicionarStopShareToolbar leyendo el alto REAL de
       #iris-controls al montar y en cada resize. Este valor es solo el arranque, para el
       instante entre que se pinta y el JS mide. */
    bottom: 20px;
    left: 0;
    right: 0;
    margin-inline: auto;
    width: fit-content;
    z-index: 1010;          /* above iris-grid (950) y modal (1000) */
    display: flex;
    align-items: center;
    gap: 12px;
    background: rgba(0, 128, 255, 0.10);
    border: 1px solid rgba(0, 128, 255, 0.40);
    color: #d4d8e0;
    font-family: 'Orbitron', monospace;
    font-size: 0.55rem;
    letter-spacing: 2px;
    padding: 8px 14px;
    border-radius: 4px;
    user-select: none;
    backdrop-filter: blur(6px);   /* solo en este overlay · NO en grid · seguro */
    -webkit-backdrop-filter: blur(6px);
}
.iris-stop-share-grip {
    cursor: move;
    color: rgba(0, 128, 255, 0.6);
    font-size: 0.7rem;
    padding: 0 4px;
}
.iris-stop-share-label {
    color: rgba(63, 185, 80, 0.95);
}
.iris-stop-share-btn {
    background: rgba(255, 68, 102, 0.15);
    border: 1px solid rgba(255, 68, 102, 0.5);
    color: #ff4466;
    font-family: 'Orbitron', monospace;
    font-size: 0.55rem;
    letter-spacing: 2px;
    padding: 5px 12px;
    border-radius: 2px;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
}
.iris-stop-share-btn:hover {
    background: rgba(255, 68, 102, 0.25);
    border-color: rgba(255, 68, 102, 0.8);
}
@media (max-width: 768px) {
    .iris-stop-share-toolbar {
        /* C1.d · el `right` anclado al ancho de la miniatura se retira aqui tambien;
           hereda el centrado de la regla base. El bottom movil ya despeja su barra. */
        bottom: 80px;
        padding: 6px 10px;
        font-size: 0.40rem;
        gap: 8px;
    }
}
@media (max-width: 480px) {
    .iris-stop-share-toolbar {
        /* C1.d · el `right` anclado al ancho de la miniatura se retira aqui tambien;
           hereda el centrado de la regla base. El bottom movil ya despeja su barra. */
        bottom: 80px;
        padding: 5px 8px;
        font-size: 0.38rem;
        gap: 6px;
    }
}

/* ═══════════════════════════════════════════════════════════════════
   Sprint 3.1 step 2 B 2026-05-13 · IRIS TOAST · MARSHA austerity
   ═══════════════════════════════════════════════════════════════════ */
:root { --z-iris-toast: 990; }   /* below modals (1000+), above iris-grid (950) */
.iris-toast-stack {
    /* Step 2c 2026-05-13 · header strip position · CEO clarification
       Toast lives inside the 64px header strip (top:12px aligns vertical-center
       with avatar 40×40 at right:32px) · right:100px clears avatar column.
       Panel-R (when visible) is below header zone · 100px const safe. */
    position: fixed;
    top: 12px;
    right: 100px;
    z-index: var(--z-iris-toast);
    display: flex;
    flex-direction: column-reverse;
    gap: 6px;
    pointer-events: none;
    max-width: calc(100vw - 40px);
}
.iris-toast {
    width: 280px;
    max-width: calc(100vw - 20px);                                 /* Step 2d · cap mobile · keep desktop 280 */
    height: 40px;                                                  /* Step 2c · fit header strip */
    background: #080c10;
    border-top: 1px solid var(--iris-toast-color, #4DA6FF);        /* Step 2c · thinner top */
    border-left: 3px solid var(--iris-toast-color, #4DA6FF);
    padding: 8px 14px;                                             /* Step 2c · compact vertical */
    display: flex;                                                  /* Step 2c · single-row inline */
    align-items: center;
    gap: 10px;
    pointer-events: auto;
    cursor: pointer;
    font-family: 'Share Tech Mono', monospace;
    box-shadow: 0 2px 8px rgba(0,0,0,0.5);
    /* Step 2d 2026-05-13 · anchor-right reveal · width 0→280 · NOT translateX(110%→0)
       Toast nace en el margen derecho · crece hacia adentro · siempre conectado al borde.
       overflow:hidden clips inner content during reveal · text ellipsis already handles
       reflow gracefully (white-space:nowrap on .iris-toast-name). */
    overflow: hidden;
    transform-origin: right center;
    animation: marshaToastSlideIn 0.35s cubic-bezier(0.16,1,0.3,1);
}
.iris-toast[data-type="joined"]       { --iris-toast-color: #4DA6FF; }
.iris-toast[data-type="left"]         { --iris-toast-color: #6b7280; }
.iris-toast[data-type="admitted"]     { --iris-toast-color: #3FB950; }
.iris-toast[data-type="disconnected"] { --iris-toast-color: #e5534b; }
.iris-toast[data-type="rejected"]     { --iris-toast-color: #e5534b; }
.iris-toast .iris-toast-label {
    /* Step 2c · inline single-row · smaller for compact */
    font-family: Orbitron, monospace;
    font-size: 0.55rem;
    letter-spacing: 2px;
    color: var(--iris-toast-color);
    margin: 0;
    flex-shrink: 0;
}
.iris-toast .iris-toast-row {
    flex: 1;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
    overflow: hidden;
}
.iris-toast .iris-toast-name {
    /* Step 2c · slightly smaller for single-line · was 0.65rem */
    font-size: 0.58rem;
    color: rgba(230,235,245,0.82);
    letter-spacing: 0.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.iris-toast .iris-toast-time {
    /* Step 2c · compact time · was 0.38rem */
    font-size: 0.50rem;
    color: #3a3d4a;
    flex-shrink: 0;
}
@keyframes marshaRiseUp {
    from { transform: translateY(20px); opacity: 0; }
    to   { transform: translateY(0); opacity: 1; }
}
@keyframes marshaToastSlideIn {
    /* Step 2d 2026-05-13 · anchor-right reveal · width 0 → 280px */
    from { width: 0;     opacity: 0; }
    to   { width: 280px; opacity: 1; }
}
@keyframes marshaToastSlideOut {
    /* Step 2d · reverse · auto-dismiss collapses back to margin */
    from { width: 280px; opacity: 1; }
    to   { width: 0;     opacity: 0; }
}
@media (max-width: 768px) {
    /* Step 2d · mobile keeps anchor-right reveal · max-width cap on .iris-toast handles narrow viewport */
    .iris-toast-stack { top: 60px; right: 10px; gap: 8px; }
    .iris-toast { height: auto; padding: 10px 14px; }
}
@media (prefers-reduced-motion: reduce) {
    .iris-toast { animation: none; }
}

/* Annotation canvas */
.iris-annotation-canvas {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 10;
}
.iris-annotation-canvas.iris-drawing-active {
    pointer-events: auto;
    cursor: crosshair;
}

/* Tool palette */
.iris-anno-palette {
    position: absolute;
    top: calc(12px + env(safe-area-inset-top));
    right: calc(12px + env(safe-area-inset-right));
    display: none;
    gap: 6px;
    padding: 6px;
    background: rgba(0, 0, 0, 0.72);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 3px;
    z-index: var(--z-iris-palette);
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity var(--iris-transition), transform var(--iris-transition);
}
body.iris-focus-active .iris-anno-palette.iris-anno-visible {
    display: inline-flex;
    opacity: 1;
    transform: translateY(0);
}
.iris-anno-btn {
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: rgba(255, 255, 255, 0.7);
    font-family: Orbitron, monospace;
    font-size: 0.50rem;
    letter-spacing: 2px;
    padding: 5px 9px;
    border-radius: 2px;
    cursor: pointer;
    transition: color 180ms ease, border-color 180ms ease, background 180ms ease;
}
.iris-anno-btn:hover {
    color: #fff;
    border-color: rgba(63, 185, 80, 0.6);
}
.iris-anno-btn.iris-anno-active {
    background: #3FB950;
    color: #fff;
    border-color: #3FB950;
}

/* Privacy modal */
#iris-privacy-modal,
#iris-presenter-request-modal {
    position: fixed;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.88);
    z-index: var(--z-iris-modal);
    padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
    opacity: 0;
    transition: opacity var(--iris-transition);
}
@supports (backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px)) {
    #iris-privacy-modal,
    #iris-presenter-request-modal {
        background: rgba(0, 0, 0, 0.78);
        backdrop-filter: blur(8px);                       /* techo E2 · era 10 */
        -webkit-backdrop-filter: blur(8px);
    }
}
#iris-privacy-modal.iris-modal-open,
#iris-presenter-request-modal.iris-modal-open {
    display: flex;
    opacity: 1;
}

#iris-privacy-modal > .iris-modal-box,
#iris-presenter-request-modal > .iris-modal-box {
    background: #0b0f14;
    border: 1px solid rgba(63, 185, 80, 0.28);
    padding: 28px 32px;
    min-width: min(380px, 92vw);
    max-width: 92vw;
    border-radius: 3px;
    color: #e6ecf3;
    font-family: Orbitron, monospace;
    transform: translateY(8px);
    transition: transform var(--iris-transition);
}
#iris-privacy-modal.iris-modal-open > .iris-modal-box,
#iris-presenter-request-modal.iris-modal-open > .iris-modal-box {
    transform: translateY(0);
}
#iris-presenter-request-modal > .iris-modal-box {
    border-color: rgba(0, 128, 255, 0.35);
}

#iris-privacy-modal h3,
#iris-presenter-request-modal h3 {
    margin: 0 0 16px 0;
    font-size: 0.58rem;
    letter-spacing: 4px;
    color: #fff;
    text-transform: uppercase;
}
#iris-privacy-modal .iris-privacy-list {
    list-style: none;
    margin: 0 0 22px 0;
    padding: 0;
    font-size: 0.55rem;
    letter-spacing: 1.5px;
    line-height: 2.2;
}
#iris-privacy-modal .iris-privacy-list label {
    display: flex;
    align-items: center;
    gap: 10px;
    cursor: pointer;
    color: rgba(255, 255, 255, 0.72);
}
#iris-privacy-modal .iris-privacy-list input[type="checkbox"] {
    accent-color: #3FB950;
    width: 14px;
    height: 14px;
}
.iris-modal-actions {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
    align-items: center;
}
.iris-modal-btn {
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: rgba(255, 255, 255, 0.75);
    font-family: Orbitron, monospace;
    font-size: 0.55rem;
    letter-spacing: 3px;
    padding: 9px 18px;
    border-radius: 2px;
    cursor: pointer;
    transition: background 180ms ease, border-color 180ms ease, color 180ms ease;
}
.iris-modal-btn.iris-modal-primary {
    background: #3FB950;
    color: #fff;
    border-color: #3FB950;
}
.iris-modal-btn.iris-modal-primary:disabled {
    background: #2a2f36;
    border-color: #2a2f36;
    color: rgba(255, 255, 255, 0.28);
    cursor: not-allowed;
}
.iris-modal-countdown {
    font-size: 0.50rem;
    color: rgba(255, 255, 255, 0.42);
    margin-right: auto;
    letter-spacing: 2px;
}

/* ══════════════════════════════════════════════════════════════
   HUB MSG ACTION BUTTONS · v13 · Palantir Tier 1 · WCAG AAA
   Hit area 28px · touch-friendly · keyboard accessible
   ══════════════════════════════════════════════════════════════ */
.hub-msg-wrap { position: relative; }
/* Sprint #49 · HUB-MSG-WIDTH-EXPAND · actions wrapper position:absolute
   Libera flex space del row · bubble usa max-width 90% sin contention
   Pattern Slack/Discord: floating overlay top-right · backdrop blur reveal */
.msg-actions-group {
    position: absolute;
    top: -12px;
    right: 8px;
    display: flex;
    gap: 2px;
    padding: 2px 4px;
    background: rgba(8,12,20,0.85);
    border: 1px solid rgba(0,128,255,0.2);
    border-radius: 4px;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    z-index: 5;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
    /* SYNARIS VIVO · lo que flota lo dice la SOMBRA, no el desenfoque: una sombra declara
       "esto esta por encima" sin tocar lo que hay debajo y cuesta un solo pintado. Mismo
       valor de siempre, ahora con nombre. */
    box-shadow: var(--sombra-1);
}
/* Reveal on wrap hover (desktop) o tap-toggle (mobile · .touch-visible JS class) */
.hub-msg-wrap:hover .msg-actions-group {
    opacity: 1;
    pointer-events: auto;
}
.hub-msg-wrap.touch-visible .msg-actions-group {
    opacity: 1;
    pointer-events: auto;
}
/* Empty container (mensaje retractado o sin id) · hide totalmente */
.msg-actions-group:empty {
    display: none;
}

/* Sprint #48/#49 · individual buttons · siempre visibles dentro container
   Container handle opacity reveal · buttons mantienen color/size/cursor */
.msg-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 28px;
    min-height: 28px;
    padding: 4px 6px;
    font-size: 0.7rem;
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
    user-select: none;
    border-radius: 2px;
    background: transparent;
    border: none;
}
.msg-action:hover { background: rgba(255,255,255,0.06); }
.msg-action:focus-visible {
    outline: 1px solid rgba(0,128,255,0.5);
    outline-offset: 1px;
}
.msg-action--retract { color: #ff4466; }
.msg-action--reply   { color: #4DA6FF; }
.msg-action--signal  { color: rgba(0,128,255,0.7); }
.msg-action--edit    { color: #6b7280; }
.msg-action--edit:hover { color: #4DA6FF; }
@media (hover: none) {
    /* Mobile · disable hover-reveal completo (anti stuck-on phantom hover)
       Solo .touch-visible class JS-toggled da visibilidad container */
    .hub-msg-wrap:hover .msg-actions-group {
        opacity: 0;
        pointer-events: none;
    }
    .hub-msg-wrap.touch-visible .msg-actions-group {
        opacity: 1;
        pointer-events: auto;
    }
}

/* ══════════════════════════════════════════════════════════════
   MODULE PANELS · v13 · Flex containment fix
   chat-wall · teams-wall · nexo-wall · vault-wall · mail-wall · iris-wall
   Patrón estándar flex column con scroll interno
   ══════════════════════════════════════════════════════════════ */
#chat-wall,
#teams-wall,
#nexo-wall,
#vault-wall,
#mail-wall,
#iris-wall {
    flex: 1 1 0;
    min-height: 0;
}

/* Sprint 0.B redo · IRIS layout footer alignment (Opción B · audit 2026-05-09)
   Right-panel uses padding:0 16px 20px → footer-link.bottom ≈ viewport-26px.
   Main-view chain has zero padding → cal-scroll.bottom = viewport (Δ 26px gap).
   This rule mirrors the 20px padding-bottom for IRIS-only · zero impact other modules.
   Doc: docs/AUDIT_IRIS_LAYOUT_2026-05-09.md */
#iris-wall {
    padding-bottom: 20px;
    box-sizing: border-box;
}

/* ══════════════════════════════════════════════════════════════
   IRIS LAYOUT · v13 · Tier 1 mobile-safe
   iris-active flex containment for proper header/grid/ticker/controls
   stacking without overflow on mobile viewports
   ══════════════════════════════════════════════════════════════ */
#iris-active {
    overflow: hidden;
}
#iris-grid {
    min-height: 0;
}
/* Seleccionar por POSICION algo que tiene id es fragil por definicion: `div:last-child`
   apuntaba a #iris-controls solo mientras fuera el ultimo hijo. La cabecera si va por
   posicion porque no tiene id. Los controles, por el suyo, y ademas SIN atarlos a
   #iris-active: asi la regla les sigue si algun dia cuelgan de otro sitio. */
#iris-active > div:first-child,
#iris-controls {
    flex-shrink: 0;
}

/* F16 · hist-list scrollbar invisible */
#hist-list::-webkit-scrollbar { width: 0; display: none; }
#hist-list { -ms-overflow-style: none; }

/* F16.2 · Right panel · footer fijo abajo + auto-scroll invisible */
.right-panel {
    overflow: hidden;            /* UI-1 wrapper · el scroll lo hace #panel-R-scroll, no el panel */
    scrollbar-width: none;
}
.right-panel::-webkit-scrollbar { width: 0; display: none; }
/* UI-1 wrapper (2026-06-02) · contenedor de scroll ÚNICO de la columna · ítem flex:1 de #panel-R
   (toma la altura restante tras NOTES+footer) y BLOQUE internamente → los paneles fluyen a alto
   natural y desbordan AQUÍ, no en cajas sueltas. Reemplaza el hack flex:0 0 auto !important +
   footer sticky. NOTES y footer son hermanos de este div → pin natural abajo, fuera del scroll. */
.panel-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    scrollbar-width: none;
}
.panel-scroll::-webkit-scrollbar { width: 0; display: none; }

/* UI-1 wrapper (2026-06-02) · GATE 7 + scroll-único-por-override SUPERSEDED.
   El scroll de columna lo da #panel-R-scroll (.panel-scroll, arriba). Los paneles internos fluyen
   a alto natural — sus flex:1 inline quedan INERTES porque el wrapper es bloque, no flex.
   DEPT/CANALES sin max-height interno (decisión CEO · scroll de columna puro + acordeón).
   Footer + NOTES viven FUERA del wrapper → pin natural abajo, sin sticky ni !important. */
#hub-channels-list { overflow-y: auto; scrollbar-width: none; }
#hub-channels-list::-webkit-scrollbar { width: 0; display: none; }
/* C3 · ARCHIVOS · ÚNICA caja con scroll propio (máx ~3-4 ítems) dentro del scroll de columna.
   ítem ≈ 38px + gap 6px → 150px ≈ 3-4. Sin !important: ya no hay guerra de overrides. */
#hub-files-list {
    max-height: 150px;
    overflow-y: auto;
}
#hub-files-list::-webkit-scrollbar { width: 0; display: none; }
/* Colapso acordeón · headers ocultan sus items (no la cabecera) · chevron rota (regla abajo).
   UI-1 ronda 3 · ARCHIVOS (#hub-files-panel) también colapsa → oculta sus tabs + lista. */
#hub-dept-panel.collapsed [data-dept] { display: none !important; }
#hub-files-panel.collapsed #hub-files-tabs,
#hub-files-panel.collapsed #hub-files-list { display: none !important; }
/* GATE 9 · !important para ganar al display:flex INLINE de #hub-channels-list
   (sin él, el inline gana y el colapso de CANALES no oculta la lista · chevron rotaba pero no colapsaba) */
#hub-channels-panel.collapsed #hub-channels-list { display: none !important; }
/* GATE 9/10 · active state canal · scoped a #hub-channels-list (NO afecta dept items, que usan estilo inline).
   GATE 10 · !important en border-left-color + color: el item tiene 'border-left:2px solid transparent'
   y 'color:...' INLINE → sin !important el inline gana y solo se veía el fondo tenue (la barra y el texto
   cyan no aplicaban). background NO está inline → aplicaba ya. Mismo patrón que el fix de colapso. */
#hub-channels-list .hub-dept-item.active {
    border-left-color: #00B4D8 !important;
    background: rgba(0, 180, 216, 0.06);
    color: #00B4D8 !important;
}
.hub-section-header { user-select: none; }
.hub-section-chevron { transition: transform 0.2s ease; font-size: 0.6rem; display: inline-block; }
.collapsed > .hub-section-header .hub-section-chevron { transform: rotate(-90deg); }
/* GATE 10.3 · headers de sección legibles · azul nav #0080FF (token SYNARIS §09).
   SCOPED por panel-id a los headers HUB (DEPARTAMENTOS/CANALES/ARCHIVOS).
   UI-1 (2026-06-02) · #iris-panel (SESIONES IRIS) añadido al MISMO grupo para paridad de
   color con sus hermanos (antes heredaba el gris base de .sidebar-title) · reutiliza la única
   declaración #0080FF, sin hex nuevo. Sigue SIN tocar NEXO/MAIL/DM que comparten .sidebar-title.
   Especificidad #id > .sidebar-title → gana sin !important.
   Solo el header (> .sidebar-title direct child) · los items (GENERAL/#canal) quedan gris/cyan. */
/* COSMETIC-4b 2026-08-03 · #nexo-panel entra al grupo. La nota de arriba decia "SIGUE SIN TOCAR
   NEXO/MAIL/DM", y por eso el encabezado del tunel se quedaba en el gris base de .sidebar-title
   (rgba(255,255,255,.18) = 1.68:1) mientras su hermano SESIONES IRIS iba a #0080FF (5.09:1).
   Misma clase, mismo margen, pero NO el mismo color: la divergencia estaba aqui, no en el markup.
   MAIL/DM se quedan fuera a proposito — no estan en el alcance sellado. */
#hub-dept-panel > .sidebar-title,
#hub-channels-panel > .sidebar-title,
#hub-files-panel > .sidebar-title,
#nexo-panel > .sidebar-title,
#iris-panel > .sidebar-title {
    color: #0080FF;
}

/* Auto-scroll invisible global · todos los containers scrolleables */
.hub-chat-wall,
.email-inbox,
.left-panel,
#mail-thread-messages,
#mail-list-view,
#hub-chat-wall,
#vault-file-list,
#hub-files-list,
#email-inbox-container {
    scrollbar-width: none;
}

@keyframes vaultFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.hub-chat-wall::-webkit-scrollbar,
.email-inbox::-webkit-scrollbar,
.left-panel::-webkit-scrollbar,
#mail-thread-messages::-webkit-scrollbar,
#mail-list-view::-webkit-scrollbar,
#hub-chat-wall::-webkit-scrollbar,
#vault-file-list::-webkit-scrollbar,
#hub-files-list::-webkit-scrollbar,
#email-inbox-container::-webkit-scrollbar,
#iris-right-panel::-webkit-scrollbar {
    width: 0;
    display: none;
}

/* F16.3 · chat-wall MIA · scroll invisible */
#chat-wall {
    scrollbar-width: none;
    -ms-overflow-style: none;
}
#chat-wall::-webkit-scrollbar { width: 0; display: none; }


/* F16.7 · Panel-R visibilidad por módulo · solo desktop */
@media (min-width: 769px) {
    body.ia-active #panel-R,
    body.mail-active #panel-R,
    body.vault-active #panel-R {
        display: none !important;
    }
    body.p2p-active #panel-R,
    body.iris-active #panel-R,
    body.nexo-active #panel-R {
        display: flex !important;
    }
}

/* F16.10 · Admin fixed top-right viewport · panel-R debajo header */
@media (min-width: 769px) {
    body.p2p-active .header-controls,
    body.iris-active .header-controls,
    body.nexo-active .header-controls {
        position: fixed;
        top: 0;
        right: 32px;
        height: var(--header-h);
        display: flex;
        align-items: center;
        z-index: 100;
    }
    body.p2p-active #panel-R,
    body.iris-active #panel-R,
    body.nexo-active #panel-R {
        margin-top: var(--header-h);
    }
    body.p2p-active main.main-view > header,
    body.iris-active main.main-view > header,
    body.nexo-active main.main-view > header {
        margin-right: calc(-1 * var(--panel-w));
    }
}

/* F16.11 · MI POST mobile · ELIMINADO 2026-06-14 (consolida drift @media).
 * Bloque muerto: apuntaba a #mail-folders / #mail-list-col / #mail-detail-col,
 * ids inexistentes en el markup actual (los reales son #mpx-folders /
 * #mpx-list-pane / #mpx-viewer-pane). Su única regla viva (#mail-wall
 * flex-direction:column) era redundante con el bloque mobile activo en L1152-1161.
 * El stack-vertical mobile real vive en @media(max-width:768px) L1149-1182. */

/* F16.13 · Enterprise modal entrance · slide+fade Apple-style */
@keyframes adminPanelSlideIn {
    0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.96) translateY(8px); }
    100% { opacity: 1; transform: translate(-50%, -50%) scale(1) translateY(0); }
}
@keyframes ledgerBoxSlideIn {
    0%   { opacity: 0; transform: scale(0.96) translateY(12px); }
    100% { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes overlayFadeIn {
    0%   { opacity: 0; }
    100% { opacity: 1; }
}
#admin-panel {
    animation: adminPanelSlideIn 0.35s cubic-bezier(0.32, 0.72, 0, 1) both;
}
#ledger-modal > div {
    animation: ledgerBoxSlideIn 0.35s cubic-bezier(0.32, 0.72, 0, 1) both;
}
#admin-panel-overlay,
#ledger-modal {
    animation: overlayFadeIn 0.28s ease-out both;
}

/* F16.13b · settings-dropdown centered + animated */
#settings-dropdown {
    animation: adminPanelSlideIn 0.35s cubic-bezier(0.32, 0.72, 0, 1) both;
}
#settings-overlay {
    animation: overlayFadeIn 0.28s ease-out both;
}

@keyframes vaultThumbShimmer { 0%{background-position:200% 0} 100%{background-position:-200% 0} }

/* P1-13 · Skeleton loaders · shimmer animation reusable */
@keyframes skeletonShimmer {
    0%   { background-position: -200% 0; }
    100% { background-position:  200% 0; }
}
.skeleton {
    background: linear-gradient(
        90deg,
        rgba(255,255,255,0.02) 0%,
        rgba(0,128,255,0.06) 50%,
        rgba(255,255,255,0.02) 100%
    );
    background-size: 200% 100%;
    animation: skeletonShimmer 1.4s linear infinite;
    border-radius: 2px;
    color: transparent !important;
    pointer-events: none;
}
.skeleton-row {
    height: 14px;
    margin: 8px 14px;
}
.skeleton-row + .skeleton-row { margin-top: 4px; }
.skeleton-card {
    height: 48px;
    margin: 4px 12px;
    border-radius: 2px;
}
@media (prefers-reduced-motion: reduce) {
    .skeleton { animation: none; opacity: 0.4; }
}

/* F21 · MIA disclaimer · solo visible en modo IA */
.mia-disclaimer { display: none; }
body.ia-active .mia-disclaimer { display: block; }

/* ─────────────────────────────────────────────────────────────────
   P0v2 · Per-input font-size policy mobile · supersedes original P0-Bug C
   global 16px rule (que rompía visual hierarchy de 16+ elementos).

   Decision matrix Opción B (audit v2):
   - Typing-heavy inputs (textarea + password + compose body + MIA input
     + IRIS ticker): 16px !important · preserves iOS anti-zoom (usuario
     tipea texto largo · zoom interrumpe flow)
   - Search/select/short inputs (input[type=search/text/email]+select):
     0.85rem !important · visual hierarchy match · iOS auto-zoom on focus
     acceptable (low-frequency taps · usuario rezooms manual)

   Affected elements:
   - 16px PRESERVED: textarea (any), input[type=password], #mpx-compose-body,
     #user-input, #iris-ticker-input
   - 0.85rem APPLIED: #mpx-search, #mpx-tenant-filter, #mpx-compose-tenant/
     from/to-input/cc/subject, #peer-in NEXO, #nexo-msg-input, #admin-name/
     dept, all input[type=text/search/email] + all select
   ───────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
    /* Typing-heavy: anti iOS auto-zoom 16px */
    textarea,
    input[type="password"] {
        font-size: 16px !important;
    }
    /* Specific typing-heavy IDs (long text body composers) */
    #mpx-compose-body,
    #user-input,
    #iris-ticker-input {
        font-size: 16px !important;
    }
    /* Search/select/short inputs: visual hierarchy 0.85rem */
    input[type="search"],
    input[type="text"],
    input[type="email"],
    input:not([type]),
    select {
        font-size: 0.85rem !important;
    }
}

/* ─────────────────────────────────────────────────────────────────
   P0 · Mobile touch targets ≥44px (Apple HIG / Material Design 48dp)
   8 targets confirmados sub-44 en audit MOBILE_AUDIT_2026-05-08.md.
   Última en cascade para vencer inline styles de index.html.
   ───────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
    /* VAULT · upload + view-toggle (28×28 inline → 44×44) */
    #btn-vault-upload {
        width: 44px !important;
        height: 44px !important;
    }
    .vault-view-btn {
        width: 44px !important;
        height: 44px !important;
    }

    /* IRIS · ticker mode buttons (padding 4/10 → 12/14 · ~44H) */
    .iris-ticker-mode-btn {
        padding: 12px 14px !important;
        min-height: 44px !important;
    }

    /* IRIS · control buttons MIC/CAM/SCREEN */
    .iris-ctrl-btn {
        min-height: 44px !important;
        min-width: 44px !important;
        padding: 10px 14px !important;
    }

    /* IRIS · create room button (padding 10/32 → 14/32 · 44H+) */
    #btn-iris-create {
        padding: 14px 32px !important;
        min-height: 44px !important;
    }

    /* NEXO · close tunnel button (padding 6/14 inline → 14/14 · 44H+) */
    #btn-nexo-close {
        padding: 14px 14px !important;
        min-height: 44px !important;
    }
}

/* P0-mobile-F4 · #vault-search visually smaller mobile (override 16px global rule).
   Trade-off: iOS Safari auto-zooms on focus para inputs <16px font-size. Aceptado
   por CEO con conocimiento del trade-off · vault-search es input secundario · UX
   visual harmony prioritized sobre anti-zoom. mpx-search NO afectado · mantiene 16px. */
@media (max-width: 768px) {
    #vault-search { font-size: 0.85rem !important; }
}

/* ─────────────────────────────────────────────────────────────────
   P0-mobile-F1v2 · header + avatar rules ≤1024 (tablet incluido)
   Cliff anterior 768→767 abrupto · iPad portrait (820w) y landscape (1024w)
   recibían desktop layout. Bumped a 1024 cubre tablet completo. Last in
   cascade · gana sobre rules @ 768 mismas selectores.
   ───────────────────────────────────────────────────────────────── */
@media (max-width: 1024px) {
    header {
        justify-content: space-between !important;
        padding: 8px 16px 12px !important;
        gap: 12px !important;
        box-sizing: border-box !important;
    }
    .header-controls { gap: 12px !important; }
    #btn-profile {
        width: 44px !important;
        height: 44px !important;
        margin-top: 4px !important;
    }
}

/* MIPOST-MOBILE-FIX-3 Bug 1 · FAB compose ELIMINADO · era redundante con
   "+ COMPOSE" button L419 inline en mpx-folders, que ES visible en stack
   mobile (mpx-folders L1036-1041: flex:0 0 auto + max-height:35vh + border-right:none).
   FAB causaba collision con pagination NEXT (L464) en bottom-right viewport.
   Folders button es vía única coherente desktop + mobile. */

/* ─────────────────────────────────────────────────────────────────
   P0-mobile-NEXO · vertical centering content viewport
   #nexo-handshake usa height:100% inline · pero parent #nexo-wall mobile
   tiene padding-bottom:72px (clearance bottom-nav) que hace child height
   compute incorrect cuando height % vs flex. Override a flex:1 + min-height:0
   garantiza child fill available space · align/justify center funciona real.
   ───────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
    #nexo-handshake,
    #nexo-tunnel {
        flex: 1 !important;
        min-height: 0 !important;
        height: auto !important;
    }
    /* CEO panel · content centered · breathing room horizontal */
    #nexo-ceo-panel {
        margin: auto 0 !important;
    }
}

/* ─────────────────────────────────────────────────────────────────
   ISSUE-10 fix · 2026-05-10 · revert KILL MAIL INTERNO oversight
   - CSS block KILL_MAIL_INTERNO removed · F22-PRE-USR-03 revivió backend pero
     dejó esta regla CSS huérfana · resultado: MANU sin folders visibles 2 días
   - Backend ACL granular Foundry pattern preserved · 8 endpoints internal funcionales
   - INTERNAL section ahora visible per HTML default display:flex
   - Decisión arquitectural ELIMINAR INTERNAL diferida sprint dedicado (10-15h)
   ───────────────────────────────────────────────────────────────── */

/* ─────────────────────────────────────────────────────────────────
   P0v2-followup · 6 fixes · ID-specific defensive (last cascade winners)
   1 · Avatar margin-top 4 → 8 (visible vs subtle)
   2 · #mpx-tenant-filter SELECT explicit ID rule (defeat inline 0.5rem edge)
   3 · FAB icon "+" → ✉ (modificado en P0-mobile-FAB block above)
   4 · #mpx-viewer-empty margin:auto + min-height defensive centering
   5 · footer chips gap 8px uniforme (era 6 14)
   6 · #peer-in NEXO ID rule defeat inline edge case
   ───────────────────────────────────────────────────────────────── */
@media (max-width: 1024px) {
    /* Bug 1 fix · header height 52→64 · interior 44px (= avatar 44) zero overflow.
       Math: header 64 - padding-top 8 - padding-bottom 12 = 44 interior · avatar 44
       caps exact. Margin-top reset 0 · centering clean align-items:center default. */
    header {
        height: 64px !important;
    }
    #btn-profile {
        margin-top: 0 !important;
    }
}

/* Bug 2 · MIA avatar/bubble escape container · cap msg max-width + flex-shrink avatar
   .msg flex-row con avatar 32 + bubble · sin max-width caped en mobile · puede overflow.
   .row-user/.row-mia padding-left 16 reduce a 12 mobile · más breathing room. */
@media (max-width: 768px) {
    .msg {
        max-width: 96vw !important;
        overflow: hidden;
    }
    .row-user,
    .row-mia {
        padding-left: 12px !important;
    }
    .msg > *:first-child {
        flex-shrink: 0;
    }
}

/* Bug 3 · MIPOST viewer fullscreen overlay mobile when message selected.
   Mirror exactly F3 vault preview pattern (commit f7d357f). loadMpxMessage adds
   class .mpx-mobile-overlay-active · pane se vuelve fixed inset:0 z 9999 ·
   header back button visible · ESC + back close · zero chrome cropping. */
@media (max-width: 768px) {
    #mpx-viewer-pane.mpx-mobile-overlay-active {
        display: flex !important;                 /* MIPOST-MOBILE-GMAIL-PATTERN · reveal override de display:none default */
        position: fixed !important;
        inset: 0 !important;
        z-index: 9999 !important;
        background: rgba(8, 12, 20, 0.98) !important;
        backdrop-filter: blur(8px) !important;
        -webkit-backdrop-filter: blur(8px) !important;
        flex: 1 1 auto !important;
        max-height: 100dvh !important;
        border-top: none !important;
    }
    #mpx-viewer-pane.mpx-mobile-overlay-active #mpx-viewer-mobile-header {
        display: flex !important;
    }
    #mpx-viewer-pane.mpx-mobile-overlay-active #mpx-viewer-empty {
        display: none !important;
    }
    #mpx-viewer-pane.mpx-mobile-overlay-active #mpx-viewer-pane-content {
        flex: 1 !important;
    }
    #mpx-viewer-pane.mpx-mobile-overlay-active #mpx-viewer-actions {
        padding-bottom: max(8px, env(safe-area-inset-bottom)) !important;
    }
}
@media (max-width: 768px) {
    /* Bug 5 · #mpx-tenant-filter SELECT · -webkit-appearance:none + height/padding +
       custom chevron arrow · controla closed-state text rendering en Safari iOS.
       Native picker abre on tap (OS font interno · uncontrolable but acceptable
       iOS pattern) · pero CLOSED text size ahora respeta CSS rules. */
    #mpx-tenant-filter {
        -webkit-appearance: none !important;
        -moz-appearance: none !important;
        appearance: none !important;
        font-size: 0.85rem !important;
        line-height: 1.2 !important;
        height: 36px !important;
        padding: 6px 28px 6px 10px !important;
        background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M3 5L6 8L9 5' stroke='%234DA6FF' stroke-width='1.5' fill='none'/></svg>") !important;
        background-repeat: no-repeat !important;
        background-position: right 8px center !important;
    }
    /* MIPOST-MOBILE-FIX-3 Bug 2 · viewer-empty centering fix · root cause: prev
       "Bug 7" fix usaba flex:0 0 auto en viewer-empty que COLAPSABA pane a
       ~15px (intrinsic text height) · text quedaba al borde superior sin
       espacio para centrar verticalmente. Solución: viewer-empty flex:1 +
       min-height:180px da pane altura · inline display:flex + align-items:center
       + justify-content:center (L479) hace el centering vertical+horizontal. */
    #mpx-viewer-empty {
        margin: 0 !important;
        flex: 1 !important;
        min-height: 180px !important;
        align-self: stretch !important;
    }
    /* ITEM-VAULT-UI 2026-06-15 · grid 3-col rígido ELIMINADO (anulaba el flex-wrap de :207-211 →
       desalineaba/desbordaba <375). El footer técnico usa flex-wrap natural + compresión progresiva
       de font/gap en ≤480 / ≤320 (más abajo). Los 5 badges de seguridad se preservan. */
    /* Fix 6 · NEXO #peer-in ID rule · defeat inline 0.5rem edge */
    #peer-in {
        font-size: 0.85rem !important;
    }

    /* Bug 6 · ::placeholder NO inherits font-size del input en Safari/Firefox ·
       explicit rules para inputs con placeholder gigante mobile. */
    #peer-in::placeholder,
    #nexo-msg-input::placeholder,
    #mpx-search::placeholder,
    #mpx-compose-to-input::placeholder,
    #mpx-compose-cc::placeholder,
    #mpx-compose-subject::placeholder,
    #vault-search::placeholder,
    #admin-name::placeholder {
        font-size: 0.85rem !important;
        opacity: 0.5;
    }
    #mpx-compose-body::placeholder,
    #user-input::placeholder,
    #iris-ticker-input::placeholder {
        font-size: 16px !important;
        opacity: 0.5;
    }

    /* Finding A · #nexo-msg-input UNPROTECTED (audit v3) · WCAG fail · ID rule defensive */
    #nexo-msg-input {
        font-size: 0.85rem !important;
    }
}

/* ─────────────────────────────────────────────────────────────────────
 * COSMETIC-EMPTY 2026-08-02 · ESTADO VACIO · fuente unica de verdad
 * Los 3 mensajes de "aqui no hay nada" del producto iban a
 * rgba(255,255,255,0.08) inline y duplicado = 1.20:1 sobre --bg-panel
 * (#0a0e14): texto tecnicamente presente pero ilegible, falla AA 4.5:1.
 * Pasan a #8892a4 = 6.17:1 (gris meta de plataforma) y a esta clase, para
 * que el proximo no nazca otra vez a ojo:
 *   1 · index.html   · vault.empty          "NO FILES INGESTED"
 *   2 · soul.js:11037· vault.grid.noresults  VAULT filtrado sin resultados
 *   3 · ui_core.js   · nexo.pair.none        "Sin emparejamientos"
 * La clase fija SOLO familia, tracking y color: el tamano y los margenes
 * siguen inline porque difieren por sitio (0.65rem / 0.5rem / 0.65rem).
 * ───────────────────────────────────────────────────────────────────── */
.synaris-empty {
    font-family: 'Share Tech Mono', monospace;
    letter-spacing: 3px;
    color: #8892a4;
}

/* ─────────────────────────────────────────────────────────────────────
 * ISSUE-4 · GLOBAL SYNARIS SCROLLBAR · Linear-style hidden + 3px hover
 * Aplica a body global + class .synaris-scroll · CEO ratificado 2026-05-10
 * Reemplaza scrollbars azul visible default · existing custom (.left-panel,
 * #hist-list, etc) preserved via specificity (más-específico gana).
 * ───────────────────────────────────────────────────────────────────── */
.synaris-scroll {
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
}
.synaris-scroll:hover {
    scrollbar-color: rgba(0,128,255,0.4) transparent;
}
.synaris-scroll::-webkit-scrollbar {
    width: 3px;
    height: 3px;
}
.synaris-scroll::-webkit-scrollbar-track {
    background: transparent;
}
.synaris-scroll::-webkit-scrollbar-thumb {
    background: transparent;
    border-radius: 2px;
    transition: background 0.15s;
}
.synaris-scroll:hover::-webkit-scrollbar-thumb {
    background: rgba(0,128,255,0.4);
}

/* ─────────────────────────────────────────────────────────────────────
 * .synaris-scroll-hidden · scroll funcional · CERO scrollbar visible
 * Scope: timeline IRIS calendario sidebar (overflow-y:auto sin barra)
 * CEO ratificado 2026-05-19 · más estricto que .synaris-scroll
 * scrollbar-width:none (Firefox) + ::-webkit-scrollbar{display:none}
 * ─────────────────────────────────────────────────────────────────── */
.synaris-scroll-hidden {
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.synaris-scroll-hidden::-webkit-scrollbar {
    display: none;
    width: 0;
    height: 0;
}
.synaris-scroll::-webkit-scrollbar-thumb:hover {
    background: rgba(0,128,255,0.6);
}
/* Universal default · all overflow containers heredan synaris-scroll behavior
   (sin necesidad de class) · más específicos en .synaris-scroll y otros
   selectors sobreescriben donde haga falta. */
body,
[style*="overflow:auto"],
[style*="overflow-y:auto"],
[style*="overflow-x:auto"],
[style*="overflow: auto"],
[style*="overflow-y: auto"],
[style*="overflow-x: auto"] {
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
    transition: scrollbar-color 0.15s;
}
body:hover,
[style*="overflow:auto"]:hover,
[style*="overflow-y:auto"]:hover,
[style*="overflow-x:auto"]:hover,
[style*="overflow: auto"]:hover,
[style*="overflow-y: auto"]:hover,
[style*="overflow-x: auto"]:hover {
    scrollbar-color: rgba(0,128,255,0.4) transparent;
}

/* ─────────────────────────────────────────────────────────────────────
 * ISSUE-4 · DRAGGABLE DIVIDER COMPONENT · resize columns + persist
 * Default 4px wide vertical divider · cursor:col-resize · hover azul
 * JS attaches via _synarisAttachDivider(divider, leftPanel, opts)
 * ───────────────────────────────────────────────────────────────────── */
.synaris-divider {
    flex: 0 0 4px;
    background: rgba(0,128,255,0.04);
    cursor: col-resize;
    transition: background 0.15s;
    position: relative;
    user-select: none;
    -webkit-user-select: none;
    z-index: 5;
}
.synaris-divider:hover,
.synaris-divider.dragging {
    background: rgba(0,128,255,0.30);
}
.synaris-divider::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 1px;                          /* centrado ENTERO en barra de 4px (1..3px) · evita subpixel del grip */
    transform: translateY(-50%);        /* solo vertical · sin translate(-50%) horizontal */
    width: 2px;                          /* ancho PAR · elimina jitter del grip de 1px impar */
    height: 30px;
    background: rgba(0,128,255,0.20);
    transition: background 0.15s;
}
.synaris-divider:hover::after,
.synaris-divider.dragging::after {
    background: rgba(0,128,255,0.60);
}
/* DIV-1 folders FIJA · sin feedback de movible (no hover azul · grip neutro) */
.synaris-divider.synaris-fixed:hover { background: rgba(0,128,255,0.04); }
.synaris-divider.synaris-fixed:hover::after { background: rgba(0,128,255,0.20); }
/* BUG-B v2 · !important required to override inline style="min-width:0" on
 * #mpx-list-pane (index.html:455) and #mpx-viewer-pane (index.html:472).
 * The inline 0 was intentional to nullify min-width:auto (flex default =
 * content size) which enables flex shrink + text-overflow:ellipsis on
 * children. We replace the FLOOR without breaking the anti-auto behavior
 * (any explicit value including 380/300px still overrides auto).
 * Mobile <769px keeps inline 0 untouched because this @media rule doesn't
 * apply, preserving stack-vertical layout from @media (max-width:768px)
 * at L1020. BUG-B v1 (without !important) lost to inline specificity 1000. */
@media (min-width: 769px) {
    /* RESPONSIVE-FIX 2026-06-14 · selector de correo SIEMPRE visible · nunca recortado.
     * ─────────────────────────────────────────────────────────────────────────────
     * Causa raíz del bug "el selector desaparece al comprimir": el visor es un OVERLAY
     * absoluto on-demand (JS _mpxSetViewerVisible / _mpxViewerOverlayGeom · soul.js
     * L12974-13034 · display:none al cerrar · position:absolute al abrir, ENCIMA de la
     * lista, folders visible). El flujo desktop real es 2-pane: folders(150) + lista(flex:1).
     * Los floors fijos previos (list 320 · visor 280) forzaban un ancho de fila mínimo
     * 150+320+280=750px > main-view en ventana comprimida → desbordaba → el guard
     * #mail-wall{overflow-x:hidden} RECORTABA la lista. También mordía en el instante en
     * que el visor cae in-flow (timing/retry de _mpxOnEnterMode L15079).
     * Fix: min-width:0 en lista y visor → la fila NUNCA desborda a ningún ancho (la lista
     * cede y absorbe) → jamás se recorta · degrada con gracia. folders mantiene 150px
     * (aloja el botón COMPOSE). v3 floors revertidos. */
    #mpx-folders     { min-width: 150px !important; }   /* pane fijo · contiene COMPOSE */
    #mpx-list-pane   { min-width: 0    !important; }    /* era 320 · floor = causa del recorte */
    #mpx-viewer-pane { min-width: 0    !important; }    /* era 280 · idem (además es overlay) */
    /* Guard inocuo: con min-width:0 la fila ya no desborda nunca, así que overflow-x:hidden
     * no recorta nada · se conserva solo como cinturón anti-scrollbar ante estados raros
     * (visor in-flow transitorio). El layout NO debe desbordar por construcción. */
    #mail-wall { overflow-x: hidden; }
}
/* BUG-C · MIPOST scrollables · hide scrollbar completely (auto-scroll UX).
 * Existing .synaris-scroll class shows 3px thumb on hover · universal
 * [style*="overflow-y:auto"] also kept thumb visible on hover. CEO spec:
 * scrollbar NEVER visible · hard display:none across all browsers.
 * IDs win on specificity (100) over class .synaris-scroll (10) and
 * attribute-style selector (10). Mobile <769px global * rule at L1050
 * already hides everything · this is desktop scope only.
 *
 * v2 (2026-05-12) · #mpx-list also gets overflow-x:hidden to kill the
 * horizontal scrollbar that macOS/Safari shows when scrollWidth>clientWidth.
 * Root cause was the row's 1fr center grid item with default min-width:auto
 * (= min-content of children) · long unbroken from_addr/to_addr text
 * expanded the cell · row overflowed · horizontal scroll appeared even
 * with scrollbar-width:none. Center min-width:0 fix lives in renderMpxList
 * (soul.js) for ellipsis to actually trigger. */
@media (min-width: 769px) {
    #mpx-folders,
    #mpx-list,
    #mpx-viewer-body {
        scrollbar-width: none;
        -ms-overflow-style: none;
    }
    #mpx-folders::-webkit-scrollbar,
    #mpx-list::-webkit-scrollbar,
    #mpx-viewer-body::-webkit-scrollbar {
        width: 0;
        height: 0;
        display: none;
    }
    #mpx-list {
        overflow-x: hidden;
    }
}
/* Body class added during drag · prevents text selection + cursor consistency */
body.synaris-resizing {
    cursor: col-resize !important;
    user-select: none !important;
    -webkit-user-select: none !important;
}
body.synaris-resizing * {
    cursor: col-resize !important;
}

/* MIPOST-MOBILE-FIX-3 Bug 3 · synaris-divider 4px resize handles sin propósito
   en mobile (touch no permite drag-resize · solo vertical noise estético).
   Oculta todos los dividers en mobile · pane resizing es desktop-only feature. */
@media (max-width: 768px) {
    .synaris-divider {
        display: none !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   MIPOST-COMPOSE-MOBILE-LAYOUT · Gmail/Outlook compose pattern mobile
   · Inner modal box cascade height vh→dvh para compat universal (older
     browsers sin dvh fallback a vh con URL bar overlap aceptable)
   · Header sticky top + footer sticky bottom · body form scroll independiente
   · Footer padding-bottom safe-area-inset-bottom respeta home indicator iOS
   · Textarea body min-height 240→140 mobile (240 era excesivo · push form)
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
    /* Inner box · cascade vh fallback ANTES de dvh override · si browser no
       soporta dvh, parser ignora segunda regla pero primera (vh) aplica */
    #mpx-compose-modal > div:nth-child(2) {
        height: 100vh !important;
        height: 100dvh !important;
        max-height: 100vh !important;
        max-height: 100dvh !important;
    }
    /* Body form · fill espacio restante · scroll vertical independiente */
    #mpx-compose-form-wrap {
        flex: 1 1 auto !important;
        min-height: 0 !important;
        overflow-y: auto !important;
    }
    /* Header sticky top · evita scroll del form cubriendo título */
    #mpx-compose-header {
        position: sticky !important;
        top: 0 !important;
        z-index: 2 !important;
        background: #080c10 !important;
    }
    /* Footer sticky bottom · safe-area-inset bottom respeta home indicator */
    #mpx-compose-footer {
        position: sticky !important;
        bottom: 0 !important;
        z-index: 2 !important;
        background: #080c10 !important;
        padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)) !important;
    }
    /* MIPOST-COMPOSE-MOBILE-FIX-2 Bug 1 · body textarea fluid mobile · wrap padre
       flex:1 + min-height:0 da espacio · textarea flex:1 fill auto · resize:none
       disable handle (usuario ya NO arrastra manual · auto-grow espacio disponible
       entre form fields scroll y footer sticky bottom) */
    #mpx-compose-body-wrap {
        flex: 1 1 auto !important;
        min-height: 0 !important;
    }
    #mpx-compose-body {
        flex: 1 1 auto !important;
        min-height: 120px !important;
        resize: none !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   MOBILE-AUDIT-A · BATCH A · auditoría visual responsive
   M3 word-break + M4 IRIS grid 768px + H2 font-size targeted uplift
   Track B agendado · H1 sidebar drawer + H3 safe-area-inset (Sprint MOBILE-B)
   ═══════════════════════════════════════════════════════════════════════ */

/* M3 · prevent horizontal overflow · URLs/hashes/tokens largos sin espacios
   rompen viewport en mobile · break-word permite wrap sin afectar texto normal */
.hub-bubble,
.dm-bubble,
#hub-chat-wall,
#email-inbox-container .mpx-list-row,
#email-inbox-container .mpx-snippet,
#mail-thread-messages,
#mail-list-view,
.mpx-viewer-body {
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* M4 · IRIS grid stack vertical en mobile 481-768px (gap del breakpoint 480px existente) */
@media (max-width: 768px) {
    #iris-grid {
        grid-template-columns: 1fr !important;
        /* FIX solapamiento · neutraliza el grid-template-rows:repeat(2,1fr) que pone el JS
           (_updateGridLayout calcula filas para 2 columnas → descuadra 3+ tiles en 1 columna).
           Con none + auto-rows: N tiles = N filas que respetan min-height y llenan/scrollean. */
        grid-template-rows: none !important;
        grid-auto-rows: minmax(180px, 1fr) !important;
        grid-auto-flow: row !important;
    }
    #iris-grid > .iris-tile,
    #iris-grid > div[data-peer] {
        width: 100% !important;
        max-width: 100% !important;
        min-height: 180px;
    }
}

/* H2 · WCAG SC 1.4.4 legibilidad mobile · uplift selectivo de texto USER-READABLE
   No global · preserva estética Palantir/military intencional · solo selectors
   con contenido que el usuario lee (no badges/labels decorativos) */
@media (max-width: 768px) {
    /* Chat content principal HUB + DM */
    .hub-bubble,
    .dm-bubble,
    #hub-chat-wall .hub-msg-wrap {
        font-size: 0.72rem !important;
    }
    /* Reply preview HUB + DM */
    .hub-reply-preview,
    .dm-reply-preview {
        font-size: 0.65rem !important;
    }
    /* Timestamps DM · uplift de 0.4rem (~6.4px) a 0.55rem (~8.8px) */
    .dm-timestamp {
        font-size: 0.55rem !important;
    }
    /* Mail content user-readable */
    #email-inbox-container .mpx-list-row,
    #email-inbox-container .mpx-snippet,
    .mpx-viewer-body {
        font-size: 0.72rem !important;
    }
    /* Metadata legible (timestamps, senders) min 0.55rem */
    .hub-edit-tag,
    .dep-link-status,
    .copy-txt {
        font-size: 0.55rem !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   VAULT-TRASH-MOBILE-COMPLETE · 4 bugs bundled · WCAG 44px + responsive
   Bug 1 PAPELERA chip · soul.js inline · NO CSS necesario
   Bug 2 preview actions buttons · min-height 44px tap target
   Bug 3 trash list row · stack vertical · buttons 50% side-by-side
   Bug 4 trash banner word-wrap · long Spanish text mobile
   Refinement R4 :only-of-type · non-CEO 1 button full-width
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
    /* Bug 2 · preview pane buttons WCAG tap target 44px + font legibility */
    #vault-preview-actions button,
    #vault-preview-actions a {
        min-height: 44px !important;
        padding: 10px 12px !important;
        font-size: 0.5rem !important;
    }
    /* Bug 3 · trash list row stack vertical · buttons 50% side-by-side */
    .vault-trash-row {
        flex-wrap: wrap !important;
        gap: 10px !important;
    }
    .vault-trash-row > div {
        flex: 1 1 100% !important;
        overflow-wrap: anywhere;
        word-break: break-word;
    }
    .vault-trash-row > button {
        flex: 1 1 calc(50% - 5px) !important;
        min-height: 44px !important;
        padding: 10px !important;
        font-size: 0.5rem !important;
        letter-spacing: 1.5px !important;
    }
    /* Refinement R4 · non-CEO single button (RESTAURAR only) · full-width */
    .vault-trash-row > button:only-of-type {
        flex: 1 1 100% !important;
    }
    /* Bug 4 · banner destructive word-wrap mobile · long Spanish text safe */
    .vault-trash-banner {
        overflow-wrap: anywhere !important;
        word-break: break-word !important;
        font-size: 0.5rem !important;
        line-height: 1.5 !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   MIPOST-BULK-OPERATIONS P0 · multi-select mobile responsive + WCAG
   Checkbox column wider mobile (44px tap target) · bulk toolbar wrap +
   buttons min-height 44px · sticky top backdrop-filter blur
   ═══════════════════════════════════════════════════════════════════════ */
/* MIPOST-STAR-VISUAL · star column hover transition · solo unfilled muestra hover effect
   Filled star siempre amber #D29922 (no cambia hover) · empty star pasa 0.25→0.55 stroke */
.mpx-star-empty:hover {
    stroke: rgba(255,255,255,0.55) !important;
}

/* MIPOST-46 FASE 3 · priority flag hover · empty rgba 0.25 → 0.6 stroke smooth */
.mpx-priority-empty:hover {
    stroke: rgba(255,68,102,0.6) !important;
}

/* ═══════════════════════════════════════════════════════════════════════
   MAIL-LIST-UX-ENTERPRISE · typography upgrade (Gmail/Outlook/Proton level)
   Extraído de inline soul.js renderMpxList (R4 · hardcoded inline → class).
   Desktop base; mobile hereda (ahora más legible que el 0.55/0.6rem previo).
   ═══════════════════════════════════════════════════════════════════════ */
.mpx-from {
    font-family: 'Inter', system-ui, sans-serif;  /* FONT-HYBRID · scan UX (no forense) · Inter ~30% más compacto vs mono */
    font-size: 12px;                  /* 0.55rem (~8.8px) → 12px · downsize -1px */
    font-weight: 500;                 /* medium */
    color: rgba(230,235,245,0.7);     /* POLISH-FINAL · default external · sender subordinate */
    letter-spacing: 0;                /* POLISH-FINAL · Inter sans-serif no necesita tracking */
}
/* MAIL-LIST-SENDER-IDENTITY · 3-tier hierarchy diferenciador SYNARIS vs Gmail/Outlook */
.mpx-from.mpx-from-corporate { color: #4DA6FF; }              /* info@<tenant> · brand azul · most prominent */
.mpx-from.mpx-from-internal  { color: rgba(86,211,100,0.6); } /* <user>@<tenant> · verde subtle team member */
.mpx-subject {
    font-family: 'Inter', system-ui, sans-serif;  /* FONT-HYBRID · scan UX (no forense) */
    font-size: 13px;                  /* 0.6rem (~9.6px) → 13px · downsize -1px */
    font-weight: 600;                 /* semibold base · unread=700 inline clampea a 600 bajo eje Inter (300-600) · señal no-leído queda en sobre+opacidad */
    color: rgba(230,235,245,0.85);
}
.mpx-meta {
    font-family: 'Share Tech Mono', monospace;
    font-size: 0.42rem;               /* sin cambio de tamaño */
    color: rgba(255,255,255,0.32);    /* 0.35 → 0.32 · score más muted */
    letter-spacing: 1px;
}

@media (max-width: 768px) {
    /* MIPOST-46 FASE 3 · grid mobile 6 cols · checkbox 44 + star 40 + priority 40 + envelope 18 + 1fr + meta 60 */
    .mpx-list-row {
        grid-template-columns: 44px 40px 40px 18px 1fr 60px !important;
    }
    /* Bulk toolbar wrap multi-row mobile · buttons WCAG 44px */
    #mpx-bulk-toolbar {
        flex-wrap: wrap !important;
        gap: 6px !important;
        padding: 10px 12px !important;
    }
    #mpx-bulk-toolbar button {
        min-height: 44px !important;
        padding: 10px 12px !important;
        font-size: 0.45rem !important;
        flex: 1 1 auto !important;
    }
    /* Counter span full-width al wrap · separate visualmente de actions */
    #mpx-bulk-toolbar > span:first-child {
        flex: 1 1 100% !important;
        margin-bottom: 4px !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   F31-B · @MEETING AUTO-SCHEDULE · meeting card + undo toast
   Pattern Slack/Discord enterprise · austero Palantir
   Gradient azul SYNARIS · border distintivo · botones acción
   ═══════════════════════════════════════════════════════════════════════ */
.hub-meeting-card {
    max-width: 480px;
    width: fit-content;   /* F31-CARD-WIDTH · anula el stretch del wall en pendiente → content-width en ambos estados (= burbuja confirmada) */
    margin: 8px 14px;
    padding: 14px;
    background: linear-gradient(135deg, rgba(77,166,255,0.08), rgba(77,166,255,0.02));
    border: 1px solid rgba(77,166,255,0.35);
    border-left: 3px solid #4DA6FF;
    border-radius: 4px;
    font-family: Share Tech Mono, monospace;
    color: rgba(230,235,245,0.85);
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.hub-meeting-card.cancelled {
    opacity: 0.5;
}
.hub-meeting-card.cancelled .hub-meeting-card-body span:last-child {
    text-decoration: line-through;
}
/* F31-FIX-BUNDLE Bug 4 · estado confirmed verde post-30s undo passed · azul→verde transición */
.hub-meeting-card.confirmed {
    border-color: rgba(63,185,80,0.4);
    border-left: 3px solid #3FB950;
    background: linear-gradient(135deg, rgba(63,185,80,0.06), rgba(63,185,80,0.02));
}
.hub-meeting-card.confirmed .hub-meeting-card-header {
    color: #56D364;
}
.hub-meeting-card.confirmed .hub-meeting-card-header svg {
    stroke: #3FB950;
}
.hub-meeting-card-header {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: Orbitron, monospace;
    font-size: 0.55rem;
    letter-spacing: 3px;
    color: #4DA6FF;
}
.hub-meeting-card-body {
    display: flex;
    flex-direction: column;
    gap: 8px;
    font-size: 0.6rem;
}
.hub-meeting-line {
    display: flex;
    gap: 12px;
    align-items: baseline;
}
.hub-meeting-label {
    min-width: 80px;
    color: rgba(255,255,255,0.32);
    font-size: 0.5rem;
    letter-spacing: 1.5px;
    flex-shrink: 0;
}
/* F31-FINAL · values contrast tier 1 · color brighter + weight bump */
.hub-meeting-line > span:last-child {
    color: #e8f4f8;
    font-weight: 500;
}
/* MEETING-MOBILE-WRAP - L0 medido (Playwright + fuentes reales): un token sin puntos de corte
   (email/user_id sin espacios ni guiones) desbordaba .hub-meeting-card <=360px y el toast <=414px.
   overflow-wrap:anywhere reduce el min-content + min-width:0 deja encoger el flex item. Verificado 414->320px. */
.hub-meeting-line > span:last-child { overflow-wrap: anywhere; min-width: 0; }
.hub-meeting-undo-toast > div { min-width: 0; overflow-wrap: anywhere; }
.hub-meeting-undo-btn { flex-shrink: 0; }
.hub-meeting-card-actions {
    display: flex;
    gap: 8px; /* peso visual · armoniza con el ritmo 8px del cartel (era 10px) */
    margin-top: 4px;
}
.hub-meeting-join, .hub-meeting-edit, .hub-meeting-cancel {
    padding: 5px 12px; /* peso visual · vertical 6px→5px */
    background: transparent;
    font-family: Orbitron, monospace;
    font-size: 0.42rem;
    letter-spacing: 2px;
    cursor: pointer;
    border-radius: 2px;
    min-height: 28px; /* peso visual · 36px→28px · la altura la dominaba min-height (no el padding) · texto 0.42rem no necesita 36px */
    flex: 1 1 auto;
    transition: background 0.15s;
}
.hub-meeting-join { border: 1px solid rgba(77,166,255,0.5); color: #4DA6FF; }
.hub-meeting-join:hover { background: rgba(77,166,255,0.08); }
.hub-meeting-edit { border: 1px solid rgba(255,255,255,0.18); color: #8892a4; }
.hub-meeting-edit:hover { background: rgba(255,255,255,0.05); color: #c8d2e0; }
.hub-meeting-cancel { border: 1px solid rgba(255,68,102,0.5); color: #ff4466; }
.hub-meeting-cancel:hover { background: rgba(255,68,102,0.08); }

/* Undo toast bottom-center · countdown 30s · pattern Slack */
/* F31-FIX-BUNDLE-2 Bug C · bottom 20→90px · sobre input zone (no solapa textarea) */
.hub-meeting-undo-toast {
    position: fixed;
    bottom: 90px;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(8,12,20,0.95);
    border: 1px solid rgba(77,166,255,0.35);
    border-radius: 4px;
    padding: 10px 14px;
    display: flex;
    gap: 12px;
    align-items: center;
    z-index: 9999;
    font-family: Share Tech Mono, monospace;
    font-size: 0.55rem;
    color: #d4d8e0;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    box-shadow: var(--sombra-2);   /* SYNARIS VIVO · mismo valor, ahora con nombre */
}
.hub-meeting-undo-btn {
    background: transparent;
    border: 1px solid rgba(255,68,102,0.5);
    color: #ff4466;
    font-family: Orbitron, monospace;
    font-size: 0.42rem;
    letter-spacing: 2px;
    padding: 4px 10px;
    cursor: pointer;
    border-radius: 2px;
    min-height: 28px;
}
.hub-meeting-undo-btn:hover { background: rgba(255,68,102,0.08); }

@media (max-width: 768px) {
    .hub-meeting-card { max-width: calc(100% - 28px); }
    .hub-meeting-line { flex-direction: column; gap: 2px; }
    .hub-meeting-label { min-width: 0; }
    .hub-meeting-card-actions button {
        flex: 1 1 auto;
        min-height: 44px;
    }
    .hub-meeting-undo-toast {
        left: 14px;
        right: 14px;
        transform: none;
        max-width: calc(100% - 28px);
    }
}

/* ═══════════════════════════════════════════════════════════════════
   IRIS SHARE STAGE · GATE 1 (HTML+CSS only · sin JS/wiring)
   Patrón Zoom: el screen-share sale del grid (#iris-grid celda → se troceaba
   en franjas al re-render) a un contenedor dedicado, fixed y dominante, con
   los participantes en strip lateral. Stage arranca display:none (inline) y
   NADA la activa → runtime idéntico a hoy. Gate 2+ añade el wiring JS.
   z-index: usa --z-iris-grid (950) · dominante sobre overlay (900), bajo
   palette/modal · alinea con la escala IRIS FOCUS MODE (F14).
   ═══════════════════════════════════════════════════════════════════ */
#iris-share-stage {
    position: fixed;
    inset: 0;
    z-index: var(--z-iris-grid, 950);
    display: flex;
    flex-direction: row;
    background: #000;
}
#iris-share-video {
    flex: 1;
    min-width: 0;
    object-fit: contain;
    background: #000;
}
#iris-share-strip {
    width: 200px;
    flex: 0 0 200px;
    overflow-y: auto;
    background: #020509;
    /* GATE strip · columna de miniaturas tipo Zoom · los tiles se reparentan aquí (no clon) */
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 6px;
    box-sizing: border-box;
}
/* GATE strip · tiles reparentados → override del layout de grid (gate C aspect/stretch)
   para que se vean como miniaturas verticales ordenadas, no como celdas de grid. */
#iris-share-strip .iris-tile {
    width: 100%;
    aspect-ratio: 16 / 9;
    min-height: 0;
    min-width: 0;
    flex: 0 0 auto;
}
#iris-share-strip .iris-tile video {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
/* GATE 3 · strip sin poblar → ocultar (share full-bleed). :empty (id+pseudo) gana al
   display:flex base → cuando hay miniaturas reaparece como flex column. */
#iris-share-strip:empty { display: none; }
/* C1.c · el stage vive en inset:0, y C1 elevo .selector-area y #iris-subtab-bar a
   position:fixed z:975 SOBRE el: su franja superior quedaba debajo de las barras y la
   miniatura del strip salia cortada por el menu. Mismo patron que GATE FIX-1
   2026-06-01 (L2231) para el grid en focus-active, pero con la medida CORREGIDA.

   MEDIDO, no heredado: aquel comentario dice "selector-area 42 + subtab 45 = 87" y el 87
   no sale. La nav mide 42, pero el subtab no empieza donde acaba la nav: esta anclado a
   `top: 48px`, con 6px de hueco, y mide 45 → su borde inferior cae en 93. Banco de render
   con el CSS y las FUENTES reales (sin Orbitron la nav mide 41 y el numero cambia; por eso
   se midio con document.fonts.ready). El precedente arrastra ese mismo desfase de 6px en
   focus-active — declarado, NO tocado aqui: es otro concern.

   Sin padding abajo en escritorio a proposito: los controles flotan sobre el video
   (patron Zoom) y asi no se le roba alto al share. */
body.iris-share-staged #iris-share-stage {
    padding-top: calc(93px + env(safe-area-inset-top));
}
@media (max-width: 768px) {
    /* En movil el 42 del precedente NO aplica, y por dos motivos medidos:
       (a) #iris-subtab-bar no tiene override movil — sigue en top:48 y su borde inferior
           sigue en 93, igual que en escritorio;
       (b) la .selector-area SI se mueve: pasa a ser barra INFERIOR de 56px (L1341, z:9999),
           y el strip tambien baja (el stage gira a column, strip de 90px abajo) → la barra
           le tapaba 56 de esos 90px. Ahi si hace falta despejar por abajo. */
    body.iris-share-staged #iris-share-stage {
        padding-bottom: calc(56px + env(safe-area-inset-bottom));
    }
}
/* Activación gate 2+: oculta el grid cuando la stage toma el protagonismo */
body.iris-share-staged #iris-grid { display: none; }

@media (max-width: 768px) {
    #iris-share-stage { flex-direction: column; }
    #iris-share-strip {
        width: 100%;
        flex: 0 0 90px;
        height: 90px;
        flex-direction: row;
        overflow-x: auto;
        overflow-y: hidden;
    }
    /* móvil · strip horizontal abajo · miniaturas de ancho fijo por altura */
    #iris-share-strip .iris-tile {
        width: auto;
        height: 100%;
        aspect-ratio: 16 / 9;
    }
}

/* ── HUB-DIRECT · panel slide (Fase 4) · producto caro Linear/Teams ───────── */
#hubdirect-panel {
    position: fixed; top: 0; right: 0; bottom: 0; width: 380px; max-width: 100vw;
    background: #0a0e14; border-left: 1px solid rgba(77,166,255,0.18);
    transform: translateX(100%); transition: transform .28s cubic-bezier(.22,.61,.36,1);
    will-change: transform; z-index: 10000; display: flex; flex-direction: column;
    font-family: 'Share Tech Mono', monospace; box-shadow: -12px 0 32px rgba(0,0,0,0.5);
}
body.hubdirect-open #hubdirect-panel { transform: translateX(0); }
/* drawer-overlay PURO · el panel flota por encima · el contenido de fondo NO se mueve/comprime */
#hd-top { flex-shrink: 0; padding: 12px 14px; border-bottom: 1px solid rgba(255,255,255,0.07); display: flex; align-items: center; gap: 9px; min-height: 46px; }
#hd-main { flex: 1; min-height: 0; overflow-y: auto; }
#hd-seal { flex-shrink: 0; padding: 7px 14px; border-top: 1px solid rgba(255,255,255,0.05); font-size: 0.42rem; letter-spacing: 1.5px; color: #3a3d4a; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#hd-compose { flex-shrink: 0; }
.hd-row { display: flex; align-items: center; gap: 9px; padding: 9px 14px; cursor: pointer; border-bottom: 1px solid rgba(255,255,255,0.03); transition: background .12s; }
.hd-row:hover { background: rgba(77,166,255,0.06); }
.hd-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.hd-name { color: #cdd6f4; font-size: 0.62rem; letter-spacing: 1px; margin-bottom: 2px; }
.hd-sub { color: #6b7280; font-size: 0.46rem; letter-spacing: 0.5px; }
.hd-badge { background: #ff4466; color: #fff; border-radius: 8px; min-width: 16px; height: 16px; padding: 0 4px; font-size: 0.5rem; display: inline-flex; align-items: center; justify-content: center; }
.hd-msg { max-width: 78%; padding: 7px 10px; border: 1px solid rgba(77,166,255,0.18); border-radius: 3px; font-size: 0.62rem; color: #cdd6f4; letter-spacing: 0.3px; line-height: 1.45; word-break: break-word; background: rgba(77,166,255,0.03); }
.hd-msg.mine { border-color: rgba(77,166,255,0.4); background: rgba(0,128,255,0.07); }
.hd-iconbtn { width: 28px; height: 26px; border-radius: 4px; border: 1px solid rgba(77,166,255,0.3); background: transparent; color: #4DA6FF; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
    transition: border-color 0.15s, background 0.15s;   /* COSMETIC-BTN-1 · tenia hover pero no transicion: el cambio era un salto seco */ }
.hd-iconbtn:hover { background: rgba(77,166,255,0.1); }
.hd-iconbtn.active { border-color: #ff4466; color: #ff4466; }
@media (max-width: 820px) {
    #hubdirect-panel { width: 100vw; }
}

/* ── HUB-CALL-DIRECT · overlay llamada mínimo (Fase 4 BUG2) · card centrada ── */
#hubcall-overlay {
    position: fixed; inset: 0; z-index: 10001; display: none;
    align-items: center; justify-content: center; background: rgba(2,4,8,0.72);
    font-family: 'Share Tech Mono', monospace;
}
#hubcall-overlay.show { display: flex; }
#hco-card {
    background: #0b0e14; border: 1px solid rgba(77,166,255,0.4); border-radius: 12px;
    padding: 40px 46px 34px; min-width: 360px; max-width: 92vw; text-align: center;
    box-shadow: 0 18px 56px rgba(0,0,0,0.62);
}
/* animación de entrada tipo Zoom · escala 0.9→1 + fade · ~250ms ease-out (enterprise) */
.hco-pop { animation: hco-zoom .25s cubic-bezier(.16, 1, .3, 1) both; }
@keyframes hco-zoom { from { transform: scale(0.9); opacity: 0; } to { transform: scale(1); opacity: 1; } }
#hco-avatar {
    width: 76px; height: 76px; border-radius: 50%; margin: 0 auto 16px; overflow: hidden;
    background: rgba(0,128,255,0.12); border: 1px solid rgba(77,166,255,0.35);
    display: flex; align-items: center; justify-content: center;
    color: #4DA6FF; font-size: 1.4rem; letter-spacing: 1px;
}
#hco-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
#hco-name { color: #cdd6f4; font-size: 1rem; letter-spacing: 2px; margin-bottom: 6px; }
#hco-state { color: #4DA6FF; font-size: 0.6rem; letter-spacing: 3px; margin-bottom: 12px; }
#hco-state.pulse { animation: hco-pulse 1.4s ease-in-out infinite; }
@keyframes hco-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.45; } }
#hco-seal { color: #3a3d4a; font-size: 0.42rem; letter-spacing: 1.5px; margin-bottom: 22px; }
#hco-controls { display: flex; gap: 18px; justify-content: center; align-items: center; }
.hco-btn {
    width: 48px; height: 48px; border-radius: 50%; border: 1px solid rgba(77,166,255,0.4);
    background: rgba(0,128,255,0.07); color: #4DA6FF; cursor: pointer;
    display: flex; align-items: center; justify-content: center; transition: all .15s;
}
.hco-btn:hover { background: rgba(77,166,255,0.14); }
.hco-btn.active { border-color: #ff4466; color: #ff4466; background: rgba(255,68,102,0.1); }
.hco-btn.hangup { border-color: #ff4466; color: #ff4466; background: rgba(255,68,102,0.12); }
.hco-btn.hangup:hover { background: rgba(255,68,102,0.22); }

/* ── HUB-DIRECT · apertura LIMPIA · sin efecto (scan-line y partículas retirados por decisión CEO 2026-06-07) ── */

/* ── HUB-CALL · superficie de vídeo en el overlay (gap2 · NO grid IRIS) ── */
#hco-vid { position: relative; width: 100%; max-width: 280px; aspect-ratio: 4/3; margin: 0 auto 14px; border-radius: 8px; overflow: hidden; background: #05070b; border: 1px solid rgba(77,166,255,0.25); }
#hco-remote { width: 100%; height: 100%; object-fit: cover; display: none; background: #05070b; }
#hco-local { position: absolute; bottom: 8px; right: 8px; width: 84px; height: 63px; object-fit: cover; border-radius: 4px; border: 1px solid rgba(77,166,255,0.4); display: none; background: #05070b; box-shadow: 0 2px 8px rgba(0,0,0,0.5); }
