/* SAITClash360 - Tema y Variables */
/* Sistema de tokens portable (docs/diseño/design_handoff_saitclash360_ui):
   el color de marca vive en --accent-solid/--accent/--accent-cta + 3 washes,
   todos definidos UNA sola vez acá. Cambiar de cliente = cambiar estos ~8
   valores (y el logo); nada de hex sueltos en el resto del CSS. */
:root {
    /* Marca (portable) */
    --accent-solid: #7B1FA2;   /* fondos sólidos: avatares, checkbox marcado, pill activo */
    --accent: #a855c9;         /* texto/iconos sobre fondo oscuro */
    --accent-cta: #F5A623;     /* acción primaria — dorado, fijo en SAIT, swappeable */
    --accent-cta-hover: #d9910f;
    --accent-cta-ink: #1A1A1A; /* texto sobre --accent-cta (fijo, no depende del tema) */
    --accent-soft: rgba(123, 31, 162, 0.08);
    --accent-wash: rgba(123, 31, 162, 0.14);
    --accent-border: rgba(123, 31, 162, 0.35);

    /* Colores - Tema Oscuro (default) */
    --bg-primary: #14121a;
    --bg-secondary: #1d1a25;
    --bg-tertiary: #262230;
    --bg-hover: #2f2a3c;
    --text-primary: #f5f3f8;
    --text-secondary: #b7b0c4;
    --text-muted: #7c7590;
    --border: #332e40;
    --border-light: #292433;

    /* Semánticos (fijos, no cambian con marca/tema) */
    --success: #2ba862;
    --warning: #f8bb52;
    --danger: #e14040;
    --info: #4a9eda;
    --danger-bg: #3a1c1e;
    --warning-bg: #3a2e14;
    --success-bg: #173322;

    --shadow: 0 4px 6px -1px rgba(0,0,0,0.3);
    --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.4);
    --radius: 8px;
    --radius-lg: 12px;
    --radius-full: 9999px;
    --space-xs: 4px;
    --space-sm: 8px;
    --space-md: 16px;
    --space-lg: 24px;
    --space-xl: 32px;
    --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
    --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
    --transition: 150ms ease;
    --sidebar-width: 280px;
}

[data-theme="light"] {
    --accent: #7B1FA2; /* en claro, el brand color directo (no la versión aclarada) */
    --bg-primary: #f4f2f8;
    --bg-secondary: #ffffff;
    --bg-tertiary: #f6f4fa;
    --bg-hover: #ece8f4;
    --text-primary: #1A1A1A;
    --text-secondary: #5c5668;
    --text-muted: #9892a8;
    --border: #e3dfec;
    --border-light: #ece8f4;
    --danger-bg: #fbe6e6;
    --warning-bg: #fef2de;
    --success-bg: #e3f6ea;
    --shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
    --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);
}

/* Reset */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; overflow: hidden; }
body {
    font-family: var(--font-sans);
    background: var(--bg-primary);
    color: var(--text-primary);
    font-size: 14px;
    line-height: 1.5;
}

/* Layout principal */
#app {
    display: grid;
    grid-template-columns: var(--sidebar-width) 1fr 320px;
    grid-template-rows: 48px 1fr 32px;
    grid-template-areas:
        "header header header"
        "sidebar viewer panel"
        "footer footer footer";
    height: 100vh;
    width: 100vw;
}

/* Header */
.header {
    grid-area: header;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 var(--space-md);
    background: var(--bg-secondary);
    border-bottom: 1px solid var(--border);
    z-index: 100;
}
.header__logo {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    font-weight: 800;
    font-size: 17px;
}
/* Logo "BiPro by SAIT" (2026-09-26, cont.: cajetín completo recortado sin la
   franja en inglés -- ver .footer__brand para dónde fue esa franja) -- 2 <img>
   apiladas (variante clara/oscura real del diseño), se togglea por tema igual
   que el resto de los tokens de color: dark es la base, light es la excepción. */
.header__logo-img { height: 38px; width: auto; display: block; }
.header__logo-img--light { display: none; }
[data-theme="light"] .header__logo-img--dark { display: none; }
[data-theme="light"] .header__logo-img--light { display: block; }
.header__actions { display: flex; align-items: center; gap: var(--space-sm); }

