/* ---------- Tipografía pixel (autoalojada) ----------
   'Minecraftia'/'Minecraft Regular' son nombres de fuentes que casi nadie tiene
   instaladas en el sistema (y la de Minecraft es propiedad de Mojang, no
   redistribuible); sin un @font-face real esos nombres nunca hacían match y la
   pila caía en Lucida Console/monospace, que NO es una fuente de píxeles. Aquí
   se autoaloja "Pixelify Sans" (Google Fonts, licencia SIL Open Font License —
   libre de redistribuir) en public/assets/fonts, servida desde el propio origen
   para cumplir la CSP (`font-src 'self'`, sin permitir CDNs externos). Se
   mantienen 'Minecraftia'/'Minecraft Regular' PRIMERO en la pila por si el
   usuario las tiene instaladas de verdad (frecuente entre jugadores/modders de
   Minecraft), y Pixelify Sans queda como la que garantiza el efecto pixel
   incluso si no las tiene. */
@font-face{
  font-family:'Pixelify Sans'; font-style:normal; font-weight:400; font-display:swap;
  src:url('../assets/fonts/PixelifySans-400.woff2') format('woff2');
}
@font-face{
  font-family:'Pixelify Sans'; font-style:normal; font-weight:700; font-display:swap;
  src:url('../assets/fonts/PixelifySans-700.woff2') format('woff2');
}
:root{
    --paper:#f6f3ec; --paper-2:#efeadf; --ink:#1b2333; --ink-soft:#556074;
    --line:#d8d2c4; --line-soft:#e7e2d6; --panel:#fffdf7; --panel-2:#f2eee3;
    --accent:#3d4aa0; --accent-2:#5866c9; --accent-soft:#e6e8f6;
    --truss:#c0392b; --beam:#5a5f6b; --support:#1b2333; --load:#c0392b;
    --dload:#7d3cc0; --dim:#8a8577; --grid:#e4dfd2; --grid-axis:#b9b19c; --settle:#0b7285;
    --ok:#2e7d52; --danger:#b4402f;
    --shadow:0 1px 2px rgba(20,25,40,.08),0 6px 24px rgba(20,25,40,.06);
    --radius:10px;
    --serif:'Minecraftia','Minecraft Regular','Pixelify Sans','Lucida Console',monospace;
    --mono:'Minecraftia','Minecraft Regular','Pixelify Sans','Cascadia Code','Consolas','SF Mono',ui-monospace,monospace;
  }
  @media (prefers-color-scheme: dark){
    :root{
      --paper:#12151c; --paper-2:#0d1016; --ink:#e9ecf5; --ink-soft:#9aa3b8;
      --line:#2a3040; --line-soft:#20252f; --panel:#171b24; --panel-2:#12161e;
      --accent:#8b9bff; --accent-2:#a6b2ff; --accent-soft:#232a3f;
      --truss:#ff6b5a; --beam:#9aa1b3; --support:#cdd3e2; --load:#ff6b5a;
      --dload:#c48bff; --dim:#6b7386; --grid:#20252f; --grid-axis:#39414f; --settle:#3bc9db;
      --shadow:0 1px 2px rgba(0,0,0,.4),0 8px 30px rgba(0,0,0,.35);
    }
  }
  :root[data-theme="light"]{
    --paper:#f6f3ec; --paper-2:#efeadf; --ink:#1b2333; --ink-soft:#556074;
    --line:#d8d2c4; --line-soft:#e7e2d6; --panel:#fffdf7; --panel-2:#f2eee3;
    --accent:#3d4aa0; --accent-2:#5866c9; --accent-soft:#e6e8f6;
    --truss:#c0392b; --beam:#5a5f6b; --support:#1b2333; --load:#c0392b;
    --dload:#7d3cc0; --dim:#8a8577; --grid:#e4dfd2; --grid-axis:#b9b19c; --settle:#0b7285;
  }
  :root[data-theme="dark"]{
    --paper:#12151c; --paper-2:#0d1016; --ink:#e9ecf5; --ink-soft:#9aa3b8;
    --line:#2a3040; --line-soft:#20252f; --panel:#171b24; --panel-2:#12161e;
    --accent:#8b9bff; --accent-2:#a6b2ff; --accent-soft:#232a3f;
    --truss:#ff6b5a; --beam:#9aa1b3; --support:#cdd3e2; --load:#ff6b5a;
    --dload:#c48bff; --dim:#6b7386; --grid:#20252f; --grid-axis:#39414f; --settle:#3bc9db;
  }
  *{box-sizing:border-box}
  html,body{height:100%}
  body{
    margin:0; font-family:var(--serif);
    color:var(--ink); background:var(--paper);
    display:flex; flex-direction:column; overflow:hidden;
    -webkit-font-smoothing:antialiased;
  }
  /* ---------- Header ---------- */
  header{
    display:flex; align-items:center; gap:16px; padding:10px 18px;
    background:linear-gradient(180deg,var(--panel),var(--panel-2));
    border-bottom:1px solid var(--line);
  }
  .brand{display:flex; align-items:baseline; gap:10px; user-select:none}
  .brand .facemark{width:80px; height:auto; color:var(--ink); flex-shrink:0; align-self:center}
  .brand .facemark .delx-eye{transition:transform .12s ease-out}
  .brand .logo{
    font-family:var(--serif); font-weight:700; font-size:26px; letter-spacing:.5px;
    color:var(--ink);
  }
  .brand .logo em{font-style:italic; color:var(--accent)}
  .brand .tag{font-size:12px; color:var(--ink-soft); font-family:var(--serif); font-style:italic}
  .struct-badge{
    margin-left:4px; font-family:var(--serif); font-size:13px; color:var(--accent);
    background:var(--accent-soft); border:1px solid var(--line);
    padding:3px 11px; border-radius:999px; white-space:nowrap;
  }
  header .spacer{flex:1}
  .hbtns{display:flex; gap:8px; align-items:center}
  /* ---------- Buttons ---------- */
  .btn{
    font:inherit; font-size:13px; color:var(--ink); cursor:pointer;
    background:var(--panel); border:1px solid var(--line); border-radius:8px;
    padding:7px 12px; display:inline-flex; align-items:center; gap:7px;
    transition:.12s ease; white-space:nowrap; line-height:1;
  }
  .btn:hover{border-color:var(--accent); color:var(--accent)}
  .btn:active{transform:translateY(1px)}
  .btn:disabled{opacity:.4; cursor:not-allowed; pointer-events:none}
  .btn.primary{background:var(--accent); border-color:var(--accent); color:#fff}
  .btn.primary:hover{background:var(--accent-2); color:#fff}
  .btn.ghost{background:transparent}
  .btn.icon{width:34px; height:34px; padding:0; justify-content:center}
  .btn .k{font-size:15px}
  /* ---------- Layout ---------- */
  .stage{flex:1; display:flex; min-height:0}
  /* Tool rail */
  .rail{
    width:210px; flex-shrink:0; background:var(--panel); border-right:1px solid var(--line);
    padding:14px 12px; overflow-y:auto; display:flex; flex-direction:column; gap:18px;
    transition:width .16s ease, padding .16s ease, opacity .12s ease;
  }
  /* Panel plegable: el contenido del rail/inspector se colapsa a 0 (recortado
     por overflow, sin reflow raro de los inputs al 100%), pero el ASA que lo
     abre y cierra queda SIEMPRE visible entre el panel y el lienzo — así el
     usuario nunca pierde de dónde volver a abrirlo. */
  .rail.collapsed{width:0; padding-left:0; padding-right:0; border-right:none; opacity:0; overflow:hidden}
  .panel-handle{
    width:14px; flex-shrink:0; align-self:stretch; padding:0; margin:0;
    background:var(--panel); border:none; border-right:1px solid var(--line);
    color:var(--ink-soft); cursor:pointer; display:flex; align-items:center; justify-content:center;
    font-size:12px; transition:.12s; position:relative; z-index:5;
  }
  .panel-handle:hover{background:var(--accent-soft); color:var(--accent)}
  .panel-handle span{transition:transform .16s ease}
  .panel-handle.collapsed span{transform:scaleX(-1)}
  #inspectorHandle{border-right:none; border-left:1px solid var(--line)}
  .group{display:flex; flex-direction:column; gap:8px}
  .group > .lbl{
    font-family:var(--serif); font-size:11px; letter-spacing:.14em; text-transform:uppercase;
    color:var(--ink-soft); font-weight:700; padding-left:2px;
  }
  .tool{
    display:flex; align-items:center; gap:11px; padding:9px 11px; border-radius:9px;
    border:1px solid transparent; cursor:pointer; font-size:13.5px; color:var(--ink);
    background:transparent; text-align:left; transition:.12s; user-select:none;
  }
  .tool:hover{background:var(--panel-2)}
  .tool .ic{
    width:26px; height:26px; flex-shrink:0; display:grid; place-items:center;
    border-radius:7px; background:var(--panel-2); color:var(--ink-soft);
  }
  .tool .ic svg{width:17px; height:17px}
  .tool .key{margin-left:auto; font-size:10.5px; color:var(--ink-soft); font-family:var(--mono);
    border:1px solid var(--line); border-radius:5px; padding:1px 5px}
  .tool.active{background:var(--accent-soft); border-color:var(--accent); color:var(--accent); font-weight:600}
  .tool.active .ic{background:var(--accent); color:#fff}
  .tool.danger.active{background:rgba(180,64,47,.12); border-color:var(--danger); color:var(--danger)}
  .tool.danger.active .ic{background:var(--danger)}
  .seg{display:flex; background:var(--panel-2); border:1px solid var(--line); border-radius:8px; padding:3px; gap:3px}
  .seg button{
    flex:1; font:inherit; font-size:12.5px; padding:6px 4px; border:none; border-radius:6px;
    background:transparent; color:var(--ink-soft); cursor:pointer; transition:.12s;
  }
  .seg button.on{background:var(--panel); color:var(--accent); box-shadow:var(--shadow); font-weight:600}
  .row{display:flex; gap:8px; align-items:center}
  .toggle{
    display:flex; align-items:center; justify-content:space-between; gap:10px;
    font-size:13px; padding:7px 9px; border-radius:8px; cursor:pointer; user-select:none;
  }
  .toggle:hover{background:var(--panel-2)}
  .switch{width:36px; height:20px; border-radius:999px; background:var(--line); position:relative; transition:.15s; flex-shrink:0}
  .switch::after{content:""; position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:50%; background:#fff; transition:.15s; box-shadow:0 1px 2px rgba(0,0,0,.3)}
  .toggle.on .switch{background:var(--accent)}
  .toggle.on .switch::after{left:18px}
  /* Selección múltiple: la propiedad no coincide en todos los elementos. El
     interruptor se dibuja a medias para no afirmar ni un sí ni un no. */
  .toggle.mix .switch{background:var(--ink-soft)}
  .toggle.mix .switch::after{left:10px}
  /* Canvas */
  .canvas-wrap{flex:1; position:relative; min-width:0; background:var(--paper)}
  /* Barra de herramientas flotante (solo iconos) en la parte superior del lienzo */
  .toolbar{
    position:absolute; top:12px; left:50%; transform:translateX(-50%); z-index:20;
    display:flex; gap:3px; align-items:center;
    background:var(--panel); border:1px solid var(--line); border-radius:12px;
    padding:5px; box-shadow:var(--shadow); max-width:calc(100% - 24px);
  }
  .tbtn{
    width:38px; height:38px; border-radius:9px; border:1px solid transparent; flex-shrink:0;
    background:transparent; color:var(--ink-soft); cursor:pointer;
    display:grid; place-items:center; transition:.12s;
  }
  .tbtn svg{width:20px; height:20px}
  .tbtn:hover{background:var(--panel-2); color:var(--ink)}
  .tbtn.active{background:var(--accent); color:#fff; border-color:var(--accent)}
  .tbtn.danger:hover{color:var(--danger)}
  .tbtn.danger.active{background:var(--danger); color:#fff; border-color:var(--danger)}
  .tsep{width:1px; height:22px; background:var(--line); margin:0 3px; flex-shrink:0}
  .tbtn-seg{display:flex; gap:2px; margin-left:2px}
  .tbtn-seg button{
    font:inherit; font-size:11.5px; padding:0 9px; height:38px; border-radius:9px; border:1px solid var(--line);
    background:var(--panel-2); color:var(--ink-soft); cursor:pointer; transition:.12s;
  }
  .tbtn-seg button.on{background:var(--accent-soft); border-color:var(--accent); color:var(--accent); font-weight:600}
  svg#board{width:100%; height:100%; display:block; touch-action:none; cursor:default}
  svg#board.mode-node{cursor:crosshair}
  svg#board.mode-connect,svg#board.mode-nload,svg#board.mode-dload{cursor:pointer}
  svg#board.mode-delete{cursor:not-allowed}
  svg#board.panning{cursor:grabbing}
  .hud{
    position:absolute; left:14px; bottom:12px; display:flex; gap:8px; align-items:center;
    font-size:12px; color:var(--ink-soft); font-family:var(--mono);
    background:var(--panel); border:1px solid var(--line); border-radius:8px; padding:5px 10px;
    box-shadow:var(--shadow); pointer-events:none;
  }
  .zoomctl{position:absolute; right:14px; bottom:12px; display:flex; gap:6px}
  .hint{
    position:absolute; left:50%; top:62px; transform:translateX(-50%);
    background:var(--ink); color:var(--paper); font-size:12.5px; padding:7px 14px;
    border-radius:999px; box-shadow:var(--shadow); opacity:0; transition:.2s; pointer-events:none;
    font-family:var(--serif); z-index:19; white-space:nowrap; max-width:calc(100% - 24px); overflow:hidden; text-overflow:ellipsis;
  }
  .hint.show{opacity:.94}
  /* Inspector */
  .inspector{
    width:340px; flex-shrink:0; background:var(--panel); border-left:1px solid var(--line);
    display:flex; flex-direction:column; min-height:0;
    transition:width .16s ease, opacity .12s ease;
  }
  .inspector.collapsed{width:0; border-left:none; opacity:0; overflow:hidden}
  .tabs{display:flex; border-bottom:1px solid var(--line)}
  .tabs button{
    flex:1; font:inherit; font-size:13px; padding:11px; border:none; background:transparent;
    color:var(--ink-soft); cursor:pointer; font-family:var(--serif); letter-spacing:.02em;
    border-bottom:2px solid transparent;
  }
  .tabs button.on{color:var(--accent); border-bottom-color:var(--accent); font-weight:700}
  .tabpane{flex:1; overflow-y:auto; padding:16px; display:none}
  .tabpane.on{display:block}
  .card{background:var(--panel-2); border:1px solid var(--line); border-radius:var(--radius); padding:14px; margin-bottom:14px}
  .card h3{margin:0 0 12px; font-family:var(--serif); font-size:15px; font-weight:700; display:flex; align-items:center; gap:8px}
  .card h3 .dot{width:9px; height:9px; border-radius:50%; background:var(--accent)}
  .field{display:flex; flex-direction:column; gap:5px; margin-bottom:11px}
  .field label{font-size:12px; color:var(--ink-soft); font-weight:600}
  .field .fx{font-family:var(--serif); font-style:italic}
  input[type=text],input[type=number],select{
    font:inherit; font-size:13px; color:var(--ink); background:var(--panel);
    border:1px solid var(--line); border-radius:7px; padding:8px 10px; width:100%;
  }
  input:focus,select:focus{outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft)}
  /* campo con expresión aritmética inválida: el modelo conserva el valor
     anterior, así que se marca el campo en vez de aceptar una entrada rota */
  input.numexpr.bad{border-color:var(--danger); color:var(--danger)}
  /* tabla de numeración (nudos y GDL) del método con restricciones cinemáticas */
  .numtable{width:100%; border-collapse:collapse; font-size:12.5px}
  .numtable th,.numtable td{padding:3px 4px; text-align:center; border-bottom:1px solid var(--line)}
  .numtable th{color:var(--ink-soft); font-weight:600}
  .numtable td:first-child,.numtable th:first-child{text-align:left}
  .numtable input{width:100%; padding:4px 5px; text-align:center}
  .numtable .fixedcell{color:var(--ink-soft)}
  .numtable input::placeholder{color:var(--ink-soft); opacity:.55}
  /* aviso de numeración: ámbar cuando solo avisa (nudos), rojo cuando impide
     calcular (GDL) — el color distingue de un vistazo "revisa esto" de "esto
     no se va a calcular" */
  .numwarn{margin-top:8px; padding:8px 10px; border-radius:7px; font-size:12px; line-height:1.5;
    border:1px solid var(--warn,#b8860b); background:rgba(184,134,11,.10); color:var(--ink)}
  .numwarn.block{border-color:var(--danger); background:rgba(180,64,47,.10)}
  .numwarn b{display:block; margin-bottom:3px}
  .numwarn ul{margin:0 0 4px 16px; padding:0}
  .grid2{display:grid; grid-template-columns:1fr 1fr; gap:10px}
  .muted{color:var(--ink-soft); font-size:12.5px; line-height:1.6}
  .empty{text-align:center; color:var(--ink-soft); padding:30px 10px; font-family:var(--serif); font-style:italic}
  .empty .big{font-size:34px; opacity:.5; margin-bottom:8px}
  .loadlist{display:flex; flex-direction:column; gap:6px}
  .loadchip{display:flex; align-items:center; gap:8px; font-size:12.5px; background:var(--panel); border:1px solid var(--line); border-radius:7px; padding:6px 9px}
  .loadchip .rm{margin-left:auto; cursor:pointer; color:var(--danger); font-weight:700; border:none; background:none; font-size:15px; line-height:1}
  /* edición de cargas directamente en el panel de propiedades (sin ventana emergente) */
  .loadedit{display:flex; flex-direction:column; gap:7px; background:var(--panel); border:1px solid var(--line); border-radius:7px; padding:9px}
  .loadedit .loadedit-head{display:flex; align-items:center; justify-content:space-between; font-size:12px; color:var(--ink-soft); font-weight:600}
  .loadedit .loadedit-head .rm{cursor:pointer; color:var(--danger); font-weight:700; border:none; background:none; font-size:15px; line-height:1}
  .loadedit .loadedit-head .secName{flex:1; min-width:0; border:none; background:transparent; color:var(--ink); font:inherit; font-weight:600; padding:2px 4px; border-radius:5px}
  .loadedit .loadedit-head .secName:hover,.loadedit .loadedit-head .secName:focus{background:var(--panel-2); outline:none}
  .toggle.disabled{cursor:not-allowed; opacity:.65}
  .toggle.disabled:hover{background:transparent}
  .codebar{display:flex; gap:8px; padding:12px 14px; border-bottom:1px solid var(--line); flex-wrap:wrap}
  pre#code{
    flex:1; margin:0; overflow:auto; padding:14px 16px; font-family:var(--mono); font-size:11.5px;
    line-height:1.55; color:var(--ink); background:var(--paper-2); white-space:pre; tab-size:2;
  }
  pre#code .cmd{color:var(--accent)}
  pre#code .cm{color:var(--ink-soft); font-style:italic}
  pre#code .env{color:var(--dload); font-weight:600}
  .toast{
    position:fixed; bottom:20px; left:50%; transform:translateX(-50%) translateY(20px);
    background:var(--ink); color:var(--paper); padding:10px 18px; border-radius:10px;
    font-size:13px; box-shadow:var(--shadow); opacity:0; transition:.25s; pointer-events:none; z-index:50;
  }
  .toast.show{opacity:1; transform:translateX(-50%) translateY(0)}
  .stat{display:flex; justify-content:space-between; font-size:12.5px; padding:4px 0; border-bottom:1px dashed var(--line-soft)}
  .stat:last-child{border:none}
  .stat b{font-family:var(--serif)}
  .minibtn{font-size:11.5px; padding:4px 9px}
  .quickdirs{display:flex; gap:6px; margin-top:7px}
  .quickdirs button{
    flex:1; font:inherit; font-size:12px; padding:6px 0; border:1px solid var(--line);
    border-radius:7px; background:var(--panel-2); color:var(--ink-soft); cursor:pointer; transition:.12s;
  }
  .quickdirs button:hover{border-color:var(--accent); color:var(--accent); background:var(--accent-soft)}
  .quickdirs button.on{border-color:var(--accent); color:var(--accent); background:var(--accent-soft); font-weight:600}
  /* visor de PDF compilado */
  .pdf-back{
    position:fixed; inset:0; background:rgba(10,14,22,.62); z-index:95;
    display:none; align-items:center; justify-content:center;
  }
  .pdf-back.open{display:flex}
  .pdf-panel{
    /* 93dvh en vez de 93vh: en Safari de iOS, vh mide el viewport SIN la barra
       de direcciones, así que con la barra visible el panel se sale por abajo y
       recorta los botones. dvh sigue el alto realmente disponible; la línea vh
       queda antes como reserva para navegadores que no lo entiendan. */
    width:min(1100px,95vw); height:93vh; height:93dvh; background:var(--panel);
    border:1px solid var(--line); border-radius:14px; overflow:hidden;
    display:flex; flex-direction:column; box-shadow:0 12px 60px rgba(0,0,0,.4);
  }
  .pdf-bar{
    display:flex; gap:8px; align-items:center; padding:8px 12px;
    border-bottom:1px solid var(--line); font-size:12.5px; color:var(--ink-soft);
  }
  .pdf-bar .t{font-family:var(--serif); font-weight:700; color:var(--ink); font-size:13.5px}
  .pdf-body{flex:1; position:relative; background:#525659}
  #pdfFrame{position:absolute; inset:0; width:100%; height:100%; border:none}
  .pdf-status{
    position:absolute; inset:0; display:flex; flex-direction:column; gap:10px;
    align-items:center; justify-content:center; color:#eee; font-size:14px;
    font-family:var(--serif); pointer-events:none; transition:.3s;
  }
  .pdf-status.hide{opacity:0}
  /* ---- cargador del visor de PDF: el logo aguantando la carga ----
     El logo de DELX ya ES un pórtico con ojos, así que en vez de un reloj
     giratorio se le pone encima una carga (las mismas flechas que la app dibuja
     sobre las barras) y se le nota el esfuerzo: la estructura se acorta
     (scaleY), se ensancha un poco, tiembla justo en el pico y aprieta los ojos.
     Un único ciclo de 1.6 s sincroniza todas las capas. */
  .delx-loader{width:132px; height:auto; overflow:visible}
  .delx-loader .ld-arrows line{stroke:#e8a33d; stroke-width:2.1; stroke-linecap:round}
  .delx-loader .ld-arrows polygon{fill:#e8a33d}
  .delx-loader .ld-eye{fill:currentColor}
  .delx-loader .ld-drop{fill:#63c8ff; stroke:#0d5c8a; stroke-width:.9}
  .delx-loader .ld-shine{fill:#fff; opacity:.9}
  /* cada capa se anima sobre su propio origen */
  .delx-loader .ld-body,.delx-loader .ld-eye,.delx-loader .ld-sweat{transform-box:fill-box}
  .delx-loader .ld-body{transform-origin:50% 100%; animation:ldSquash 1.6s ease-in-out infinite}
  .delx-loader .ld-shake{animation:ldTremble 1.6s linear infinite}
  .delx-loader .ld-load{animation:ldPress 1.6s ease-in-out infinite}
  .delx-loader .ld-eye{transform-origin:center; animation:ldSquint 1.6s ease-in-out infinite}
  /* fill-mode backwards: durante el retardo se aplica ya el fotograma 0% (gota
     invisible). Sin esto, la gota retrasada se queda VISIBLE y quieta hasta que
     arranca su animación. */
  .delx-loader .ld-sweat{transform-origin:center top; animation:ldSweat 1.6s ease-in-out infinite backwards}
  .delx-loader .ld-sweat2{animation-delay:.22s}
  /* la estructura acusa la carga: se acorta y se abomba */
  @keyframes ldSquash{
    0%,100%{transform:scale(1,1)}
    20%{transform:scale(1.015,.975)}
    50%{transform:scale(1.032,.944)}
    70%{transform:scale(1.012,.982)}
    86%{transform:scale(.996,1.009)}     /* rebote al recuperarse */
  }
  /* tembleque concentrado en el momento de máximo esfuerzo */
  @keyframes ldTremble{
    0%,36%,68%,100%{transform:translateX(0)}
    40%{transform:translateX(-.5px)} 44%{transform:translateX(.55px)}
    48%{transform:translateX(-.55px)} 52%{transform:translateX(.5px)}
    56%{transform:translateX(-.4px)} 62%{transform:translateX(.28px)}
  }
  @keyframes ldPress{
    0%,100%{transform:translateY(0)}
    20%{transform:translateY(.9px)}
    50%{transform:translateY(2.3px)}
    70%{transform:translateY(.8px)}
    86%{transform:translateY(-.5px)}
  }
  @keyframes ldSquint{
    0%,14%{transform:scaleY(1)}
    34%{transform:scaleY(.55)}
    50%,62%{transform:scaleY(.16)}       /* ojos apretados: >_< */
    80%{transform:scaleY(.85)}
    100%{transform:scaleY(1)}
  }
  /* Movimiento reducido: un cargador CONGELADO es mala señal (no se distingue de
     la app colgada), así que en vez de pararlo todo se quitan los gestos bruscos
     —tembleque y apretar los ojos— y queda un esfuerzo lento y suave que sigue
     diciendo "estoy trabajando".
     OJO al depurar: Chrome headless activa esta preferencia POR DEFECTO, y
     Windows la activa con Configuración → Accesibilidad → Efectos visuales →
     "Efectos de animación" desactivado. */
  /* la gota: brota de golpe en el pico del esfuerzo, resbala y cae.
     Se ve durante ~1 s de cada 1.6 s para que dé tiempo a notarla. */
  @keyframes ldSweat{
    0%,24%{opacity:0; transform:translateY(-2px) scale(.35)}
    32%{opacity:1; transform:translateY(0) scale(1.2)}       /* brota */
    40%{opacity:1; transform:translateY(1px) scale(1)}
    68%{opacity:1; transform:translateY(13px) scale(1)}      /* resbala */
    86%{opacity:.55; transform:translateY(21px) scale(.9)}
    94%,100%{opacity:0; transform:translateY(26px) scale(.75)}
  }
  @media (prefers-reduced-motion: reduce){
    .delx-loader .ld-shake,.delx-loader .ld-eye,.delx-loader .ld-sweat{animation:none !important}
    .delx-loader .ld-sweat{opacity:0}
    .delx-loader .ld-body,.delx-loader .ld-load{animation-duration:3.4s}
  }
  /* menú desplegable (Ejemplo) */
  .dropdown{position:relative}
  .dropdown .menu{
    position:absolute; top:calc(100% + 6px); right:0; z-index:30; min-width:230px;
    background:var(--panel); border:1px solid var(--line); border-radius:10px;
    box-shadow:var(--shadow); padding:6px; display:none;
  }
  .dropdown .menu.open{display:block}
  .dropdown .menu button{
    display:flex; flex-direction:column; align-items:flex-start; gap:2px; width:100%;
    font:inherit; font-size:13px; text-align:left; padding:8px 10px; border:none;
    border-radius:7px; background:transparent; color:var(--ink); cursor:pointer;
    font-family:var(--serif); font-weight:600;
  }
  .dropdown .menu button span{font-size:11.5px; color:var(--ink-soft); font-family:system-ui; font-weight:400}
  .dropdown .menu button:hover{background:var(--accent-soft); color:var(--accent)}
  /* resultados */
  .restable{width:100%; border-collapse:collapse; font-size:12px; margin-top:6px}
  .restable th,.restable td{text-align:right; padding:4px 7px; border-bottom:1px solid var(--line-soft)}
  .restable th{color:var(--ink-soft); font-weight:600; font-family:var(--serif)}
  .restable td:first-child,.restable th:first-child{text-align:left; font-family:var(--serif); font-style:italic}
  .restable td{font-family:var(--mono)}
  .restable tr:hover td{background:var(--panel-2)}
  .pos{color:#1f9d55} .neg{color:#c0392b}
  .slider{width:100%; accent-color:var(--accent)}
  .segsm button{font-size:11.5px; padding:5px 3px}
  .analyze-note{font-size:11.5px; color:var(--ink-soft); line-height:1.5; margin-top:6px}
  .legend{display:flex; gap:12px; flex-wrap:wrap; font-size:11px; color:var(--ink-soft); margin-top:8px}
  .legend span{display:inline-flex; align-items:center; gap:5px}
  .legend i{width:14px; height:3px; border-radius:2px; display:inline-block}
  .okbadge{font-size:11px; padding:2px 8px; border-radius:999px; background:rgba(31,157,85,.14); color:#1f9d55; border:1px solid rgba(31,157,85,.3)}
  .errbadge{font-size:12px; padding:8px 11px; border-radius:8px; background:rgba(192,57,43,.12); color:#c0392b; border:1px solid rgba(192,57,43,.3)}
  @media (max-width:1080px){ .inspector{width:290px} .rail{width:184px} }

  /* ============================================================
     PANTALLA ANGOSTA (móvil)
     Los tres paneles en fila no caben: rail e inspector juntos ya suman más
     que el ancho de un teléfono y al lienzo no le quedaría sitio. Aquí los dos
     laterales dejan de robar ancho y pasan a FLOTAR sobre el dibujo, que así
     ocupa la pantalla entera. El asa de cada uno sigue en el flujo, pegada a
     su borde, para que nunca se pierda por dónde volver a abrirlos.
     ============================================================ */
  @media (max-width:820px){
    /* La cabecera es una sola fila sin envolver: en un teléfono la marca se
       come el ancho y los botones de la derecha —incluido «Analizar»— quedaban
       FUERA de la pantalla, sin ninguna otra forma de llegar a ellos. Se quita
       lo decorativo (el subtítulo y el badge, que repite lo que ya dice el
       panel de Resultados), se encoge el logotipo y, como red de seguridad, se
       deja que la fila se parta en dos antes que recortar nada. */
    header{flex-wrap:wrap; gap:10px; padding:10px 12px}
    /* «LaTeX» se va: hace lo mismo que la pestaña LaTeX del panel, y cada
       botón que sobra en la cabecera es una fila más robada al dibujo */
    .brand .tag,.struct-badge,#genBtn{display:none}
    .brand .facemark{width:44px}
    .brand .logo{font-size:22px}
    header .spacer{flex:1 1 0; min-width:0}
    .hbtns{flex-wrap:wrap; justify-content:flex-end}

    .stage{position:relative; --handle-w:26px}
    .rail,.inspector{
      position:absolute; top:0; bottom:0; z-index:30;
      width:min(80vw,300px); box-shadow:0 10px 40px rgba(0,0,0,.34);
    }
    .rail{left:var(--handle-w)}
    .inspector{right:var(--handle-w)}
    .rail.collapsed,.inspector.collapsed{width:0; box-shadow:none}
    /* el asa es lo único que queda para reabrir el panel, y 14 px no se
       aciertan con el dedo */
    .panel-handle{width:var(--handle-w); font-size:15px}
    /* la barra flotante se reparte en varias filas en vez de desbordarse:
       con el rail plegado es la única forma de llegar a las herramientas */
    /* La barra se queda en UNA fila que se desliza en horizontal, en vez de
       envolverse: con los botones a 44 px, envolver la convertía en un bloque
       de tres filas que tapaba medio dibujo. Deslizar mantiene el lienzo libre
       y además absorbe los grupos que aparecen según la herramienta (Viga/
       Armadura, Fuerza/Momento…), que si no añadirían más filas todavía.
       #toolGroup es otro flex con estilo en línea: si no se le dice que no
       envuelva, se desborda por su cuenta y deja la papelera fuera. */
    .toolbar{
      flex-wrap:nowrap; justify-content:flex-start; max-width:calc(100% - 68px);
      overflow-x:auto; overflow-y:hidden; scrollbar-width:none;
    }
    .toolbar::-webkit-scrollbar{display:none}
    .toolbar #toolGroup{flex-wrap:nowrap}
    .toolbar .tbtn,.toolbar .tbtn-seg button,.toolbar .tsep{flex:0 0 auto}
    /* la barra del visor de PDF tampoco envolvía: el título largo empujaba
       «Recompilar / Pestaña / Cerrar» fuera del panel */
    .pdf-bar{flex-wrap:wrap}
    .pdf-note{display:none}
    .pdf-panel{width:96vw; border-radius:10px}
  }

  /* ============================================================
     PUNTERO GRUESO (dedo)
     Se consulta el TIPO DE PUNTERO, no el ancho: una tableta grande también se
     toca con el dedo y necesita lo mismo que un teléfono. La referencia de
     ~44 px es el mínimo cómodo para tocar sin fallar; con el ratón esos
     tamaños serían aparatosos, por eso van aquí y no en la regla general.
     ============================================================ */
  @media (pointer:coarse){
    /* min-height en vez de solo relleno: la altura de un .btn depende de su
       fuente (line-height:1), y ajustándola a mano se queda en 37 px. Así los
       44 px están garantizados pase lo que pase con la tipografía. */
    .btn{padding:11px 14px; min-height:44px}
    .btn.icon{width:44px; height:44px}
    .minibtn{padding:10px 12px; font-size:12.5px}
    .tbtn{width:44px; height:44px}
    .tbtn svg{width:22px; height:22px}
    .tbtn-seg button{height:44px; padding:0 12px; font-size:12.5px}
    .seg button{padding:12px 6px}
    .segsm button{padding:10px 4px}
    .tabs button{padding:15px 11px}
    .tool{padding:12px 11px}
    .quickdirs button{padding:11px 0}
    .toggle{padding:11px 9px}
    .loadchip .rm,.loadedit .loadedit-head .rm{font-size:20px; padding:2px 10px}
    .panel-handle{min-width:26px}
    /* Un range de altura por defecto es una línea fina imposible de agarrar */
    .slider{height:34px}
    /* 16 px es el umbral por debajo del cual Safari en iOS AMPLÍA la página al
       enfocar un campo — y luego la deja desplazada y descuadrada. Subirlo es
       la forma de evitar ese salto, no una cuestión de estética. */
    input[type=text],input[type=number],select{padding:12px 10px; font-size:16px}
    .numtable input{padding:8px 5px; font-size:15px}
  }

  /* ============================================================
     HAY TÁCTIL DISPONIBLE (aunque no sea el puntero principal)
     `any-pointer` en vez de `pointer`: un portátil con pantalla táctil apunta
     sobre todo con el trackpad (puntero FINO), así que no le vienen bien los
     botones de 44 px de arriba, pero sus dedos sufren exactamente los mismos
     problemas de menú nativo y de zoom por doble toque. Estas reglas no
     molestan con el ratón, así que se aplican en cuanto haya un dedo posible.
     ============================================================ */
  @media (any-pointer:coarse){
    /* Sin esto, mantener el dedo sobre un botón o sobre el lienzo abre el menú
       nativo de selección/copia del navegador en mitad del gesto. */
    .btn,.tbtn,.tool,.seg button,.tabs button,.panel-handle,.quickdirs button,
    .tbtn-seg button,.toggle,svg#board{
      -webkit-touch-callout:none; -webkit-user-select:none; user-select:none;
    }
    /* Quita el zoom por doble toque (y su retardo de 300 ms) SOLO en los
       controles: el pellizco para ampliar la página sigue disponible, que es
       una ayuda de accesibilidad y no se debe desactivar. */
    .btn,.tbtn,.tool,.seg button,.tabs button,.panel-handle,.quickdirs button,
    .tbtn-seg button,.toggle,.tabs,.dropdown .menu button{touch-action:manipulation}
  }

  /* ============================================================
     PORTADA / LANDING (primera página) — diseño minimalista
     ============================================================ */
  .landing{
    position:fixed; inset:0; z-index:200; overflow-y:auto;
    background:
      radial-gradient(1100px 560px at 50% -12%, var(--accent-soft), transparent 62%),
      linear-gradient(180deg, var(--paper), var(--paper-2));
    color:var(--ink); animation:lpIn .4s ease;
  }
  .landing.hidden{display:none}
  .landing.leaving{animation:lpOut .3s ease forwards}
  @keyframes lpIn{from{opacity:0}to{opacity:1}}
  @keyframes lpOut{to{opacity:0; transform:translateY(-10px)}}
  .lp-inner{max-width:1080px; margin:0 auto; padding:22px 26px 84px; display:flex; flex-direction:column; gap:56px}
  .lp-top{display:flex; align-items:center; justify-content:space-between; gap:12px}
  .lp-brand{display:flex; align-items:center; gap:11px; user-select:none}
  .lp-brand .facemark{width:42px; height:auto; color:var(--ink)}
  .lp-name{font-family:var(--serif); font-weight:700; font-size:23px; letter-spacing:.5px}
  .lp-name em{font-style:italic; color:var(--accent)}
  .landing .facemark .delx-eye{transition:transform .12s ease-out}
  /* hero */
  .lp-hero{display:flex; flex-direction:column; align-items:center; text-align:center; gap:18px; padding:34px 0 6px}
  .lp-hero .facemark{width:118px; height:auto; color:var(--ink)}
  .lp-hero h1{margin:0; font-family:var(--serif); font-weight:700; font-size:clamp(32px,6vw,52px); line-height:1.08; letter-spacing:-.5px}
  .lp-hero h1 em{font-style:italic; color:var(--accent)}
  .lp-sub{margin:0; max-width:640px; font-size:clamp(15px,2.4vw,18px); color:var(--ink-soft); line-height:1.6}
  .lp-cta{display:flex; gap:12px; flex-wrap:wrap; justify-content:center; margin-top:8px}
  .lp-cta .btn{font-size:15px; padding:12px 24px; border-radius:11px}
  /* secciones */
  .lp-section{display:flex; flex-direction:column; gap:18px}
  .lp-section > h2{
    margin:0; font-family:var(--serif); font-size:12.5px; font-weight:700;
    letter-spacing:.17em; text-transform:uppercase; color:var(--ink-soft);
  }
  .lp-grid{display:grid; gap:14px}
  .lp-grid.features{grid-template-columns:repeat(auto-fit,minmax(215px,1fr))}
  .lp-grid.work{grid-template-columns:repeat(auto-fit,minmax(248px,1fr))}
  .lp-feat{background:var(--panel); border:1px solid var(--line); border-radius:14px; padding:19px; display:flex; flex-direction:column; gap:9px}
  .lp-feat .ic{width:38px; height:38px; border-radius:10px; display:grid; place-items:center; background:var(--accent-soft); color:var(--accent); font-size:19px}
  .lp-feat h3{margin:0; font-size:14.5px; font-weight:700}
  .lp-feat p{margin:0; font-size:12.8px; color:var(--ink-soft); line-height:1.55}
  /* tarjetas de trabajo (ejemplos, clic → app) */
  .lp-card{
    text-align:left; font:inherit; color:var(--ink); cursor:pointer;
    background:var(--panel); border:1px solid var(--line); border-radius:14px;
    padding:0; overflow:hidden; display:flex; flex-direction:column; transition:.16s ease;
  }
  .lp-card:hover{border-color:var(--accent); transform:translateY(-3px); box-shadow:var(--shadow)}
  .lp-card:active{transform:translateY(-1px)}
  .lp-card .thumb{height:118px; background:var(--paper-2); border-bottom:1px solid var(--line); display:grid; place-items:center}
  .lp-card .thumb svg{width:80%; height:80%}
  .lp-card .meta{padding:13px 16px 15px; display:flex; flex-direction:column; gap:3px}
  .lp-card .meta .t{font-family:var(--serif); font-weight:700; font-size:15px}
  .lp-card .meta .d{font-size:12.3px; color:var(--ink-soft); line-height:1.5}
  .lp-card .meta .g{
    margin-top:8px; align-self:flex-start; font-size:10.5px; font-family:var(--mono);
    color:var(--accent); background:var(--accent-soft); border:1px solid var(--line);
    padding:2px 9px; border-radius:999px;
  }
  .lp-foot{text-align:center; color:var(--ink-soft); font-size:12px; font-family:var(--serif); font-style:italic; padding-top:6px}
  @media (max-width:560px){ .lp-inner{padding:18px 16px 60px; gap:42px} }
