
  :root {
    --tinta:#2D2D2D; --acento:#F5C800; --dato:#1E7BC4; --verde:#2E9C3A;
    --bg:#0f1113; --panel:#16191D; --panel-2:#23272c; --linea:#2C3038;
    --txt:#E9E6E1; --txt-soft:#C9CDD3; --txt-mute:#8F959D;
    /* El oro OSCURO de la marca (bima_web «amarillo-2»). Es el que se lee sobre
       el panel: --acento a 11 px vibra. Va aquí y no como var(--bima-amarillo-2)
       porque `bima_web.inyectar` NO trae la hoja de paleta al visor —se comprobó
       el 30-ago-2026: los tres `var(--bima-…)` que la plantilla ya usaba sin
       valor de respaldo no pintan nada—. Esta es la capa de tokens del visor. */
    --acento-2:#D8A800;
  }
  * { box-sizing:border-box; }
  html,body { margin:0; height:100%; }
  body {
    background:var(--bg); color:var(--txt); overflow:hidden;
    font-family:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  }
  a { color:inherit; text-decoration:none; }
  #app { position:fixed; inset:0; display:flex; flex-direction:column; }

  /* ── Barra superior ── */
  header {
    display:flex; align-items:center; gap:16px; padding:0 16px; height:56px;
    background:var(--tinta); border-bottom:3px solid var(--acento); z-index:20; flex:none;
  }
  header .volver {
    color:#C9CDD3; font-weight:600; font-size:13.5px; padding-right:12px;
    border-right:1px solid rgba(255,255,255,.14);
  }
  header .volver:hover { color:var(--acento); }
  header .brand { display:flex; align-items:center; gap:9px; }
  /* Los aliados van AL LADO del logo de BIM y a la misma altura óptica, con un
     filete de separación: BIM presente pero no protagonista (regla 17). El
     ancho es libre y la altura fija — un escudo municipal es casi cuadrado y un
     logotipo universitario es apaisado; forzar el ancho aplasta uno de los dos. */
  header .aliados { display:inline-flex; align-items:center; gap:9px; }
  header .aliados:not(:empty) { padding-left:9px; margin-left:1px;
      border-left:1px solid rgba(255,255,255,.16); }
  header .aliados img { height:1.45rem; width:auto; display:block; opacity:.95; }
  header .al-mapa { display:inline-flex; align-items:center; gap:5px; margin-left:auto;
      text-decoration:none; color:var(--txt-soft); font-size:.82rem;
      padding:.32rem .6rem; border:1px solid rgba(255,255,255,.16); border-radius:8px; }
  header .al-mapa:hover { color:var(--acento); border-color:rgba(255,255,255,.3); }
  header .al-mapa .i { width:15px; height:15px; }
  @media (max-width:700px) { header .aliados img { height:1.15rem } header .al-mapa span { display:none } }
  header .logo { height:26px; width:auto; display:block; }
  header .ttl { font-weight:800; font-size:15px; letter-spacing:.01em; white-space:nowrap; }
  header .ttl small { display:block; font-weight:500; font-size:11px; color:#8F959D; letter-spacing:.02em; white-space:nowrap; }
  header .sep { flex:1; }
  .btn {
    display:inline-flex; align-items:center; gap:7px; font:inherit; font-size:13.5px; font-weight:600;
    border:1px solid var(--linea); background:var(--panel-2); color:var(--txt);
    padding:8px 14px; border-radius:9px; cursor:pointer; white-space:nowrap;
  }
  .btn:hover { border-color:#8F959D; }
  .btn.on { background:var(--acento); color:#111; border-color:var(--acento); }
  .btn.primary { background:var(--acento); color:var(--tinta); border-color:var(--acento); }
  .btn.primary:hover { filter:brightness(1.05); }
  .btn:disabled { opacity:.45; cursor:not-allowed; }

  /* ── Estadísticas ── */
  .stats { display:flex; gap:18px; align-items:center; }
  .stat { text-align:right; line-height:1.1; }
  .stat .n { font-weight:800; font-size:15px; font-variant-numeric:tabular-nums; }
  .stat .l { font-size:10px; text-transform:uppercase; letter-spacing:.08em; color:var(--txt-mute); }
  /* En pantalla angosta se va lo TECNICO y se queda el nombre de la obra.
     El `!important` no es capricho: `cargarIFC` hace `elStats.style.display =
     "flex"` EN LINEA al terminar de cargar, y un estilo en linea le gana a un
     media query. Medido el 30-ago-2026 en un celular de 375 px: el encabezado
     decia «257 ELEMENTOS · 69.426 TRIANGULOS» y NO decia «Casa Olga Yaneth».
     A una clienta el conteo de triangulos no le dice nada; el nombre de su
     casa, si. */
  @media (max-width:760px){ .stats { display:none !important; } header .ttl small { display:none; } }

  /* ── Íconos SVG de línea (premium, monocromo) ── */
  .i { width:17px; height:17px; flex:none; stroke:currentColor; fill:none;
       stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
  .btn.ic { gap:6px; }
  header .volver { display:inline-flex; align-items:center; gap:5px; }
  header .volver .i { width:15px; height:15px; }
  header .brand { min-width:0; }               /* deja truncar el título si falta espacio */
  header .ttl { overflow:hidden; text-overflow:ellipsis; }

  /* ── Barra de herramientas + menú ⋯ ── */
  .toolbar { display:flex; align-items:center; gap:8px; }
  .more-wrap { position:relative; display:inline-flex; }
  .more-menu { position:absolute; top:calc(100% + 9px); right:0; z-index:40; display:none;
    flex-direction:column; gap:3px; min-width:196px; padding:7px;
    background:var(--panel); border:1px solid var(--linea); border-radius:12px;
    box-shadow:0 20px 44px -16px rgba(0,0,0,.9); }
  .more-menu.show { display:flex; }
  .more-menu .btn.mi { width:100%; justify-content:flex-start; background:transparent; border-color:transparent; padding:9px 11px; }
  .more-menu .btn.mi:hover { background:var(--panel-2); border-color:var(--linea); }
  .more-menu .btn.mi.on { background:var(--acento); color:#111; border-color:var(--acento); }

  /* Responsive: en pantallas angostas, botones principales solo-ícono (nada se pierde;
     lo secundario vive en el menú ⋯). El logo y el ⋯ siempre visibles. */
  @media (max-width:1100px){ .toolbar .btn.ic span, #btn-open span, header .volver span { display:none; }
    .toolbar .btn.ic, #btn-open { padding:9px 11px; } }
  @media (max-width:600px){ header { gap:10px; padding:0 10px; } header .ttl { font-size:13px; } }
  /* El titulo YA NO se esconde en celular angosto: se escondia para dejarle
     sitio a las cifras, y las cifras acaban de irse. Si no cabe, se corta con
     puntos suspensivos (linea 95), que es mejor que desaparecer. */

  /* ── Escena ── */
  #escena { position:relative; flex:1; min-height:0; }
  #canvas-wrap { position:absolute; inset:0; }
  canvas { display:block; width:100%; height:100%; }

  /* ── Zona de arrastre ── */
  #drop {
    position:absolute; inset:0; z-index:10; display:flex; flex-direction:column;
    align-items:center; justify-content:center; text-align:center; padding:24px;
    background:radial-gradient(circle at 50% 40%, #16191d 0%, #0f1113 70%);
    transition:background .2s ease;
  }
  #drop.hide { display:none; }
  #drop.drag { background:radial-gradient(circle at 50% 40%, var(--bima-bien-fondo) 0%, #0f1113 70%); }
  #drop .ico { font-size:56px; margin-bottom:8px; filter:grayscale(.1); }
  #drop h2 { margin:0 0 6px; font-size:22px; font-weight:800; }
  #drop p { margin:0; color:var(--txt-soft); font-size:14px; max-width:440px; line-height:1.5; }
  #drop .hint { margin-top:20px; font-size:12px; color:var(--txt-mute); }
  #drop .marca-tri { color:var(--acento); }

  /* ── Overlay de carga ── */
  #cargando {
    position:absolute; inset:0; z-index:15; display:none; flex-direction:column;
    align-items:center; justify-content:center; gap:16px; background:rgba(15,17,19,.86);
  }
  #cargando.show { display:flex; }
  /* MIENTRAS EL MODELO SE ARMA, LA CAPA SE APARTA.
     Es negra al 86 % y cubre todo el lienzo: dejarla puesta durante el armado
     progresivo taparía exactamente lo que el armado progresivo existe para
     enseñar. Deja de ser un telón y pasa a ser un rótulo abajo, con el modelo
     creciendo detrás. `pointer-events:none` para que no se coma un clic. */
  #cargando.armando { background:transparent; justify-content:flex-end;
    padding-bottom:104px; gap:10px; pointer-events:none; }
  #cargando.armando .spinner { width:20px; height:20px; border-width:3px; }
  #cargando.armando .msg { background:rgba(15,17,19,.82); backdrop-filter:blur(8px);
    padding:7px 15px; border-radius:99px; font-size:12.5px;
    border:1px solid rgba(255,255,255,.12); }
  .spinner {
    width:44px; height:44px; border-radius:50%;
    border:4px solid var(--panel-2); border-top-color:var(--acento); animation:spin 1s linear infinite;
  }
  @keyframes spin { to { transform:rotate(360deg); } }
  #cargando .msg { font-size:14px; color:var(--txt-soft); }

  /* ── Aviso de error ── */
  #error {
    position:absolute; left:50%; bottom:20px; transform:translateX(-50%); z-index:16;
    display:none; max-width:520px; background:var(--bima-grave-fondo); border:1px solid var(--bima-grave-linea);
    color:var(--bima-grave-ink); padding:12px 16px; border-radius:10px; font-size:13px; line-height:1.45;
  }
  #error.show { display:block; }
  #error b { color:#fff; }

  /* ── Aviso del render de precisión ──
     Abajo a la derecha y no centrado como el error: no interrumpe, acompaña.
     El trabajo tarda minutos y el usuario sigue navegando mientras tanto. */
  #rpx-aviso {
    position:absolute; right:16px; bottom:20px; z-index:16; display:none;
    max-width:340px; background:#2d2d2dee; border:1px solid #f5c80055;
    color:#eee; padding:12px 14px; border-radius:10px; font-size:13px; line-height:1.5;
    box-shadow:0 8px 24px #0007;
  }
  #rpx-aviso.show { display:block; }
  #rpx-aviso b { color:#f5c800; }
  #rpx-aviso img { max-width:100%; margin-top:8px; border-radius:6px; display:block; }
  #rpx-aviso a { color:#f5c800; }
  #rpx-aviso .x { float:right; cursor:pointer; opacity:.6; margin-left:8px; }

  /* ── Panel de propiedades ── */
  #panel {
    position:absolute; top:0; right:0; bottom:0; width:340px; max-width:86vw; z-index:14;
    background:var(--panel); border-left:1px solid var(--linea);
    transform:translateX(100%); transition:transform .24s ease;
    display:flex; flex-direction:column; box-shadow:-12px 0 30px -18px rgba(0,0,0,.8);
  }
  #panel.show { transform:translateX(0); }
  #panel .p-head {
    display:flex; align-items:center; gap:10px; padding:14px 16px;
    border-bottom:1px solid var(--linea); background:var(--panel-2);
  }
  #panel .p-head .clase {
    flex:1; min-width:0; font-weight:800; font-size:14px; color:var(--acento);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  #panel .p-close {
    border:0; background:transparent; color:var(--txt-mute); font-size:20px; cursor:pointer;
    line-height:1; padding:2px 6px; border-radius:6px;
  }
  #panel .p-close:hover { color:var(--txt); background:var(--linea); }
  /* Minimizado: queda solo la cabecera, como en las ventanas del gestor. */
  #panel.wm-mini > :not(.p-head) { display:none; }
  #panel.wm-mini { height:auto; bottom:auto; }
  #panel .p-body { flex:1; overflow-y:auto; padding:6px 0 20px; }

  /* ── Marcado del elemento (escritura sobre el modelo) ──
     Vive DENTRO del panel y debajo de las propiedades a propósito: primero se
     lee lo que el modelo dice, y después se le agrega lo que uno sabe. Al
     revés invita a escribir sin haber mirado. */
  #p-marcas { border-top:1px solid var(--linea); background:var(--panel-2); padding:12px 16px 16px; }
  #p-marcas[hidden] { display:none; }
  #p-marcas .m-tit { font-weight:800; font-size:11.5px; letter-spacing:.4px;
    text-transform:uppercase; color:var(--txt-mute); margin-bottom:10px;
    display:flex; align-items:center; gap:7px; }
  #p-marcas .m-tit .pt { width:7px; height:7px; border-radius:50%; background:var(--linea); flex:0 0 auto; }
  #p-marcas.tiene .m-tit .pt { background:var(--acento); }
  #p-marcas label { display:block; font-size:10.5px; font-weight:700; color:var(--txt-mute);
    margin:8px 0 3px; letter-spacing:.2px; }
  #p-marcas select, #p-marcas input, #p-marcas textarea {
    width:100%; box-sizing:border-box; font:inherit; font-size:12.5px;
    background:var(--panel); color:var(--txt); border:1px solid var(--linea);
    border-radius:7px; padding:6px 8px; }
  #p-marcas textarea { resize:vertical; min-height:52px; line-height:1.35; }
  #p-marcas select:focus, #p-marcas input:focus, #p-marcas textarea:focus {
    outline:none; border-color:var(--acento); }
  #p-marcas .m-pie { display:flex; gap:7px; margin-top:12px; }
  #p-marcas .m-pie button { flex:1; font:inherit; font-size:12px; font-weight:700; cursor:pointer;
    border:1px solid var(--linea); background:var(--panel); color:var(--txt);
    border-radius:8px; padding:7px 6px; }
  #p-marcas .m-pie button.principal { background:var(--acento); color:#111; border-color:var(--acento); }
  #p-marcas .m-pie button:disabled { opacity:.45; cursor:default; }
  #p-marcas .m-aviso { font-size:10.5px; line-height:1.4; color:var(--txt-mute); margin-top:9px; }
  #p-marcas .m-aviso.malo { color:#ff8a80; }
  /* La firma de quién marcó y cuándo. No es decoración: es lo que permite
     preguntarle a alguien qué quiso decir, seis meses después. */
  #p-marcas .m-firma { font-size:10px; color:var(--txt-mute); margin-top:8px; font-style:italic; }
  /* Invitacion a entrar, cuando hay Hub detras pero no hay sesion. */
  #m-entrar[hidden] { display:none; }
  #m-entrar p { margin:0 0 10px; font-size:11.5px; line-height:1.45; color:var(--txt-mute); }
  #m-entrar a { display:block; text-align:center; font-weight:700; font-size:12px;
    background:var(--acento); color:#111; border-radius:8px; padding:8px 6px;
    text-decoration:none; }
  #m-entrar a:hover { filter:brightness(1.08); }
  /* Lo que la partida escogida declara: unidad, cantidad y valor unitario.
     Va DEBAJO del desplegable y en gris: es contexto para decidir, no un dato
     del elemento. Confundirlos haria creer que ese muro vale ese unitario. */
  #m-partida-det { font-size:10.5px; line-height:1.45; color:var(--txt-mute);
    margin-top:5px; }
  #m-partida-det b { color:var(--txt); font-weight:700; }
  #m-partida-det .cot { color:var(--acento); font-weight:700; }
  #panel .grupo { padding:12px 16px 4px; }
  #panel .grupo h4 {
    margin:0 0 8px; font-size:10.5px; letter-spacing:.1em; text-transform:uppercase; color:var(--txt-mute);
  }
  #panel .fila {
    display:flex; gap:10px; padding:5px 0; border-bottom:1px solid rgba(255,255,255,.045); font-size:12.5px;
  }
  #panel .fila:last-child { border-bottom:0; }
  #panel .fila .k { color:var(--txt-mute); flex:none; width:120px; }
  #panel .fila .v { color:var(--txt); word-break:break-word; font-variant-numeric:tabular-nums; }
  #panel .vacio { padding:12px 16px; color:var(--txt-mute); font-size:12.5px; }
  #panel .pset-nom { color:var(--dato); font-weight:700; font-size:12px; margin:10px 0 2px; }

  /* ── Ayuda de controles ── */
  .ayuda {
    position:absolute; left:14px; bottom:12px; z-index:12; font-size:11.5px; color:var(--txt-mute);
    background:rgba(27,30,34,.7); border:1px solid var(--linea); border-radius:8px; padding:7px 11px;
    line-height:1.6; pointer-events:none;
  }
  .ayuda b { color:var(--txt-soft); }
  @media (prefers-reduced-motion: reduce){ .spinner { animation:none; } }

  /* ── Panel de herramientas (izquierda): sección + capas ── */
  #tools {
    position:absolute; left:0; top:0; bottom:0; width:266px; z-index:14;
    background:var(--panel); border-right:1px solid var(--linea);
    transform:translateX(-100%); transition:transform .22s ease; overflow-y:auto;
    display:flex; flex-direction:column;
  }
  #tools.show { transform:none; }
  #tools .t-sec { border-bottom:1px solid var(--linea); }
  #tools .t-head {
    display:flex; align-items:center; justify-content:space-between; gap:8px;
    padding:12px 14px; font-weight:800; font-size:12.5px; letter-spacing:.02em;
    color:var(--txt-soft); text-transform:uppercase; background:var(--panel-2);
  }
  #tools .t-body { padding:10px 14px 14px; }
  #tools .t-empty { color:var(--txt-mute); font-size:12px; }
  #tools .t-axis { display:grid; grid-template-columns:16px 1fr 1fr; gap:8px; align-items:center; margin:7px 0; }
  #tools .t-axis > span { color:var(--acento); font-weight:800; font-size:12px; }
  #tools input[type=range] { width:100%; accent-color:var(--acento); height:3px; }
  #tools #sec-body { opacity:.4; pointer-events:none; transition:opacity .2s; }
  #tools #sec-body.on { opacity:1; pointer-events:auto; }
  /* switch pequeño */
  .t-sw { position:relative; width:34px; height:19px; flex:none; }
  .t-sw input { opacity:0; width:0; height:0; position:absolute; }
  .t-sw span { position:absolute; inset:0; background:#2c3038; border-radius:99px; transition:.2s; }
  .t-sw span::before { content:""; position:absolute; top:2px; left:2px; width:15px; height:15px; border-radius:50%; background:#8F959D; transition:.2s; }
  .t-sw input:checked + span { background:color-mix(in srgb, var(--acento) 40%, #2c3038); }
  .t-sw input:checked + span::before { transform:translateX(15px); background:var(--acento); }
  /* fila de capa */
  .capa { display:grid; grid-template-columns:1fr auto auto; gap:8px; align-items:center; padding:5px 0; font-size:12.5px; border-bottom:1px solid rgba(255,255,255,.04); }
  .capa:last-child { border-bottom:0; }
  .capa-grupo { color:var(--txt-mute); font-size:10.5px; font-weight:700; letter-spacing:.06em;
    padding:9px 0 3px; margin-top:4px; border-top:1px solid rgba(255,255,255,.06); overflow:hidden;
    text-overflow:ellipsis; white-space:nowrap; }
  .capa-grupo:first-of-type { border-top:0; margin-top:0; }
  .capa .cn { color:var(--txt); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .capa .cn small { color:var(--txt-mute); font-weight:400; }
  .capa button { border:1px solid var(--linea); background:var(--panel-2); color:var(--txt-mute); border-radius:6px; width:26px; height:22px; cursor:pointer; font-size:12px; padding:0; }
  .capa button.on { color:var(--tinta); background:var(--acento); border-color:var(--acento); }
  .capa button.off { opacity:.5; text-decoration:line-through; }

  /* ── Recorrido en primera persona (HUD) ── */
  #hud-fp { position:absolute; inset:0; z-index:16; pointer-events:none; }
  #hud-fp .cross {
    position:absolute; left:50%; top:50%; width:10px; height:10px; margin:-5px 0 0 -5px;
    border:2px solid rgba(245,200,0,.9); border-radius:50%; box-shadow:0 0 0 1px rgba(0,0,0,.5);
  }
  #hud-fp .hint-fp {
    position:absolute; left:50%; bottom:22px; transform:translateX(-50%);
    background:rgba(18,20,23,.86); color:var(--txt); padding:9px 15px; border-radius:22px;
    font-size:12.5px; border:1px solid var(--acento-linea, rgba(245,200,0,.3)); white-space:nowrap;
    -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px);
  }
  #hud-fp .hint-fp b { color:var(--acento); }
  /* Selector de niveles: a la izquierda, vertical y de abajo hacia arriba, que es
     como está el edificio. `pointer-events` se reactiva aquí porque el HUD entero
     los tiene apagados para no estorbar al recorrido. */
  #fp-niveles {
    position:absolute; left:14px; top:50%; transform:translateY(-50%);
    display:flex; flex-direction:column-reverse; gap:4px; pointer-events:auto;
    background:rgba(20,20,20,.72); padding:7px; border-radius:10px;
    border:1px solid rgba(255,255,255,.10); backdrop-filter:blur(6px);
  }
  #fp-niveles button {
    font:inherit; font-size:11px; font-weight:600; cursor:pointer; white-space:nowrap;
    border:1px solid rgba(255,255,255,.14); background:rgba(255,255,255,.05);
    color:var(--txt); border-radius:7px; padding:5px 9px; text-align:left;
  }
  #fp-niveles button:hover { border-color:var(--acento); color:var(--acento); }
  #fp-niveles button.actual { background:var(--acento); color:#111; border-color:var(--acento); }
  #fp-niveles .tecla {
    display:inline-block; min-width:15px; margin-right:7px; text-align:center;
    border-radius:4px; background:rgba(255,255,255,.13); padding:1px 3px;
    font-variant-numeric:tabular-nums; font-size:10px;
  }
  #fp-niveles button.actual .tecla { background:rgba(0,0,0,.22); }
  /* Con el ratón CAPTURADO por el 3D no hay cursor, así que estos botones no se
     pueden pulsar: se manejan con las teclas 1-9. Dejan de comportarse como
     botones para no invitar a un clic que no va a llegar. En táctil —iPad— no
     hay captura de puntero y siguen siendo pulsables de verdad. */
  #fp-niveles.solo-teclado button { cursor:default; }
  #fp-niveles.solo-teclado button:hover { border-color:rgba(255,255,255,.14); color:var(--txt); }
  #fp-niveles.solo-teclado button.actual:hover { border-color:var(--acento); color:#111; }

  /* ── Render IA (panel de resultado) ── */
  /* Render a FONDO VIVO: la foto ocupa toda la pantalla; controles flotan encima. */
  #render-ia { position:absolute; inset:0; z-index:30; background:#0F1113; display:none; }
  #render-ia.show { display:block; }
  /* Comparando: el panel se vuelve transparente y deja ver el 3D detrás, pero la
     barra de botones sigue viva — si el panel entero dejara de recibir clics no
     habría forma de volver al render. De ahí `pointer-events` apagado en el
     panel y encendido otra vez en la barra. */
  #render-ia.comparando { background:transparent; pointer-events:none; }
  #render-ia.comparando img { display:none !important; }
  #render-ia.comparando .msg { display:none !important; }
  #render-ia.comparando #render-barra { pointer-events:auto; }
  /* ── EL MARCO DE LA COMPARACIÓN ───────────────────────────────────────────
     Comparar es comparar el MISMO encuadre, y hasta el 27-ago-2026 no lo era:
     el render se dispara sobre un RECORTE 3:2 del canvas (`recortarAFoto`) —de
     84° horizontales a 70°, que es lo que hace que se lea como fotografía— y al
     comparar se enseñaba el canvas ENTERO, que es ~1,93:1. El 3D mostraba
     franjas laterales que el render no tiene, así que la casa salía más pequeña
     y desplazada. No era un desajuste de cámara: era geometría, y ninguna
     corrección de posición lo iba a arreglar.
     Estas dos barras tapan exactamente lo que el recorte quita. La ventana que
     queda coincide con el rectángulo donde `object-fit:contain` dibuja el
     render, así que las dos imágenes ocupan el mismo sitio en pantalla. */
  #render-ia .marco-foto { position:absolute; top:0; bottom:0; width:0;
    background:#0F1113; display:none; pointer-events:none; }
  #render-ia.comparando .marco-foto { display:block; }
  #render-ia .marco-foto.izq { left:0; }
  #render-ia .marco-foto.der { right:0; }
  #render-ia img { position:absolute; inset:0; width:100%; height:100%; object-fit:contain; display:none; }
  #render-ia .msg { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    gap:12px; text-align:center; color:var(--txt); font-size:15px; line-height:1.5; padding:24px;
    -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px); background:rgba(10,11,13,.85); }
  /* LA BARRA DEL RENDER ENVUELVE, Y EL AVISO VA EN SU PROPIA LÍNEA.
     Era un flex de una sola fila sin `flex-wrap` ni `align-items`, y el aviso
     —que puede traer dos frases: la cortesía agotada y lo que el visor cambió
     del encuadre— entraba como una COLUMNA más. Con siete botones al lado se
     comprimía a su ancho mínimo y crecía hacia abajo; y como `align-items`
     por defecto es `stretch`, los botones se estiraban hasta ESE alto. Medido
     en la captura del 30-ago-2026: una tira de ~350 px de alto ocupando media
     pantalla, con «Descargar» convertido en un botón vertical.
     Con `wrap` + `order:-1` el aviso se lleva una línea entera arriba, y con
     `align-items:center` los botones vuelven a medir lo que miden. */
  .rv-bar { position:absolute; top:14px; left:50%; transform:translateX(-50%); z-index:2;
    display:flex; gap:10px; flex-wrap:wrap; align-items:center; justify-content:center;
    max-width:min(94vw,1040px);
    background:rgba(18,17,15,.72); backdrop-filter:blur(12px);
    border:1px solid rgba(255,255,255,.14); border-radius:14px; padding:8px 10px; box-shadow:0 16px 40px rgba(0,0,0,.55); }
  /* El aviso: línea propia, arriba, y con tope de alto. El `max-width:38ch` que
     tenía EN LÍNEA era lo que lo volvía columna; aquí no hace falta porque ya
     ocupa el ancho de la barra. Si algún día trae un texto largo, scrollea en
     vez de empujar los botones fuera de la pantalla. */
  #render-aviso { flex:1 0 100%; order:-1; display:block; text-align:center;
    color:#f5c800; font-size:12px; line-height:1.45; padding:2px 6px 0;
    max-height:4.6em; overflow-y:auto; }
  /* Mientras se mira un RENDER, el conmutador de propuestas se va. Dos razones:
     se superponía —él pinta por encima (z-index 32 contra el 30 del visor de
     render) y le tapaba el aviso a media frase— y, sobre todo, un conmutador de
     estados encima de una imagen ya generada invita a un gesto que no hace lo
     que promete: el render es de ESTE estado y pulsar «Propuesta v2» no lo
     vuelve a generar. Vuelve solo al pulsar «Volver al 3D». */
  body:has(#render-ia.show) #estados { display:none !important; }
  /* En pantalla pequeña la barra va a TODO EL ANCHO y los botones se reparten.
     Centrada y ajustada al contenido medía 187 px de ancho, así que los siete
     botones se apilaban de a uno: 451 px de alto, el 56 % de un celular, encima
     del render que se acaba de pagar. Repartidos de a tres por fila caben en un
     cuarto de eso. `flex:1 1 30%` y no un ancho tecleado porque el número de
     botones cambia —«Crear video» y «Crear recorrido» solo aparecen con dos y
     tres tomas—. */
  @media (max-width:640px), (max-height:560px) {
    .rv-bar { left:8px; right:8px; transform:none; max-width:none;
      padding:6px 8px; gap:6px; }
    .rv-bar .btn { flex:1 1 30%; min-width:0; justify-content:center;
      font-size:11.5px; padding:0 8px; }
    #render-aviso { font-size:11px; max-height:3.4em; }
  }
  .rv-carrusel { position:absolute; bottom:14px; left:50%; transform:translateX(-50%); z-index:2;
    display:flex; gap:8px; max-width:92vw; overflow-x:auto; padding:8px 10px;
    background:rgba(18,17,15,.55); backdrop-filter:blur(12px); border:1px solid rgba(255,255,255,.12); border-radius:14px; }
  .rv-carrusel:empty { display:none; }
  .rv-carrusel img { width:104px; height:64px; object-fit:cover; border-radius:8px; cursor:pointer;
    border:2px solid transparent; flex:0 0 auto; }
  .rv-carrusel img.sel { border-color:var(--acento); }
  #render-cfg .cfg-mat-t { font-weight:700; font-size:11px; color:var(--acento); margin-top:6px; letter-spacing:.04em; }
  #render-cfg select.cfg-mat { text-transform:capitalize; }
  /* Popover de configuración del render */
  /* El panel crece con cada opcion nueva —ya son ocho selectores y tres
     casillas— y lo que queda ABAJO es el boton de generar. Sin tope, en un
     portatil el boton se sale de la pantalla y el configurador se ve completo:
     no hay nada que indique que falta algo debajo. Con tope y scroll, crece
     hacia adentro. */
  #render-cfg { position:absolute; top:12px; right:14px; z-index:32; width:250px; display:none;
    background:var(--panel); border:1px solid var(--acento); border-radius:12px; padding:14px;
    box-shadow:0 18px 50px rgba(0,0,0,.6); flex-direction:column; gap:9px;
    max-height:calc(100vh - 90px); overflow-y:auto; }
  #render-cfg.show { display:flex; }
  #render-cfg .cfg-t { font-weight:800; font-size:13px; color:var(--acento); margin-bottom:2px; }
  #render-cfg label { display:flex; flex-direction:column; gap:4px; font-size:11.5px; color:var(--txt-mute); }
  #render-cfg select { font:inherit; font-size:13px; background:var(--panel-2); color:var(--txt);
    border:1px solid var(--linea); border-radius:8px; padding:6px 8px; cursor:pointer; }
  #render-cfg select:focus { outline:none; border-color:var(--acento); }
  #render-cfg #cfg-gen { margin-top:4px; }
  #render-cfg .cfg-check { flex-direction:row; align-items:center; gap:7px; cursor:pointer;
    font-size:12px; color:var(--txt); margin-top:2px; }
  #render-cfg .cfg-check input { width:15px; height:15px; accent-color:var(--acento); }
  /* Previsualizador: mismo lienzo que el panel del render, para que pasar de la
     vista al render no cambie de sitio ni de tamaño. */
  /* NO tapa el visor: la vista ES el visor. El contenedor solo aloja la barra y
     el rótulo, y deja pasar el ratón —`pointer-events:none`— para que se pueda
     seguir seleccionando, midiendo y girando debajo. Sus hijos sí lo reciben. */
  #vistas-prev { position:absolute; inset:0; z-index:34; display:none; pointer-events:none; }
  #vistas-prev.show { display:block; }
  #vistas-prev > * { pointer-events:auto; }
  #vistas-prev .vp-tabs { display:flex; gap:5px; }
  #vistas-prev .vp-tabs button { font:inherit; font-size:12px; padding:6px 10px; cursor:pointer;
    background:rgba(255,255,255,.07); color:var(--txt); border:1px solid rgba(255,255,255,.14);
    border-radius:9px; }
  #vistas-prev .vp-tabs button.on { background:var(--acento); color:#111; border-color:var(--acento); font-weight:700; }
  #vistas-prev .vp-niv { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--txt-soft); }
  #vistas-prev .vp-niv select { font:inherit; font-size:12px; background:var(--panel-2); color:var(--txt);
    border:1px solid var(--linea); border-radius:8px; padding:5px 7px; }
  #vp-lienzo { position:absolute; inset:0; z-index:35; cursor:crosshair;
    background:color-mix(in srgb, var(--dato) 8%, transparent); }
  #vp-lienzo .marca { position:absolute; border:2px dashed var(--dato);
    background:color-mix(in srgb, var(--dato) 22%, transparent); pointer-events:none; }
  #vp-zona-form { position:absolute; top:70px; right:14px; z-index:36; width:280px;
    background:var(--panel); border:1px solid var(--acento); border-radius:12px;
    padding:14px; box-shadow:0 18px 50px rgba(0,0,0,.6); flex-direction:column; gap:9px; }
  #vp-zona-form.show { display:flex; }
  #vp-zona-form label { display:flex; flex-direction:column; gap:4px;
    font-size:11.5px; color:var(--txt-mute); }
  #vp-zona-form input { font:inherit; font-size:13px; background:var(--panel-2);
    color:var(--txt); border:1px solid var(--linea); border-radius:8px; padding:6px 8px; }
  #vp-zona-form input:focus { outline:none; border-color:var(--acento); }
  #vp-zona-form .vpz-acc { display:flex; gap:7px; margin-top:4px; }
  #vp-zona-form .vpz-nota { margin:2px 0 0; font-size:11px; color:var(--acento-2); line-height:1.45; }
  #vistas-prev .vp-info { position:absolute; left:14px; bottom:14px; z-index:3; font-size:12px;
    color:var(--txt-soft); background:rgba(18,17,15,.78); border:1px solid rgba(255,255,255,.13);
    border-radius:10px; padding:8px 11px; max-width:52ch; line-height:1.45; }
  #realidad { position:absolute; top:52px; right:14px; z-index:32; width:236px; display:none;
    background:var(--panel); border:1px solid var(--acento); border-radius:12px; padding:14px;
    box-shadow:0 18px 50px rgba(0,0,0,.6); flex-direction:column; gap:10px; }
  #realidad.show { display:flex; }
  #realidad .cfg-t { font-weight:800; font-size:13px; color:var(--acento); }
  #realidad label.rl { display:flex; align-items:center; gap:8px; font-size:12.5px; color:var(--txt); cursor:pointer; }
  #realidad label.sl { display:flex; flex-direction:column; gap:4px; font-size:11.5px; color:var(--txt-mute); }
  #realidad input[type=range] { accent-color:var(--acento); }
  /* Acciones sobre el elemento seleccionado (panel de propiedades) */
  .p-acciones { display:flex; gap:7px; padding:10px 16px 4px; }
  /* Con el icono ya en SVG, el botón alinea como los del dock: el glifo y el
     texto en una fila centrada. Con emoji esto no se podía — cada plataforma le
     daba otra caja y otra línea base. */
  .p-acciones button { flex:1; font:inherit; font-size:12px; font-weight:600; cursor:pointer;
    border:1px solid var(--linea); background:var(--panel-2); color:var(--txt); border-radius:8px; padding:7px 6px;
    display:inline-flex; align-items:center; justify-content:center; gap:6px; line-height:1.15; }
  .p-acciones button .i { width:15px; height:15px; }
  .p-acciones button:hover { border-color:var(--acento); color:var(--acento); }
  .p-trans { display:flex; align-items:center; gap:9px; padding:8px 16px 2px;
             font-size:11.5px; color:#b9b9b9; letter-spacing:.02em; }
  .p-trans input[type=range] { flex:1; accent-color:var(--acento); min-width:0; }
  .p-trans #p-op-v { min-width:52px; text-align:right; font-variant-numeric:tabular-nums;
                     color:#8f8f8f; }
  .mini { font:inherit; font-size:10.5px; font-weight:600; cursor:pointer; border:1px solid var(--linea);
    background:var(--panel); color:var(--acento); border-radius:6px; padding:2px 7px; }
  /* Medición */
  .btn.activo { background:var(--acento); color:#111; border-color:var(--acento); }
  #med-labels { position:absolute; inset:0; pointer-events:none; z-index:26; overflow:hidden; }
  .med-label { position:absolute; transform:translate(-50%,-150%); background:var(--acento); color:#111;
    font-weight:800; font-size:12.5px; padding:3px 9px; border-radius:8px; white-space:nowrap;
    box-shadow:0 6px 18px rgba(0,0,0,.45); text-align:center; line-height:1.25; }
  .med-label .sub { display:block; font-weight:700; font-size:10px; opacity:.8; letter-spacing:.2px; }
  #med-hint .snap { color:var(--acento); font-weight:800; }
  #med-hint { position:absolute; left:50%; bottom:18px; transform:translateX(-50%); z-index:27; display:none;
    background:var(--panel); color:var(--txt); border:1px solid var(--acento); border-radius:10px;
    padding:8px 14px; font-size:12.5px; box-shadow:0 10px 30px rgba(0,0,0,.5); }
  #med-hint.show { display:block; }
  /* ── Perfil longitudinal ─────────────────────────────────────────────── */
  #perfil-hint { position:absolute; left:50%; bottom:18px; transform:translateX(-50%); z-index:27;
      display:none; background:rgba(18,18,20,.92); border:1px solid rgba(255,255,255,.14);
      border-radius:10px; padding:.5rem .8rem; font-size:.82rem; white-space:nowrap }
  #perfil-hint.show { display:block }
  #perfil-hint b { color:var(--acento) }
  #perfil-hint .med-ctrls { display:inline-flex; gap:6px; margin:0 5px }
  /* Nace OCULTO, como sus hermanos. NO lo estaba: `#perfil` solo declaraba su
     ancho —ni `display`, ni `position`—, asi que sus botones «⭳ CSV» y
     «↺ Otro perfil» se veian flotando sobre el modelo desde el primer segundo,
     sin que nadie hubiera pedido un perfil del terreno. Se ve en la captura del
     30-ago-2026: dos botones sueltos a media altura, encima de la casa.
     `abrirPanel()` ya le ponia `.show` y `limpiar()` se lo quitaba; lo que
     faltaba era que eso significara algo. `position:absolute` SIN desplazamientos
     lo deja donde estaba —esa es la posicion estatica— pero lo saca del flujo. */
  #perfil { width:min(46rem, 94vw); display:none; position:absolute; z-index:26 }
  #perfil.show { display:block }
  #perfil .pf-cifras { display:flex; flex-wrap:wrap; gap:.15rem 1.1rem; margin:0 0 .5rem;
      font-size:.8rem; color:var(--ink2) }
  #perfil .pf-cifras b { color:var(--ink1); font-variant-numeric:tabular-nums }
  #perfil svg { width:100%; height:auto; display:block; background:rgba(255,255,255,.03);
      border-radius:8px }
  #perfil .pf-eje { stroke:rgba(255,255,255,.18); stroke-width:1 }
  #perfil .pf-rej { stroke:rgba(255,255,255,.07); stroke-width:1 }
  #perfil .pf-rot { fill:var(--ink3); font-size:9px; font-family:inherit }
  #perfil .pf-linea { fill:none; stroke:var(--acento); stroke-width:1.8;
      stroke-linejoin:round; stroke-linecap:round }
  #perfil .pf-relleno { fill:rgba(245,200,0,.13) }
  #perfil .pf-vert { stroke:rgba(255,255,255,.28); stroke-width:1; stroke-dasharray:3 3 }
  /* La nota NO es decoración: es la mitad del resultado. Va con el mismo peso
     visual que las cifras, no en letra chica al pie. */
  #perfil .pf-nota { margin:.6rem 0 0; padding:.55rem .7rem; border-radius:8px;
      background:rgba(245,200,0,.09); border:1px solid rgba(245,200,0,.28);
      font-size:.76rem; color:var(--ink2); text-align:justify; hyphens:auto }
  #perfil .pf-nota b { color:var(--acento) }
  #perfil .pf-nota ul { margin:.35rem 0 0; padding-left:1.1rem }
  #perfil .pf-acc { display:flex; gap:.4rem; margin-top:.5rem; flex-wrap:wrap }
  #med-hint b { color:var(--acento); }
  #med-hint .med-ctrls { display:inline-flex; gap:6px; margin:0 5px; }
  .med-b { font:inherit; font-size:11.5px; font-weight:700; cursor:pointer; color:var(--txt);
    background:var(--panel-2); border:1px solid var(--linea); border-radius:7px; padding:3px 8px; }
  .med-b:hover { border-color:var(--acento); color:var(--acento); }
  .med-b.on { background:var(--acento); color:#111; border-color:var(--acento); }

  /* ── Capa PORTAL: portada inmersiva + capítulos de obra ── */
  #portal-hero { position:absolute; inset:0; z-index:30; display:flex; flex-direction:column;
    justify-content:center; align-items:center; text-align:center; padding:2rem; pointer-events:none;
    background:radial-gradient(120% 90% at 50% 32%, rgba(12,11,9,.30), rgba(12,11,9,.74));
    transition:opacity .8s ease, visibility .8s; }
  #portal-hero.hide { opacity:0; visibility:hidden; }
  #portal-hero .ph-logo { height:2.3rem; width:auto; margin-bottom:1.3rem; opacity:.95;
    filter:drop-shadow(0 6px 20px rgba(0,0,0,.5)); }
  #portal-hero h1 { font-size:clamp(2rem,5vw,3.4rem); font-weight:600; letter-spacing:-.022em;
    margin:0 0 .5rem; color:#E9E6E1; text-shadow:0 6px 34px rgba(0,0,0,.55); max-width:18ch; line-height:1.05; }
  #portal-hero p { color:#C9CDD3; font-size:1.05rem; margin:0 0 1.6rem; max-width:36ch;
    text-shadow:0 2px 14px rgba(0,0,0,.55); }
  #portal-hero .explorar { pointer-events:auto; cursor:pointer; font:inherit; font-weight:700;
    font-size:.95rem; color:#111; background:var(--acento); border:0; border-radius:999px;
    padding:.72rem 1.6rem; box-shadow:0 14px 38px rgba(0,0,0,.45); transition:transform .15s; }
  #portal-hero .explorar:hover { transform:translateY(-1px); }
  #portal-hero .hint { margin-top:1rem; font-size:.8rem; color:#8F959D; letter-spacing:.02em;
    text-shadow:0 2px 10px rgba(0,0,0,.6); }

  #capitulos { position:absolute; left:50%; bottom:16px; transform:translateX(-50%); z-index:24;
    max-width:min(94vw,62rem); display:none; flex-direction:column; gap:.45rem; align-items:center; }
  #capitulos.show { display:flex; }
  #capitulos .cap-t { font-size:.66rem; letter-spacing:.24em; text-transform:uppercase; color:#C9CDD3;
    font-weight:700; text-shadow:0 2px 10px rgba(0,0,0,.7); }
  #cap-chips { display:flex; flex-wrap:wrap; gap:.4rem; justify-content:center; max-height:31vh; overflow:auto;
    background:rgba(18,17,15,.55); backdrop-filter:blur(16px) saturate(1.1); -webkit-backdrop-filter:blur(16px) saturate(1.1);
    border:1px solid rgba(255,255,255,.12); border-radius:16px; padding:.6rem; box-shadow:0 20px 54px rgba(0,0,0,.55); }
  .cap-chip { font:inherit; cursor:pointer; font-size:.82rem; font-weight:600; color:#E9E6E1;
    background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.12); border-radius:999px;
    padding:.4rem .85rem; display:flex; align-items:center; gap:.45rem; transition:border-color .15s, color .15s, background .15s; }
  .cap-chip:hover { border-color:var(--acento); color:#fff; }
  .cap-chip .c { font-size:.72rem; color:#8F959D; font-variant-numeric:tabular-nums; }
  .cap-chip.on { background:var(--acento); color:#111; border-color:var(--acento); }
  .cap-chip.on .c { color:rgba(0,0,0,.55); }
  .cap-chip.todo { border-style:dashed; }
  .cap-chip.sinmodelo { opacity:.62; }
  .cap-grupo { flex-basis:100%; width:100%; display:flex; align-items:center; justify-content:center; gap:.4rem;
    color:#E9E6E1; font-size:.64rem; font-weight:700; letter-spacing:.13em; text-transform:uppercase;
    padding:.5rem .3rem .1rem; opacity:.92; }
  .cap-grupo .gn { max-width:24rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    text-transform:none; letter-spacing:.02em; }
  .cap-grupo .c { font-size:.62rem; color:#8F959D; letter-spacing:normal; }
  #cap-t b { color:var(--acento); }
  #cap-t .cap-nota { color:#8F959D; font-weight:400; text-transform:none; letter-spacing:normal; }

  /* ── Táctil (iPad): gestos al 3D, no al navegador ── */
  canvas { touch-action:none; }
  body { -webkit-touch-callout:none; -webkit-user-select:none; }
  #escena.dibujando canvas { cursor:crosshair; }

  /* ── Panel del plan urbanístico ── */
  /* Se abre y se cierra por `.show`, como los demás paneles del gestor de
     ventanas: un panel que se abre por `style.display` no lo cierra la × (16-sep-2026). */
  #planbar { position:absolute; top:12px; left:14px; z-index:24; width:250px; max-width:78vw; display:none;
    background:rgba(27,30,34,.94); border:1px solid var(--linea); border-radius:12px;
    box-shadow:0 14px 34px -18px rgba(0,0,0,.85); overflow:hidden; font-size:12.5px; backdrop-filter:blur(3px); }
  #planbar.show { display:block; }
  #planbar .pb-capa { display:flex; align-items:center; gap:6px; font-weight:600; color:var(--txt-soft); cursor:pointer; }
  #planbar .pb-body { padding:10px 11px; display:flex; flex-direction:column; gap:9px; }
  #planbar .pb-row { display:flex; align-items:center; gap:7px; }
  #planbar select, #planbar input[type=number] { font:inherit; font-size:12.5px; background:var(--panel);
    color:var(--txt); border:1px solid var(--linea); border-radius:7px; padding:6px 8px; }
  #planbar select { flex:1; }
  #planbar .pb-ancho { flex:1; display:flex; align-items:center; gap:6px; color:var(--txt-soft); }
  #planbar .pb-ancho input { width:52px; }
  .btn.sm { padding:6px 10px; font-size:12.5px; border-radius:8px; }
  #planbar .pb-draw { border-top:1px dashed var(--linea); padding-top:9px; display:flex; flex-direction:column; gap:8px; }
  #planbar .pb-hint { color:var(--acento); font-size:11.5px; line-height:1.45; }
  #planbar .pb-list { display:flex; flex-direction:column; gap:5px; max-height:34vh; overflow-y:auto; }
  #planbar .pb-item { display:flex; align-items:center; gap:8px; padding:6px 8px; border-radius:8px;
    background:var(--panel); border:1px solid var(--linea); }
  #planbar .pb-item .sw { width:12px; height:12px; border-radius:3px; flex:none; border:1px solid rgba(255,255,255,.25); }
  #planbar .pb-item .nom { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-weight:600; }
  #planbar .pb-item .med { color:var(--txt-mute); font-variant-numeric:tabular-nums; font-size:11.5px; }
  #planbar .pb-item .del { border:0; background:transparent; color:var(--txt-mute); cursor:pointer; font-size:14px; padding:0 3px; border-radius:5px; }
  #planbar .pb-item .del:hover { color:var(--bima-grave-ink); background:var(--linea); }
  #planbar .pb-vacio { color:var(--txt-mute); font-size:11.5px; padding:2px; }
  #planbar .pb-foot { display:flex; gap:7px; border-top:1px solid var(--linea); padding-top:9px; }
  #planbar .pb-foot .btn { flex:1; justify-content:center; }
  .etq-plan { background:rgba(20,22,26,.82); border:1px solid rgba(255,255,255,.16); border-radius:7px;
    padding:3px 8px; font-size:11.5px; font-weight:700; color:#fff; white-space:nowrap;
    box-shadow:0 3px 10px -4px rgba(0,0,0,.7); user-select:none; }
  .etq-plan small { display:block; font-weight:500; color:var(--txt-soft); font-size:10.5px; font-variant-numeric:tabular-nums; }

  /* ── Overlay de amarre (loteo 2D → terreno) ── */
  #amarre { position:absolute; top:12px; right:14px; z-index:26; width:320px; max-width:88vw;
    background:rgba(27,30,34,.97); border:1px solid var(--linea); border-radius:12px;
    box-shadow:0 16px 40px -18px rgba(0,0,0,.9); overflow:hidden; }
  #amarre .am-head { display:flex; align-items:center; gap:8px; padding:10px 12px; background:var(--panel-2);
    border-bottom:1px solid var(--linea); font-weight:800; font-size:13.5px; }
  #amarre .am-head span { flex:1; }
  #amarre .am-x { border:0; background:transparent; color:var(--txt-mute); font-size:20px; cursor:pointer; line-height:1; padding:0 4px; border-radius:6px; }
  #amarre .am-x:hover { color:var(--txt); background:var(--linea); }
  #amarre .am-body { padding:11px 12px; display:flex; flex-direction:column; gap:9px; }
  #amarre .am-paso { font-weight:700; font-size:12.5px; color:var(--acento); }
  #amarre .am-paso.terreno { color:var(--verde); }
  #amarre .am-mapa-wrap { background:#0F1113; border:1px solid var(--linea); border-radius:8px; overflow:hidden; }
  #amarre svg { display:block; width:100%; height:220px; touch-action:none; cursor:crosshair; }
  #amarre svg .lote { fill:rgba(245,200,0,.18); stroke:var(--acento); stroke-width:1.2; vector-effect:non-scaling-stroke; }
  #amarre svg .via { fill:rgba(120,125,130,.25); stroke:#8F959D; stroke-width:1.2; vector-effect:non-scaling-stroke; }
  #amarre svg .marca { fill:#fff; stroke:var(--dato); stroke-width:2; vector-effect:non-scaling-stroke; }
  #amarre .am-row { display:flex; align-items:center; }
  #amarre .am-row-btns { gap:9px; }
  #amarre .am-row-btns .btn { flex:1; justify-content:center; }
  #amarre .am-flip { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--txt-soft); cursor:pointer; white-space:nowrap; }
  #amarre .am-hint { font-size:11px; color:var(--txt-mute); line-height:1.5; }

  @media (pointer:coarse) {
    #planbar { width:280px; } #planbar select, #planbar input[type=number] { padding:9px 10px; font-size:14px; }
    #planbar .pb-item { padding:9px 10px; } .btn.sm { padding:10px 13px; }
    /* 44 px de lado: es el blanco al que un dedo acierta. Los botones medían
       entre 6 y 9 px de relleno —hechos para un ratón— y en el celular había
       que apuntar. Se toca el ALTO MÍNIMO, no el relleno, para no descuadrar
       las barras: el texto sigue centrado y el botón crece hacia afuera. */
    .btn, .btn.ic, .btn.sm, .btn.mi, #btn-open { min-height:44px; }
    .btn.ic, #btn-open { min-width:44px; justify-content:center; }
    /* El menú «más» y el cajón de capas son listas: ahí cada renglón es un
       blanco, no solo los botones. */
    .more-menu .btn.mi { min-height:44px; }
    #panel label, #tools label { min-height:44px; display:flex; align-items:center; }
    /* La cruz de cerrar del cajón tenía 2×6 px de relleno: imposible de acertar
       sin uña. El selector es `#panel .p-close` — comprobado en el archivo, no
       supuesto: una clase inventada aquí no da error, simplemente no hace nada. */
    #panel .p-close { min-width:44px; min-height:44px;
      display:inline-flex; align-items:center; justify-content:center; padding:0; }
  }

  /* ── Paneles: que no se encimen ── */
  /* El plan flota arriba-izquierda; si abre el cajón de Capas/Sección, se corre a
     su derecha (Safari iPad soporta :has). */
  #escena:has(#tools.show) #planbar { left:282px; }
  /* En pantallas angostas (celular / iPad vertical) los paneles flotantes pasan a
     HOJA INFERIOR de ancho completo → no se encima nada y se ve la página entera. */
  @media (max-width:820px) {
    #planbar, #amarre, #render-cfg {
      left:8px; right:8px; top:auto; bottom:8px; width:auto; max-width:none; max-height:54vh; overflow-y:auto;
    }
    #escena:has(#tools.show) #planbar { left:8px; }   /* en hoja inferior no se desplaza */
    #tools, #panel { width:min(86vw,320px); }
    .more-menu { min-width:210px; }
  }

  /* ── Controles táctiles de recorrido/dron (joystick + mirar) ── */
  #touch-ctl { position:absolute; inset:0; z-index:28; pointer-events:none; }
  #joy-base { position:absolute; left:26px; bottom:26px; width:120px; height:120px; border-radius:50%;
    background:rgba(20,22,26,.5); border:1.5px solid rgba(255,255,255,.22); pointer-events:auto;
    touch-action:none; backdrop-filter:blur(2px); }
  #joy-knob { position:absolute; left:50%; top:50%; width:52px; height:52px; border-radius:50%;
    transform:translate(-50%,-50%); background:rgba(245,200,0,.9); box-shadow:0 5px 16px -5px rgba(0,0,0,.7); }
  #touch-ctl .tc-btns { position:absolute; right:22px; bottom:26px; display:flex; flex-direction:column-reverse; gap:12px; pointer-events:auto; }
  #touch-ctl .tc-b { width:56px; height:56px; border-radius:50%; border:1.5px solid rgba(255,255,255,.22);
    background:rgba(20,22,26,.62); color:#fff; font-size:19px; font-weight:800; touch-action:none;
    display:flex; align-items:center; justify-content:center; backdrop-filter:blur(2px); }
  #touch-ctl .tc-b:active { background:rgba(245,200,0,.9); color:#111; }
  #touch-ctl .tc-exit { background:rgba(120,30,30,.6); }
  /* Solo el modo DRON tiene subir/bajar; el recorrido va pegado al piso */
  #touch-ctl:not(.dron) #tc-up, #touch-ctl:not(.dron) #tc-down { display:none; }

  /* EN TÁCTIL, DENTRO DEL RECORRIDO, EL DOCK SE QUITA DE EN MEDIO.
     No es cosmética: el dock es `position:fixed` con z-index 90 y los controles
     táctiles tienen 28, así que el dock iba ENCIMA. Medido el 30-ago-2026 en
     375×812 dentro de «Caminar»:

         #wm-dock     0–375  ×  670–812   (z 90)
         palanca     26–146  ×  666–786   (z 28)
         botones ✕   297–353 ×  662–786   (z 28)

     La palanca quedaba tapada salvo 4 px, y el ✕ de salir salvo 8. Un toque en
     el centro de la palanca lo recibía el botón «Caminar» del dock. O sea que en
     un celular no se podía ni caminar ni salir del recorrido con el botón que
     existe para eso — solo volviendo a tocar «Caminar», que es la salida que
     nadie anunció. Dentro del recorrido el dock no hace falta: mandan la palanca
     y el ✕, que es justo lo que dice la ayuda. */
  @media (pointer: coarse) {
    body.en-primera-persona #wm-dock { display:none; }
  }