/* Sidebar */
.sidebar {
    grid-area: sidebar;
    position: relative;
    background: var(--bg-secondary);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.sidebar__resize-handle {
    position: absolute;
    top: 0; right: 0; bottom: 0;
    width: 6px;
    cursor: col-resize;
    z-index: 50;
}
.sidebar__resize-handle:hover,
.sidebar__resize-handle.dragging {
    background: var(--accent-border);
}
.sidebar__tabs {
    display: flex;
    border-bottom: 1px solid var(--border);
}
.sidebar__tab {
    flex: 1;
    padding: var(--space-sm) var(--space-xs);
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 12px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    transition: var(--transition);
    border-bottom: 2px solid transparent;
}
.sidebar__tab:hover { color: var(--text-secondary); }
.sidebar__tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}
.sidebar__content {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-md);
}

.auth-login__lead {
    margin-top: var(--space-lg);
    padding-top: var(--space-lg);
    border-top: 1px solid var(--border-light);
}

/* Pestañas internas dentro de un panel (ej. Plantas/Altura/Disciplina y
   tipo/Vistas guardadas) — mismo tratamiento visual que .sidebar__tab
   (subrayado --accent) pero sin ancho fijo por pestaña: si no entran todas,
   la tira se desplaza horizontalmente en vez de achicarlas. */
.subtabs {
    display: flex;
    overflow-x: auto;
    border-bottom: 1px solid var(--border);
    margin-bottom: var(--space-md);
}
.subtab {
    flex: 0 0 auto;
    white-space: nowrap;
    padding: var(--space-sm) var(--space-sm);
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 12px;
    font-weight: 500;
    transition: var(--transition);
    border-bottom: 2px solid transparent;
}
.subtab:hover { color: var(--text-secondary); }
.subtab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* Viewer 3D */
.viewer {
    grid-area: viewer;
    position: relative;
    overflow: hidden;
    background: var(--bg-primary);
}
.viewer canvas { display: block; width: 100%; height: 100%; }
.viewer__overlay {
    position: absolute;
    top: var(--space-md);
    left: var(--space-md);
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    z-index: 10;
}
.viewer__loading {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: rgba(15,23,42,0.9);
    z-index: 50;
    gap: var(--space-md);
}
.viewer__loading.hidden { display: none; }
.spinner {
    width: 40px; height: 40px;
    border: 3px solid var(--border);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Panel derecho (issues/clashes) */
.panel {
    grid-area: panel;
    background: var(--bg-secondary);
    border-left: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.panel__header {
    padding: var(--space-md);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.panel__title { font-weight: 600; font-size: 14px; }
.panel__body {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-md);
}

/* Footer / Status bar */
.footer {
    grid-area: footer;
    display: grid;
    /* 2026-09-26 cont.: grid en vez de flex space-between -- necesita una 3ra
       columna centrada de verdad para .footer__brand, no aproximada por los
       huecos que deja space-between entre 3 hijos de ancho desigual. */
    grid-template-columns: auto 1fr auto;
    align-items: center;
    padding: 0 var(--space-md);
    background: var(--bg-secondary);
    border-top: 1px solid var(--border);
    font-size: 12px;
    color: var(--text-muted);
}
/* Franja "BIM Team Coordination Platform" (2026-09-26 cont.) -- viene de la
   franja inferior del cajetín del header (ver header__logo-img), con el mismo
   motivo de línea-cota + marcas del kit de marca. Se oculta por debajo de
   1024px junto con el resto del ajuste de tablet (ver ese media query) -- el
   footer ya tiene info funcional en uso, no debe competirle espacio angosto. */
.footer__brand {
    grid-column: 2;
    justify-self: center;
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--font-mono);
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
    white-space: nowrap;
}
.footer__brand-line { width: 24px; height: 10px; flex: none; }

/* Botones */
.btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    padding: var(--space-xs) var(--space-md);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-tertiary);
    color: var(--text-primary);
    cursor: pointer;
    font-size: 13px;
    font-weight: 500;
    transition: var(--transition);
    white-space: nowrap;
}
.btn:hover { background: var(--bg-hover); }
.btn--primary {
    background: var(--accent-cta);
    border-color: var(--accent-cta);
    color: var(--accent-cta-ink);
}
.btn--primary:hover { background: var(--accent-cta-hover); }
/* Acción única y central (ej. "Detectar Colisiones" del header) — distinta
   del dorado --accent-cta, que queda reservado para el grupo de acciones
   secundarias (Informe/Flujo/BCF, todas con el mismo peso entre sí). */
.btn--accent {
    background: var(--accent-solid);
    border-color: var(--accent-solid);
    color: #fff;
}
.btn--accent:hover { filter: brightness(0.92); }
.btn--danger {
    background: var(--danger);
    border-color: var(--danger);
    color: white;
}
.btn--sm { padding: 2px var(--space-sm); font-size: 12px; }
.btn--icon { padding: var(--space-xs); min-width: 32px; justify-content: center; }
/* Pill "activo" dentro de un grupo pequeño de opciones (ej. Colisiones/Issues
   del panel derecho) — mismo tratamiento que los grupos de navegación del
   header (fondo --bg-hover, sin cambiar de color de texto). */
.btn.active { background: var(--bg-hover); }

/* Cards / Items */
.card {
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-md);
    margin-bottom: var(--space-sm);
    cursor: pointer;
    transition: var(--transition);
}
.card:hover { border-color: var(--accent); }
.card.active { border-color: var(--accent); background: var(--accent-wash); }
.card__header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: var(--space-xs);
}
.card__title { font-weight: 600; font-size: 13px; }
.card__subtitle { font-size: 12px; color: var(--text-secondary); }

/* Badges / Tags */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: var(--radius-full);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.badge--open { background: rgba(74,158,218,0.15); color: var(--info); }
.badge--in-progress { background: var(--warning-bg); color: var(--warning); }
.badge--resolved { background: var(--success-bg); color: var(--success); }
.badge--closed { background: rgba(124,117,144,0.15); color: var(--text-muted); }
.badge--critical { background: var(--danger-bg); color: var(--danger); }
.badge--high { background: rgba(249,115,22,0.15); color: #f97316; }
.badge--medium { background: var(--warning-bg); color: var(--warning); }
.badge--low { background: var(--success-bg); color: var(--success); }
/* MARCA vs ISSUE (2026-10-01). Una marca (`_nr: false`) es un apunte de
   coordinación: puede requerir resolución (ej. "tumbar este muro") o ser solo
   referencia. El flujo de resolución las INCLUYE a propósito — antes de este
   cambio este archivo ni siquiera leía `_nr`, así que no podía distinguirlas.
   Issue = Colisión (rojo, es lo que hay que resolver sí o sí). */
.badge--issue { background: var(--danger-bg); color: var(--danger); }
.badge--marca { background: var(--accent-wash); color: var(--accent); }

/* Panel derecho colapsable (más espacio para el visor 3D) */
#app.panel-collapsed {
    grid-template-columns: var(--sidebar-width) 1fr 0;
}
#app.panel-collapsed .panel { display: none; }

/* Sidebar izquierdo colapsable (pedido 2026-09-07): al entrar a un modelo en
   SAITClashPro (que ya tiene su propio riel de paneles — Modelos/
   Visualización/Colisiones/etc.) el sidebar de 360 queda redundante y solo
   resta ancho al visor. Se colapsa solo (ver openInClashPro() en app.js) —
   a diferencia del panel derecho (colapsado permanente, sin botón), este SÍ
   tiene un botón de reapertura (`#sidebar-reopen`) porque Proyectos/Config
   siguen viviendo solo acá. */
#app.sidebar-collapsed {
    grid-template-columns: 0 1fr 320px;
}
#app.sidebar-collapsed.panel-collapsed {
    grid-template-columns: 0 1fr 0;
}
#app.sidebar-collapsed .sidebar { padding: 0; border: none; }
#app:not(.sidebar-collapsed) #sidebar-reopen { display: none; }
#sidebar-reopen {
    position: absolute;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    z-index: 15;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-left: none;
    border-radius: 0 8px 8px 0;
    color: var(--text-muted);
    width: 20px;
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
#sidebar-reopen:hover { color: var(--accent); border-color: var(--accent-border); }

/* Contraparte de #sidebar-reopen: cierra el sidebar desde adentro (pedido
   2026-09-07 — antes solo existía la flecha para reabrirlo, no había forma
   de volver a colapsarlo a mano una vez reabierto). */