#btn-reset,#btn-medir,#btn-perfil,#btn-plan,#btn-render,#btn-open,#btn-orient,#btn-fp,#btn-dron,#btn-capas,#btn-sec,#btn-nube,#btn-wire,#btn-album,.more-menu,.more,#btn-more{display:none!important}
.wm-ic{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;display:block}
.wm-win .wm-head .wm-ic{width:14px;height:14px}
.wm-win .wm-head{display:flex;align-items:center;gap:8px;cursor:move;padding:6px 11px;
  background:rgba(20,25,32,.94);border-radius:13px 13px 0 0;user-select:none;
  border-bottom:1px solid rgba(255,255,255,.08);touch-action:none}
.wm-win .wm-ttl{flex:1;display:flex;align-items:center;gap:7px;font-size:.62rem;letter-spacing:.14em;text-transform:uppercase;color:#C9CDD3;font-weight:800;white-space:nowrap}
.wm-win .wm-min,.wm-win .wm-x{cursor:pointer;width:22px;height:22px;display:grid;place-items:center;border-radius:6px;color:#C9CDD3;font-size:15px;line-height:1}
.wm-win .wm-min:hover,.wm-win .wm-x:hover{background:rgba(255,255,255,.14);color:#fff}
.wm-win.wm-mini .wm-body{display:none}
.wm-win.wm-mini{width:auto!important;min-width:0!important}
#wm-dock{position:fixed;left:50%;bottom:11px;transform:translateX(-50%);z-index:90;display:flex;gap:3px;flex-wrap:wrap;justify-content:center;max-width:96vw;
  background:rgba(15,18,22,.86);backdrop-filter:blur(14px);border:1px solid rgba(255,255,255,.13);
  border-radius:16px;padding:6px;box-shadow:0 12px 34px rgba(0,0,0,.45)}
#wm-dock button{min-width:54px;height:46px;border:none;background:transparent;border-radius:12px;
  color:#c9cdd3;cursor:pointer;transition:.15s;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;padding:0 8px}
#wm-dock button small{font-size:7.5px;letter-spacing:.06em;text-transform:uppercase;opacity:.72;font-weight:700}
#wm-dock button:hover{background:rgba(255,255,255,.08);color:#fff}
#wm-dock button.on{background:var(--acento,#f5c800);color:#111}
#wm-dock #wm-clean{color:var(--bima-grave-ink)}
/* ── EN PANTALLA PEQUEÑA, UNA HOJA QUE NUNCA TAPA EL MODELO ──────────────────
   La regla de abajo bajaba los paneles a hoja inferior, y le faltaban las DOS
   cosas que la hacen usable. Medido el 30-ago-2026 sobre un iPhone real:

     · `#tools` (Herramientas · Caja de sección · Capas) se esconde con
       `transform:translateX(-100%)`, no con `display`. La hoja ponía
       `transform:none!important`, que es EXACTAMENTE el gesto que lo muestra:
       quedaba abierto para siempre y su ✕ no servía de nada. Eso es el «no deja
       ocultar los paneles»;
     · y la hoja no tenía TOPE de altura. La lista de Capas trae 14 categorías,
       así que crecía hasta más alto que la pantalla: el modelo desaparecía
       detrás y la cabecera —donde está la ✕— se salía por arriba. Un panel más
       alto que la pantalla no se puede cerrar con su propio botón.

   El alto del dock se MIDE (`--alto-dock`), no se teclea: en celular envuelve en
   dos o tres filas según quepa, y el 108px fijo dejaba la hoja por debajo de él.

   Se aplica también por ALTURA (`max-height:560px`) y no solo por ancho: un
   celular acostado tiene 800 px de ancho y 390 de alto, así que por ancho no
   entraba aquí y le quedaba la interfaz de escritorio en 390 px de alto. */
@media(max-width:640px), (max-height:560px){
  .wm-win{left:0!important;right:0!important;top:auto!important;
    bottom:var(--alto-dock,108px)!important;
    width:100%!important;max-width:100%!important;border-radius:15px 15px 0 0!important;
    /* El TOPE. El modelo se sigue viendo por encima, que es de lo que se trata. */
    max-height:min(52vh,400px)!important}
  /* Lo que se esconde por transform vuelve a esconderse. Va con `#id`, que pesa
     más que la clase de arriba, y solo cuando NO está abierto. */
  #tools:not(.show){transform:translateX(-100%)!important}
  #panel:not(.show){transform:translateX(100%)!important}
  .wm-win.show{transform:none!important;display:flex!important;flex-direction:column!important}
  /* La cabecera no se va con el scroll: es donde está la ✕. */
  .wm-win .wm-head{flex:0 0 auto;cursor:default}
  .wm-win .wm-body{flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch}
  .wm-win.wm-mini{max-height:none!important}
  /* El panel de propiedades (no es una hoja: es el cajón de la derecha) también
     se queda a media pantalla, para que se vea contra QUÉ elemento se abrió. */
  #panel.show{top:auto!important;bottom:var(--alto-dock,108px)!important;
    left:0!important;right:0!important;width:100%!important;max-width:100%!important;
    max-height:min(52vh,400px)!important;border-radius:15px 15px 0 0!important}
}

/* ── CELULAR ACOSTADO: COLUMNA, NO HOJA ─────────────────────────────────────
   Con 375 px de alto, una hoja inferior de 52vh mas el dock mas el encabezado
   no dejan sitio para nada. Medido a 812x375 con «Ver por dentro» abierto: la
   hoja iba de y=16 a y=211 —o sea, POR ENCIMA del encabezado, que termina en
   56— y el modelo se quedaba con MENOS CERO pixeles. Acostado hay ancho de
   sobra y no hay alto: la ventana vuelve a ser columna, pegada a un costado,
   con el modelo al lado. Es el mismo criterio de la hoja —no tapar el
   modelo—, resuelto en el eje que sí tiene espacio. */
@media (max-height:560px) and (min-width:641px){
  .wm-win.show, #panel.show{
    /* `top` cuenta desde `#escena`, que YA empieza debajo del encabezado: un
       64px aqui descontaba el encabezado dos veces y le comia 56 px de alto a
       una columna que solo tiene 319. */
    top:8px!important; bottom:calc(var(--alto-dock,108px) + 8px)!important;
    height:auto!important; max-height:none!important;
    width:min(44vw,300px)!important; max-width:44vw!important;
    border-radius:12px!important; transform:none!important}
  .wm-win.show{left:8px!important; right:auto!important}
  #panel.show{right:8px!important; left:auto!important}
}
@media(max-width:640px){
  /* En celular NO se desliza: se envuelve en dos filas. Medido a 375 px, en una
     sola fila solo caben 6 de 9 botones y quedaban escondidos «Casa de hoy» (la nube
     de puntos, que es lo que más impresiona), «Técnicas» y «Limpiar». Un gesto que
     hay que descubrir es un gesto que la clienta no va a hacer. */
  #wm-dock{left:0;right:0;bottom:0;transform:none;border-radius:0;justify-content:center;
    width:100%;max-width:100%;overflow-x:visible;flex-wrap:wrap;padding:5px 4px;gap:2px}
  #wm-dock button{min-width:19vw;height:42px;padding:0 2px}
  #wm-dock button small{font-size:7px}
}