#sidebar-close {
    margin-left: auto;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text-muted);
    width: 28px;
    align-self: center;
    cursor: pointer;
}
#sidebar-close:hover { color: var(--accent); border-color: var(--accent-border); }

/* Tooltip del visor: alias del modelo + elemento bajo el cursor */
.viewer-tooltip {
    position: absolute;
    z-index: 20;
    pointer-events: none;
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 4px 10px;
    font-size: 12px;
    max-width: 320px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.viewer-tooltip.hidden { display: none; }
.viewer-tooltip__model { color: var(--accent); font-weight: 600; }

/* Barra de progreso (subida de IFC) */
.progress {
    height: 6px;
    background: var(--bg-tertiary);
    border-radius: 3px;
    overflow: hidden;
}
.progress__bar {
    height: 100%;
    width: 0%;
    background: var(--accent-solid);
    border-radius: 3px;
    transition: width 0.2s ease;
}

/* Inputs */
.input, .select, .textarea {
    width: 100%;
    padding: var(--space-sm) var(--space-md);
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text-primary);
    font-size: 13px;
    font-family: inherit;
    transition: var(--transition);
}
.input:focus, .select:focus, .textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-wash);
}
.textarea { resize: vertical; min-height: 80px; }
.select { cursor: pointer; }

/* Drop zone */
.dropzone {
    border: 2px dashed var(--border-light);
    border-radius: var(--radius-lg);
    padding: var(--space-xl);
    text-align: center;
    color: var(--text-muted);
    transition: var(--transition);
    cursor: pointer;
}
.dropzone:hover, .dropzone.active {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
}
.dropzone svg { width: 48px; height: 48px; margin-bottom: var(--space-md); opacity: 0.5; }
.dropzone__text { font-size: 14px; margin-bottom: var(--space-xs); }
.dropzone__hint { font-size: 12px; }

/* Model list */
.model-item {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: var(--space-sm) var(--space-md);
    border-radius: var(--radius);
    cursor: pointer;
    transition: var(--transition);
}
.model-item:hover { background: var(--bg-tertiary); }
.model-item__color {
    width: 12px; height: 12px;
    border-radius: 50%;
    flex-shrink: 0;
}
.model-item__name { flex: 1; font-size: 13px; }
.model-item__toggle {
    width: 24px; height: 24px;
    background: none; border: none;
    color: var(--text-muted);
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
.model-item__toggle:hover { color: var(--text-primary); }

/* Empty state */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: var(--space-xl);
    color: var(--text-muted);
    text-align: center;
}
.empty-state svg { width: 64px; height: 64px; margin-bottom: var(--space-md); opacity: 0.3; }
.empty-state__title { font-size: 16px; font-weight: 600; margin-bottom: var(--space-xs); color: var(--text-secondary); }
.empty-state__text { font-size: 13px; max-width: 280px; }

/* Modal */
.modal-backdrop {
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.6);
    display: flex; align-items: center; justify-content: center;
    z-index: 1000;
    backdrop-filter: blur(4px);
}
.modal-backdrop.hidden { display: none; }
.modal {
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-lg);
    max-width: 500px;
    width: 90%;
    max-height: 80vh;
    overflow-y: auto;
    box-shadow: var(--shadow-lg);
}
.modal__title { font-size: 18px; font-weight: 700; margin-bottom: var(--space-md); }
.modal__actions { display: flex; justify-content: flex-end; gap: var(--space-sm); margin-top: var(--space-lg); }

/* Visor SAITClashPro embebido de forma permanente dentro de .viewer (grid
   area), no como modal/overlay — ancla también la barra flotante de captura
   (#clashpro-capture-bar). */
.viewer__frame {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: none;
    background: #0f172a;
}

#welcome-panel {
    position: absolute;
    inset: 0;
    z-index: 5;
    background: var(--bg-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow-y: auto;
    padding: var(--space-xl);
}
.welcome-panel__inner {
    /* 2026-09-26: 720px -> 860px -- el bloque nuevo "welcome-pitch" tiene
       texto más largo que las tarjetas del feature-grid, se sentía apretado
       en 3 columnas a 720px (ver comentario en .welcome-pitch__grid). */
    max-width: 860px;
    width: 100%;
    text-align: center;
}
/* Logo completo "cajetín" del panel de bienvenida (2026-09-26, pedido explícito)
   -- reemplaza el eyebrow de texto suelto ("BiPro") de antes: la imagen ya trae
   isotipo + nombre completo + frase en inglés integrados, sin duplicar texto. */
.welcome-panel__lockup { display: block; width: 100%; max-width: 340px; height: auto; margin: 0 auto var(--space-lg); }
.welcome-panel__lockup--light { display: none; }
[data-theme="light"] .welcome-panel__lockup--dark { display: none; }
[data-theme="light"] .welcome-panel__lockup--light { display: block; }
.welcome-panel__title {
    font-size: 24px;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0 0 var(--space-xl);
}
.feature-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-md);
    text-align: left;
}
@media (max-width: 1024px) {
    .feature-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
    .feature-grid { grid-template-columns: 1fr; }
}
.feature-card {
    background: var(--bg-tertiary);
    border: 1px solid var(--border-light);
    border-radius: var(--radius);
    padding: var(--space-md);
}
.feature-card__icon { font-size: 20px; margin-bottom: var(--space-sm); }
.feature-card__title {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--accent);
    margin-bottom: 4px;
}
.feature-card__text {
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.4;
}

/* "Qué te lleva" (2026-09-26): ficha comercial condensada en la pantalla de
   entrada, visible solo sin sesión (ver toggle en updateAuthUI(), app.js).
   Visualmente más "denso"/informativo que el feature-grid de arriba a
   propósito -- ese es el gancho rápido, esto es la propuesta completa para
   quien ya se quedó leyendo. */
.welcome-pitch {
    text-align: left;
    margin-top: var(--space-xl);
    padding-top: var(--space-xl);
    border-top: 1px solid var(--border-light);
}
.welcome-pitch__grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-lg);
}
@media (max-width: 1024px) {
    .welcome-pitch__grid { grid-template-columns: 1fr; }
}
.welcome-pitch__label {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent-cta);
    margin-bottom: var(--space-sm);
}
.welcome-pitch__list {
    margin: 0;
    padding-left: 18px;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--text-secondary);
}
.welcome-pitch__list li { margin-bottom: 6px; }
.welcome-pitch__list li:last-child { margin-bottom: 0; }
.welcome-pitch > .btn {
    display: block;
    margin-left: auto;
    margin-right: auto;
    width: fit-content;
}

/* Imagen corporativa del modal "Solicitar acceso" (2026-09-26, pedido explícito
   -- antes era una ventana de puro texto plano, sin marca visible). */
.lead-modal__brand { display: flex; justify-content: center; margin-bottom: var(--space-md); }
.lead-modal__brand-img { width: 100%; max-width: 280px; height: auto; display: block; }
.lead-modal__brand-img--light { display: none; }
[data-theme="light"] .lead-modal__brand-img--dark { display: none; }
[data-theme="light"] .lead-modal__brand-img--light { display: block; }

/* Ficha comercial condensada del modal "Solicitar acceso" (2026-09-26). */
.lead-modal__pitch {
    font-size: 13px;
    line-height: 1.55;
    color: var(--text-secondary);
    background: var(--bg-tertiary);
    border: 1px solid var(--border-light);
    border-radius: var(--radius);
    padding: var(--space-sm) var(--space-md);
}

/* Secciones con encabezado (rediseño): agrupan campos relacionados dentro de
   un modal o panel largo — mismo patrón reutilizado en Modal Issue y en el
   panel Proyectos. */
.modal__section { margin-bottom: var(--space-lg); }
.modal__section:last-child { margin-bottom: 0; }
.modal__section-title {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: var(--space-sm);
}
.modal__section-body {
    background: var(--bg-tertiary);
    border: 1px solid var(--border-light);
    border-radius: var(--radius);
    padding: var(--space-md);
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
}