/* ── CELULAR ACOSTADO: EL DOCK A TODO EL ANCHO, PARA QUE SEA BAJO ────────────
   Acostado hay 812 px de ancho y solo 375 de alto, o sea que el alto es lo
   escaso. El dock se quedaba centrado y angosto (406 px, la regla de ancho
   completo es solo para <=640) y por eso envolvia en TRES filas: 158 px, el
   42 % de la pantalla, encima del modelo. Repartido a todo el ancho, los
   mismos botones caben en una fila.

   `flex:1 1 52px` en vez de un ancho tecleado: el numero de botones cambia en
   caliente —«Tecnicas» destapa cuatro mas— y un ancho fijo calculado para once
   se desborda con quince. Con base flexible se reparten solos y, cuando de
   verdad no caben, envuelven. */
@media (max-height:560px){
  #wm-dock{left:0;right:0;bottom:0;transform:none;border-radius:0;
    width:100%;max-width:100%;justify-content:center;flex-wrap:wrap;padding:4px 6px;gap:2px}
  #wm-dock button{flex:1 1 52px;min-width:52px;max-width:84px;height:40px;padding:0 2px}
  #wm-dock button small{font-size:7px}
}


  :root { --ps-split: 42%; }
  /* el lienzo 3D cede espacio cuando el split está activo */
  body.ps-on #canvas-wrap { right: var(--ps-split); transition: right .18s ease; }
  #presu-split {
    position: fixed; z-index: 60; display: none; flex-direction: column;
    top: 0; right: 0; bottom: 0; width: var(--ps-split);
    background: #0F1113; border-left: 1px solid rgba(255,255,255,.12);
    box-shadow: -18px 0 44px rgba(0,0,0,.45); color: #e9e6e1;
  }
  body.ps-on #presu-split { display: flex; }
  #ps-grip { position: absolute; left: -4px; top: 0; bottom: 0; width: 9px; cursor: col-resize; z-index: 2; }
  #ps-grip::after { content: ""; position: absolute; left: 3px; top: 50%; width: 3px; height: 46px;
    margin-top: -23px; border-radius: 3px; background: rgba(255,255,255,.22); }

  /* ── VERTICAL (portrait / móvil): el corte pasa a ser horizontal ── */
  @media (orientation: portrait), (max-width: 820px) {
    body.ps-on #canvas-wrap { right: 0; bottom: var(--ps-split); transition: bottom .18s ease; }
    #presu-split { top: auto; left: 0; right: 0; bottom: 0; width: auto; height: var(--ps-split);
      border-left: none; border-top: 1px solid rgba(255,255,255,.12);
      box-shadow: 0 -18px 44px rgba(0,0,0,.45); border-radius: 16px 16px 0 0; }
    #ps-grip { left: 0; right: 0; top: -4px; bottom: auto; width: auto; height: 11px; cursor: row-resize; }
    #ps-grip::after { left: 50%; top: 4px; margin: 0 0 0 -23px; width: 46px; height: 3px; }
  }

  #ps-head { display: flex; align-items: center; gap: 10px; padding: 12px 15px 10px;
    border-bottom: 1px solid rgba(255,255,255,.08); }
  #ps-head .ttl { font-size: .64rem; letter-spacing: .16em; text-transform: uppercase; color: #f5c800; font-weight: 800; }
  #ps-head .tot { margin-left: auto; text-align: right; }
  #ps-head .tot b { font-size: 1.12rem; font-weight: 800; font-variant-numeric: tabular-nums; display: block; }
  #ps-head .tot span { font-size: .64rem; opacity: .55; letter-spacing: .06em; text-transform: uppercase; }
  .ps-ico { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round; display: block; }
  #ps-head button { background: rgba(255,255,255,.07); border: none; color: #c9cdd3; width: 30px; height: 30px;
    border-radius: 9px; cursor: pointer; display: grid; place-items: center; }
  #ps-head button:hover { background: rgba(255,255,255,.15); color: #fff; }
  #ps-pdf, #ps-armar { width: auto !important; padding: 0 12px; gap: 7px; font-size: .76rem; font-weight: 700;
    display: flex !important; align-items: center; text-decoration: none;
    background: rgba(255,255,255,.07); color: #c9cdd3; height: 30px; border-radius: 9px; }
  #ps-pdf:hover, #ps-armar:hover { background: rgba(255,255,255,.15); color: #fff; }
  /* Los botones de documento: mismo traje que #ps-pdf, uno por papel emitido. */
  .ps-doc { display:inline-flex; align-items:center; gap:7px; padding:0 12px; height:30px;
            border-radius:8px; background:rgba(255,255,255,.08); color:#d6d6d6;
            text-decoration:none; font-size:.76rem; font-weight:700; white-space:nowrap; }
  .ps-doc:hover { background:rgba(255,255,255,.15); color:#fff; }
  /* ARMAR va en amarillo y el PDF no: son dos gestos distintos. El PDF es
     consultar lo que ya está decidido; armar es DECIDIR, y es lo que queremos
     que el cliente haga desde aquí. Un botón secundario al lado de otro
     secundario no dice cuál de los dos abre la conversación. */
  #ps-armar { background: #f5c800; color: #2d2d2d; }
  #ps-armar:hover { background: #ffd91f; color: #2d2d2d; }
  #ps-armar[data-sin-servidor] { display: none !important; }

  #ps-body { flex: 1; overflow-y: auto; padding: 13px 15px 22px; }
  .ps-sec { font-size: .6rem; letter-spacing: .15em; text-transform: uppercase; color: #8f959d;
    font-weight: 800; margin: 4px 0 9px; }
  .ps-donas { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; }
  @media (max-width: 520px) { .ps-donas { grid-template-columns: 1fr; } }
  .ps-card { background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08);
    border-radius: 13px; padding: 12px 13px; }
  .ps-dr { display: flex; align-items: center; gap: 12px; }
  .ps-leg { flex: 1; min-width: 0; }
  .ps-leg .li { display: flex; align-items: center; gap: 7px; font-size: .74rem; padding: 2px 0; }
  .ps-leg .li i { width: 9px; height: 9px; border-radius: 2px; flex: none; }
  .ps-leg .li em { flex: 1; font-style: normal; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; opacity: .85; }
  .ps-leg .li b { font-variant-numeric: tabular-nums; }

  .ps-filtros { display: flex; flex-wrap: wrap; gap: 5px; margin: 14px 0 4px; }
  .ps-f { border: 1px solid rgba(255,255,255,.14); background: transparent; color: #c9cdd3;
    border-radius: 99px; padding: 4px 11px; font-size: .72rem; font-weight: 700; cursor: pointer; }
  .ps-f.on { background: #f5c800; border-color: #f5c800; color: #16191D; }

  .ps-cap { background: rgba(255,255,255,.035); border: 1px solid rgba(255,255,255,.07);
    border-radius: 11px; padding: 9px 12px; margin-top: 7px; cursor: pointer; transition: .13s; }
  .ps-cap:hover { border-color: rgba(245,200,0,.5); background: rgba(245,200,0,.07); }
  .ps-cap.on { border-color: #f5c800; background: rgba(245,200,0,.12); }
  .ps-cap .r { display: flex; align-items: baseline; gap: 9px; }
  .ps-cap .n { font-size: .84rem; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .ps-cap .p { font-size: .68rem; opacity: .5; margin-left: auto; font-variant-numeric: tabular-nums; }
  .ps-cap .v { font-size: .82rem; font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .ps-cap .bar { height: 4px; border-radius: 3px; background: rgba(255,255,255,.08); margin-top: 6px; overflow: hidden; }
  .ps-cap .bar i { display: block; height: 100%; background: linear-gradient(90deg, #f5c800, var(--bima-amarillo-2)); border-radius: 3px; }
  .ps-nota { font-size: .68rem; opacity: .45; margin-top: 14px; line-height: 1.5; }
  /* Qué NO está incluido. Con borde de aviso, no escondido en letra pequeña: es lo
     último que se lee antes de aceptar. */
  #ps-excl { margin-top: 18px; border-left: 3px solid var(--bima-aviso, #A8760A);
             padding: 2px 0 2px 12px; }
  #ps-excl ul { margin: 8px 0 0; padding-left: 16px; }
  #ps-excl li { font-size: .74rem; line-height: 1.55; opacity: .8; margin-bottom: 7px; }
  /* El único botón del visor que compromete a la clienta: se ve como lo que es. */
  #ps-aceptar { margin-top: 18px; }
  #ps-aceptar-btn {
    display: flex; align-items: center; justify-content: center; gap: 9px;
    padding: 14px 16px; border-radius: var(--bima-r-md, 10px);
    background: var(--bima-verde, #2E9C3A); color: #fff; font-weight: 600;
    font-size: .95rem; text-decoration: none; line-height: 1.25;
    min-height: 48px;                       /* pulgar, no puntero (WCAG 2.5.5) */
    transition: filter .15s ease;
  }
  #ps-aceptar-btn:hover, #ps-aceptar-btn:focus-visible { filter: brightness(1.08); }
  #ps-aceptar-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
  .ps-aceptar-pie { font-size: .68rem; opacity: .55; margin-top: 8px;
                    line-height: 1.5; text-align: center; }