/* Utilities */
.hidden { display: none !important; }
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-xs { gap: var(--space-xs); }
.gap-sm { gap: var(--space-sm); }
.gap-md { gap: var(--space-md); }
.mt-sm { margin-top: var(--space-sm); }
.mt-md { margin-top: var(--space-md); }
.mb-sm { margin-bottom: var(--space-sm); }
.mb-md { margin-bottom: var(--space-md); }
.text-xs { font-size: 11px; }
.text-sm { font-size: 12px; }
.text-muted { color: var(--text-muted); }
.text-center { text-align: center; }
.w-full { width: 100%; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Scrollbar */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-light); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }

/* Responsive - tablets */
@media (max-width: 1024px) {
    #app {
        grid-template-columns: 1fr;
        grid-template-rows: 48px 1fr 200px 32px;
        grid-template-areas:
            "header"
            "viewer"
            "panel"
            "footer";
    }
    /* Sin ancho para sidebar+viewer+panel lado a lado: la sidebar (Proyectos,
       login, "Solicitar acceso") pasa a superponerse en pantalla completa
       mientras no hay un modelo abierto. Reutiliza el mismo estado
       #app.sidebar-collapsed que en escritorio colapsa el ancho a 0 al abrir
       un modelo (ver comentario arriba) — acá, en vez de achicar el ancho,
       oculta el overlay entero. Bug real 2026-09-17: antes esta regla
       ocultaba .sidebar sin condición, dejando a un visitante en celular sin
       forma de ver el login/"Solicitar acceso" (el botón #sidebar-reopen
       tampoco aparece si no está collapsed, ver línea ~382). */
    .sidebar {
        display: flex;
        position: fixed;
        inset: 48px 0 32px 0;
        width: auto;
        z-index: 200;
        border-right: none;
    }
    #app.sidebar-collapsed .sidebar { display: none; }
    .panel { border-left: none; border-top: 1px solid var(--border); }
    /* #sidebar-reopen a 20x56px (pensado para mouse) es casi imposible de
       ver/tocar en una pantalla táctil — bug real 2026-09-18, el usuario no
       encontraba cómo volver a mostrar el sidebar tras cerrarlo con la
       flechita. Objetivo táctil más grande + sombra para que se note que es
       un botón, no un adorno del borde. */
    #sidebar-reopen {
        width: 40px;
        height: 76px;
        font-size: 18px;
        box-shadow: 2px 0 8px rgba(0,0,0,0.25);
    }
    /* Footer con info funcional (modelos/elementos/colisiones/sync) ya ajustado
       en este ancho -- la franja de marca no debe competirle espacio (pedido
       explícito de revisar esto antes de implementar, ver BITACORA). */
    .footer__brand { display: none; }
}

/* Responsive - móvil */
@media (max-width: 640px) {
    #app {
        grid-template-rows: 44px 1fr 180px 28px;
    }
    /* 2026-09-26: el wordmark completo ("BiPro by SAIT") no entra cómodo en
       un header de 44px de alto en celular -- se cambia al isotipo "o" solo
       (mark-o). El toggle claro/oscuro de qué <img> se MUESTRA ya lo
       resuelven las reglas de fuera de este media query (no se tocan acá) --
       esto solo cambia la IMAGEN (`content`) de cada uno y el alto.
       Reemplaza la regla vieja ".header__logo span" (apuntaba a los <span>
       de texto que ya no existen, ver logo nuevo). */
    .header__logo-img { height: 24px; }
    .header__logo-img--dark { content: url('../icons/mark-o-dark.svg?v=2.0.0'); }
    .header__logo-img--light { content: url('../icons/mark-o.svg?v=2.0.0'); }
    .btn span.btn-label { display: none; }
    .sidebar { inset: 44px 0 28px 0; }
}

/* Toasts (feedback no bloqueante) */
#toast-container {
    position: fixed;
    bottom: 48px;
    right: 16px;
    z-index: 9999;
    display: flex;
    flex-direction: column;
    gap: 8px;
    pointer-events: none;
}
.toast {
    padding: 10px 16px;
    border-radius: 8px;
    font-size: 13px;
    color: var(--text-primary);
    background: var(--bg-secondary);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    opacity: 0;
    transform: translateX(20px);
    transition: opacity 0.3s, transform 0.3s;
    max-width: 320px;
}
.toast--visible { opacity: 1; transform: translateX(0); }
.toast--success { border-left-color: var(--success); }
.toast--warning { border-left-color: var(--warning); }
.toast--error   { border-left-color: var(--danger); }
