/* ============ Paleta y temas ============ */
:root{
  color-scheme:light;
  --bg:#ffffff; --surface:#f4f3fb; --ink:#101014; --ink2:#4a4a52; --muted:#6f6f78;
  --grid:#e4e2ee; --axis:#c7c3d6; --border:rgba(16,16,20,.12);
  --s1:#3446d4; --s2:#15803d; --s3:#be185d; --s4:#c98a00; --s5:#0f766e; --s6:#c2410c; --s7:#9333ea; --s8:#b91c1c;
  --seq100:#e4defb; --seq200:#c9baf5; --seq300:#a68eee; --seq400:#8265e2; --seq500:#5f45bf; --seq600:#453096; --seq700:#2c1e66;
  --cell:46px; --cellgap:6px;
  font-size:16px;
  /* acentos de semestre — NO confundir con --s1..--s8, que son colores de serie */
  --sem1:#1baf7a; --sem2:#2a78d6; --sem3:#4a3aa7; --sem-actual:#eda100;
  --pendiente:#86868f;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]){
    color-scheme:dark;
    --bg:#0a0a0d; --surface:#201f2c; --ink:#f5f4fa; --ink2:#c9c7d6; --muted:#8d8b9c;
    --grid:#2b2938; --axis:#3c3950; --border:rgba(245,244,250,.14);
    --s1:#5f6ff0; --s2:#34b366; --s3:#ec6bb0; --s4:#eab308; --s5:#2dd4bf; --s6:#fb8b3c; --s7:#c084fc; --s8:#f2685f;
    /* acentos de semestre — NO confundir con --s1..--s8, que son colores de serie */
    --sem1:#1baf7a; --sem2:#2a78d6; --sem3:#4a3aa7; --sem-actual:#eda100;
    --pendiente:#706e7d;
  }
}
:root[data-theme=dark]{
  color-scheme:dark;
  --bg:#0a0a0d; --surface:#201f2c; --ink:#f5f4fa; --ink2:#c9c7d6; --muted:#8d8b9c;
  --grid:#2b2938; --axis:#3c3950; --border:rgba(245,244,250,.14);
  --s1:#5f6ff0; --s2:#34b366; --s3:#ec6bb0; --s4:#eab308; --s5:#2dd4bf; --s6:#fb8b3c; --s7:#c084fc; --s8:#f2685f;
  /* acentos de semestre — NO confundir con --s1..--s8, que son colores de serie */
  --sem1:#1baf7a; --sem2:#2a78d6; --sem3:#4a3aa7; --sem-actual:#eda100;
  --pendiente:#706e7d;
}
:root.presenta{ font-size:20px; --cell:64px; --cellgap:8px; }
/* Modo "dentro del libro" (?libro): la página va embebida en un <iframe> en la
   estantería, que ya pone su propio marco, su título y su botón de cerrar. Sin
   esto el lector vería el header y el pie repetidos dentro del libro. La clase
   la pone core.js al cargar. */
:root.enlibro header,:root.enlibro footer{display:none;}
:root.enlibro main{padding-top:.2rem; max-width:none;}
:root.enlibro .module>h2{margin-top:.4rem;}

/* Caja para los planos con iso:true (circunferencias, angulos, triangulos).
   Con iso el dibujo se dimensiona por el ALTO del lienzo, que es fijo en px,
   asi que en un escritorio ancho la figura queda chica en el medio de un lienzo
   enorme y con dos franjas vacias a los lados. Esto le da una medida comoda y
   la centra. Los controles de la animacion se montan en el mismo nodo, asi que
   quedan centrados debajo de la figura. */
.plot{max-width:520px; margin-inline:auto;}

*{box-sizing:border-box}
/* foco visible: la app se maneja con teclado (1–9, 0, ←/→) y hay que ver dónde se está */
:focus-visible{outline:2px solid var(--s1); outline-offset:2px; border-radius:4px;}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font-family:system-ui,-apple-system,"Segoe UI",sans-serif;
  /* 1.65 y no 1.5: estas páginas son texto técnico denso (definición + nota +
     fórmula, una tras otra). Con 1.5 el interlineado quedaba casi igual al
     espacio entre párrafos y todo se leía como un bloque continuo. */
  line-height:1.65;
}
/* ============ Encabezado y navegación ============
   UNA sola fila: nombre del ramo · fila de unidades · botón de tema. El header
   es sticky, así que cada pixel de alto se descuenta de la pantalla útil
   durante toda la lectura; apilado en dos bandas se comía el 11% del alto.
   En pantallas angostas el flex-wrap manda la fila de unidades a su propia
   línea (el flex-basis de #nav es el umbral). */
header{
  position:sticky; top:0; z-index:50; background:var(--surface);
  border-bottom:1px solid var(--border); padding:.28rem .8rem;
  display:flex; align-items:center; flex-wrap:wrap; gap:.3rem .8rem;
}
header h1{font-size:.95rem; margin:0; line-height:1.2; flex:0 1 auto; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
header h1 small{color:var(--muted); font-weight:500;}
#nav{flex:1 1 300px; min-width:0;}
.hdrbtns{display:flex; gap:.3rem; flex:0 0 auto; margin-left:auto;}
.hdrbtns button{
  border:1px solid var(--border); background:var(--surface); color:var(--ink2);
  font:inherit; font-size:.8rem; padding:.28rem .6rem; border-radius:8px; cursor:pointer;
}
.hdrbtns button:hover{color:var(--ink);}
@keyframes recpulse{50%{box-shadow:0 0 0 4px color-mix(in srgb, var(--s8) 25%, transparent);}}

main{max-width:1150px; margin:0 auto; padding:1rem 1.2rem 4rem;}
.module{display:none;}
.module.active{display:block; animation:fadein .25s ease;}
@keyframes fadein{from{opacity:0; transform:translateY(4px)} to{opacity:1}}
.module>h2{margin:1.6rem 0 .4rem; font-size:1.75rem; line-height:1.2; letter-spacing:-.02em;}
.lead{color:var(--ink2); font-size:1.06rem; line-height:1.6; margin:.4rem 0 2.2rem; max-width:62ch;}

/* ============ Tarjetas y controles ============ */
.card{background:var(--surface); border:1px solid var(--border); border-radius:16px;
  padding:1.5rem 1.7rem; margin:1.6rem 0;}
/* La barrita de color marca "esto es el título de una tarjeta" de un vistazo,
   sin subir el tamaño de fuente: en una página con h2 (tema), h3 (tarjeta) y
   <b> dentro del cuerpo, el peso solo no alcanzaba para separar los niveles. */
.card>h3{margin:0 0 1.1rem; font-size:1.18rem; font-weight:700;
  letter-spacing:-.01em; line-height:1.3; padding-left:.95rem; position:relative;}
.card>h3::before{
  content:""; position:absolute; left:0; top:.18em;
  width:.28rem; height:1em; border-radius:2px; background:var(--s1);
}
.card>h3 code{font-size:.95em;}

/* ---- Ritmo de lectura dentro de una tarjeta ----
   Tres registros, y hay que poder distinguirlos sin leerlos:
     principal  <p> suelto        — 1rem, var(--ink), medida corta
     secundario .note             — .9rem, var(--ink2)
     fuente     .fuente           — .78rem, var(--muted), separada por filete
   El agrupamiento lo hace el espacio, no más decoración: cada bloque abre
   1.15rem, pero un bloque seguido por su .note cierra el aire (regla :has más
   abajo) para que "afirmación + su comentario" se lean como una sola unidad y
   el aire grande caiga ENTRE unidades. */
.card p{margin:0 0 1.15rem; max-width:66ch;}
.card ul,.card ol{margin:0 0 1.25rem; padding-left:1.45rem; max-width:68ch;}
.card li{margin:0 0 .55rem;}
.card li:last-child{margin-bottom:0;}
.card li>ul,.card li>ol{margin:.5rem 0 0;}
.card>*:last-child{margin-bottom:0;}

/* Ejercicios plegables: sin caja propia se leían como texto suelto pegado al
   resto de la tarjeta. El marcador del <summary> queda en color de acento. */
.card details{
  border:1px solid var(--border); border-radius:12px;
  background:color-mix(in srgb, var(--ink) 2%, transparent);
  padding:.9rem 1.1rem; margin:0 0 .7rem;
}
.card details[open]{background:color-mix(in srgb, var(--ink) 3.5%, transparent);}
.card summary{cursor:pointer; font-weight:600; color:var(--ink); line-height:1.5;}
.card summary::marker{color:var(--s1);}
.card details[open]>summary{
  margin-bottom:.95rem; padding-bottom:.75rem; border-bottom:1px solid var(--border);
}
.card details>*:last-child{margin-bottom:0;}
.controls{display:flex; flex-wrap:wrap; gap:.5rem; align-items:center; margin:1.1rem 0;}
.controls label{font-size:.85rem; color:var(--ink2);}
/* Casilla para escribir un valor exacto, al lado de su deslizador. Ancho fijo
   para que una fila con dos o tres controles no se reacomode al teclear. */
.controls input.num{
  width:5.4rem; border:1px solid var(--border); border-radius:8px;
  background:var(--surface); color:var(--ink); font:inherit; font-size:.85rem;
  padding:.24rem .45rem; text-align:right;
}
.controls .unid{font-size:.85rem; color:var(--muted); margin-left:-.25rem;}

/* Lectura de valores en vivo: una magnitud por celda, en grilla que se acomoda
   al ancho (varias columnas en escritorio, una sola en el teléfono). En un solo
   renglón encadenado los números se confunden entre sí. */
.lectura{
  display:grid; grid-template-columns:1fr;
  gap:.1rem 1.4rem; margin:.9rem auto 1.15rem; max-width:520px;
  font-size:.9rem; color:var(--ink2);
}
/* Dos columnas como máximo, y solo si hay ancho. Con auto-fit las cuatro o
   cinco magnitudes se repartían en una única fila a lo ancho de la tarjeta, que
   es exactamente el renglón ilegible que esto venía a resolver. */
@media (min-width:620px){ .lectura{grid-template-columns:1fr 1fr;} }
/* Cada fila mide UNA línea siempre: si un rótulo o un valor largo envuelve, la
   grilla cambia de alto y la tarjeta late al mover el deslizador. Por eso las
   dos mitades van sin envolver, y lo que no entra se recorta. Corolario para
   quien escribe un módulo: rótulos y valores cortos. */
.lectura-item{display:flex; justify-content:space-between; gap:.6rem;
  border-bottom:1px solid var(--border); padding:.2rem 0; min-width:0;}
.lectura-item .k{color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0;}
.lectura-item .v{font-family:ui-monospace,Consolas,monospace; color:var(--ink);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; flex:0 1 auto;}
/* celda de relleno: ocupa su lugar sin pintarse (ver lectura() en core.js) */
.lectura-item.vacio{visibility:hidden;}

/* Párrafo que cambia de texto al mover un control. Sin reservarle el alto del
   caso más largo, pasar de un mensaje de dos líneas a uno de tres empuja hacia
   abajo todo lo que sigue, y al arrastrar el deslizador la página late. */
.card p.vivo{min-height:3.3em;}
@media (max-width:620px){ .card p.vivo{min-height:5.2em;} }
.btn{
  border:1px solid var(--border); background:var(--surface); color:var(--ink);
  font:inherit; font-size:.85rem; padding:.32rem .7rem; border-radius:8px; cursor:pointer;
  transition:background .15s;
}
.btn:hover{background:color-mix(in srgb, var(--ink) 6%, var(--surface));}
.btn.on{background:color-mix(in srgb, var(--s1) 18%, var(--surface)); border-color:var(--s1); font-weight:600;}
.btn.primary{background:var(--s1); border-color:var(--s1); color:#fff; font-weight:600;}
.btn.primary:hover{background:color-mix(in srgb, var(--s1) 85%, var(--ink));}
select,input[type=number],input[type=text]{
  font:inherit; font-size:.85rem; padding:.28rem .45rem; border-radius:8px;
  border:1px solid var(--border); background:var(--surface); color:var(--ink);
}
input[type=range]{accent-color:var(--s1); vertical-align:middle;}
.code{
  font-family:ui-monospace,Consolas,"Cascadia Mono",monospace; font-size:.85rem;
  background:color-mix(in srgb, var(--ink) 5%, var(--surface));
  border:1px solid var(--border);
  border-radius:9px; padding:.65rem .9rem; overflow-x:auto; white-space:pre; margin:.6rem 0;
  line-height:1.55;
}
.code b{color:var(--s1); font-weight:700;}
/* Fórmula inline (dentro de un <li>/<p>, no en .formula/.pasos-tex) cuyo KaTeX
   no tiene ningún punto de quiebre propio (p.ej. un conjunto enumerado largo,
   $\{CCCC,\ CCCS,\dots\}$): sin esta clase, el span inline no puede encogerse
   ni partirse, y si no entra en el ancho disponible se sale de la página en
   vez de ajustarse. display:inline-block + overflow-x:auto le da scroll
   interno propio (mismo patrón que .formula/.pasos-tex/.code), sin tocar la
   fórmula en sí ni la tipografía.  */
.mathinline{display:inline-block; max-width:100%; overflow-x:auto; vertical-align:bottom;}
.note{color:var(--ink2); font-size:.9rem; line-height:1.65; max-width:70ch;}
.note b{color:var(--ink);}
/* una .note cierra su grupo: el aire grande va DESPUÉS de ella, no antes */
.card .note{margin-bottom:1.8rem;}
/* Lo que precede a una .note se le pega. Va después de .card p / .card .note a
   propósito: misma especificidad (0,2,0 — :has() toma la de su argumento), así
   que gana por orden de aparición. Alcanza también a .note+.note (notas
   encadenadas: una sola unidad) y a h3+.note (bajada de la tarjeta). */
.card *:has(+ .note){margin-bottom:.55rem;}
.msg{font-size:.9rem; font-weight:600; min-height:1.4em; margin:.4rem 0;}
.msg.err{color:var(--s8);}
.msg.okc{color:var(--s2);}
.chip{display:inline-block; font-size:.8rem; font-weight:600; padding:.12rem .55rem;
  border-radius:999px; border:1px solid var(--border); margin:.1rem;}
.legend{display:flex; flex-wrap:wrap; gap:.35rem .8rem; align-items:center; font-size:.8rem; color:var(--ink2); margin:.4rem 0;}
.legend .sw{display:inline-block; width:.85em; height:.85em; border-radius:4px; vertical-align:-2px; margin-right:.3em; border:1px solid var(--border);}

@keyframes shake{20%{transform:translateX(-6px)}40%{transform:translateX(6px)}60%{transform:translateX(-4px)}80%{transform:translateX(4px)}}

/* ============ Stepper ============ */
.stepper{display:flex; flex-wrap:wrap; gap:.5rem; align-items:center; margin:1.1rem 0 .4rem;}
.stepper .cnt{font-size:.8rem; color:var(--muted); min-width:5.2em; text-align:center;}
.stepdesc{font-size:.94rem; color:var(--ink2); min-height:2.6em; margin:.4rem 0 1.1rem; max-width:72ch;}
.stepdesc b{color:var(--ink);}

/* ============ SVG ============ */
svg text{fill:var(--muted); font-family:inherit; font-size:11px;}
svg .axisline{stroke:var(--axis);}
svg .gridline{stroke:var(--grid);}
.flexcols{display:flex; flex-wrap:wrap; gap:1.4rem; align-items:flex-start;}
.flexcols>div{min-width:0;}

footer{max-width:1150px; margin:0 auto; padding:0 1.2rem 2rem; color:var(--muted); font-size:.8rem;}
kbd{border:1px solid var(--border); border-bottom-width:2px; border-radius:5px; padding:0 .35em; font-size:.85em; background:var(--surface);}

/* ============ Móvil ============ */
@media (max-width:700px){
  :root{--cell:38px; --cellgap:5px;}
  header{padding:.45rem .6rem; gap:.3rem;}
  header h1{font-size:.95rem;}
  header h1 small{display:none;}
  /* nav (contenedor de .unidad-nav) es un flex item de header, que en móvil
     sigue siendo columna. min-width:0 es imprescindible: sin él, nav no
     encoge más allá del min-content de la grilla de unidades —que con 5
     columnas es enorme— y termina empujando el ancho de toda la página,
     aunque .unidad-nav ya tenga su propio overflow-x resuelto más abajo. */
  nav{min-width:0;}
  main{padding:.6rem .7rem 3rem;}
  .module>h2{font-size:1.4rem; margin-top:1rem;}
  .lead{font-size:1rem; margin-bottom:1.5rem;}
  .card{padding:1.1rem 1.05rem; border-radius:14px; margin:1.2rem 0;}
  .card>h3{font-size:1.08rem; margin-bottom:.9rem;}
  .card details{padding:.75rem .85rem;}
  /* .fuente no lleva override acá: su padding-right es max(0px, 100%-86ch),
     que en un teléfono ya da 0. Una regla en este bloque además perdería, porque
     la declaración de .card .fuente aparece más abajo en la hoja. */
  .temas-lista{grid-template-columns:1fr;}
  .flexcols{gap:.9rem;}
  .code{font-size:.78rem;}
  .kbdhint{display:none;} /* atajos de teclado no aplican en táctil */
}
/* dedos, no cursores: targets más generosos */
@media (pointer:coarse){
  .btn{padding:.5rem .85rem;}
  .unidad-btn{padding:.4rem .7rem;}
  .tema-op{padding:.5rem .6rem;}
  .volver{padding:.3rem .7rem;}
  select,input[type=number],input[type=text]{padding:.45rem .55rem;}
  input[type=range]{min-height:2.2rem;}
}

/* ============ Nav de unidades con panel desplegable ============
   Una sola fila que ocupa TODO el ancho, con las unidades repartidas en
   columnas iguales (grid-auto-columns:1fr — con flex quedaban apelotonadas a
   la izquierda dejando el resto vacío). Los temas ya no viven en una segunda
   fila permanente: caen en un panel al pasar el cursor por su unidad. */
.unidad-nav{
  display:grid; grid-auto-flow:column; grid-auto-columns:1fr;
  gap:.3rem; width:100%; min-width:0;
}
.unidad{position:relative; min-width:0;}
/* Una sola línea, y todas del mismo alto: así la fila no cambia de altura al
   cambiar de unidad (no empuja el contenido) y el header se mantiene bajo. */
.unidad-btn{
  width:100%; min-height:1.85rem; cursor:pointer;
  display:flex; align-items:center; justify-content:flex-start;
  border:1px solid transparent; background:transparent; color:var(--muted);
  font:inherit; text-align:left; padding:.18rem .55rem; border-radius:8px;
  transition:background .15s, border-color .15s, color .15s;
}
.unidad-btn:hover{background:color-mix(in srgb, var(--ink) 6%, transparent);}
.unidad.on .unidad-btn{
  color:var(--ink);
  background:color-mix(in srgb, var(--sem-actual) 18%, var(--surface));
  border-color:color-mix(in srgb, var(--sem-actual) 40%, transparent);
}
/* el nombre se trunca en vez de envolver: una unidad con nombre largo no debe
   poder estirar el alto de toda la barra */
.u-nombre{
  max-width:100%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  font-size:.74rem; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
}

/* El panel arranca pegado al botón (top:100%, sin margin): un hueco de aire
   entre ambos cortaría el hover al bajar el cursor. La separación visual la dan
   el borde y la sombra. */
.unidad-menu{
  display:none; position:absolute; top:100%; left:0; z-index:60;
  grid-template-columns:repeat(var(--cols,2), minmax(0,1fr)); gap:.15rem;
  min-width:100%; width:max-content; max-width:min(40rem,92vw);
  padding:.4rem; background:var(--surface);
  border:1px solid var(--border); border-radius:12px;
  box-shadow:0 12px 30px rgba(0,0,0,.18);
}
/* .der lo pone core.js cuando el panel, anclado a la izquierda, se saldría por
   el borde derecho de la pantalla. No alcanza con :last-child: con 5 unidades
   el panel ancho de la penúltima (9 temas en 3 columnas) ya se pasa. */
.unidad-menu.der{left:auto; right:0;}
@media (hover:hover){ .unidad:hover .unidad-menu{display:grid;} }
/* :focus-within abre con teclado; .abierto es el modo táctil (lo alterna core.js) */
.unidad:focus-within .unidad-menu,
.unidad.abierto .unidad-menu{display:grid;}

.tema-op{
  display:flex; align-items:baseline; gap:.5rem; width:100%; text-align:left;
  border:1px solid transparent; background:transparent; color:var(--ink);
  font:inherit; font-size:.85rem; line-height:1.35; padding:.36rem .55rem;
  border-radius:8px; cursor:pointer; transition:background .12s, border-color .12s;
}
/* el número es el mismo atajo de teclado 1–9; tabular-nums para que la columna
   de números quede alineada aunque haya de una y dos cifras */
.tema-op .n{
  flex:0 0 1rem; text-align:right; font-size:.72rem; font-weight:700;
  color:var(--muted); font-variant-numeric:tabular-nums;
}
.tema-op .t{min-width:0;}
.tema-op:hover{background:color-mix(in srgb, var(--s1) 9%, transparent);}
.tema-op.on{
  background:color-mix(in srgb, var(--s1) 15%, var(--surface));
  border-color:color-mix(in srgb, var(--s1) 45%, transparent); font-weight:600;
}
.tema-op.on .n{color:var(--s1);}
.tema-op.pend{color:var(--pendiente); font-style:italic;}
.tema-op.pend::after{content:"○"; margin-left:auto; padding-left:.4rem; font-size:.7rem;}

@media (max-width:700px){
  /* En celular no entran 5 columnas iguales legibles: la fila pasa a ancho
     mínimo por unidad y scrollea horizontal (core.js trae la activa al centro
     con scrollIntoView). min-width:0 es imprescindible para que la grilla
     pueda encoger por debajo de su min-content y el overflow-x llegue a
     activarse. Esta regla va DESPUÉS de la declaración base de .unidad-nav
     (mismo nivel de especificidad: gana la última de la hoja). */
  .unidad-nav{grid-auto-columns:minmax(8.5rem,auto); overflow-x:auto; scrollbar-width:none;}
  .unidad-nav::-webkit-scrollbar{display:none;}
  /* El panel NO puede quedar dentro de .unidad-nav en móvil: overflow-x:auto
     computa overflow-y a auto, así que recorta verticalmente todo lo que
     cuelgue del contenedor — el panel se marcaba abierto pero no se veía.
     position:fixed lo saca de ese recorte (un fixed solo lo clipean ancestros
     con transform/filter, y acá no hay). De paso queda como hoja a todo el
     ancho bajo el header, que es lo que se quiere en un teléfono.
     --hdr-h lo mide core.js: el header es sticky y su alto cambia con el
     nombre del ramo. */
  .unidad-menu{
    position:fixed; top:calc(var(--hdr-h,6.5rem) + .3rem); left:.6rem; right:.6rem;
    width:auto; min-width:0; max-width:none;
    grid-template-columns:1fr; max-height:min(60vh,26rem); overflow-y:auto;
  }
  .unidad-menu.der{left:.6rem; right:.6rem;}
}

/* ============ Fórmulas y derivaciones ============ */
.formula{
  background:color-mix(in srgb, var(--s1) 7%, var(--surface));
  border-left:3px solid color-mix(in srgb, var(--s1) 60%, transparent);
  border-radius:0 12px 12px 0; padding:1.15rem 1.35rem; margin:1.5rem 0;
  overflow-x:auto; overflow-y:hidden;
}
.formula .katex-display{margin:0;}
.pasos{display:flex; flex-direction:column; gap:.3rem; margin:1.3rem 0;}
.pasos-linea{
  display:flex; flex-wrap:wrap; gap:.5rem 1.4rem; align-items:baseline;
  padding:.62rem .9rem; border-radius:8px;
  opacity:.18; transition:opacity .35s, background .35s;
}
.pasos-linea.vista{opacity:1;}
.pasos-linea.actual{background:color-mix(in srgb, var(--s4) 16%, transparent);}
/* max-width:100% además de min-width:0: cuando .pasos-tex queda solo en su
   propia línea (envuelto por flex-wrap) y su fórmula KaTeX no tiene ningún
   punto de quiebre (una sola línea sin \qquad ni fracciones separables), el
   navegador la dimensiona por el ancho de su contenido en vez de encogerla
   al espacio disponible — el scroll interno (overflow-x:auto) nunca llega a
   activarse porque la caja ya creció para no necesitarlo, y esa caja de más
   se sale de .pasos-linea (que sí tiene overflow visible) hacia el resto de
   la página. max-width:100% fuerza la caja al ancho del contenedor, y ahí
   overflow-x:auto sí entra a tallar. Visto en fórmulas largas de una sola
   línea (p.ej. "dP=166,2(-1)+...=-199,44") en viewport de teléfono. */
.pasos-tex{flex:1 1 20rem; min-width:0; max-width:100%; overflow-x:auto; overflow-y:hidden;}
.pasos-nota{flex:1 1 16rem; font-size:.88rem; line-height:1.6; color:var(--ink2); min-width:0; max-width:100%;}
.pasos-nota b{color:var(--ink);}

/* ============ Lienzos de las primitivas ============ */
.lienzo{
  display:block; width:100%; border:1px solid var(--border); border-radius:10px;
  background:color-mix(in srgb, var(--ink) 2%, var(--surface)); margin:1.2rem 0;
}
.arbol{margin:1.2rem 0; overflow-x:auto;}
.arbol-nodo{
  display:inline-flex; align-items:center; justify-content:center; text-align:center;
  border:1px solid var(--border); border-radius:9px; padding:.35rem .7rem;
  background:var(--surface); font-size:.88rem; transition:background .3s, box-shadow .3s;
}
.arbol-nodo.hl{background:color-mix(in srgb, var(--s4) 26%, var(--surface)); box-shadow:0 0 0 2px var(--s4);}
.tabla-va{overflow-x:auto; margin:1.2rem 0;}
.tabla-va table{border-collapse:collapse; font-variant-numeric:tabular-nums;}
.tabla-va th,.tabla-va td{
  border:1px solid var(--border); padding:.35rem .7rem; font-size:.88rem;
  text-align:center; transition:background .3s, box-shadow .3s;
}
.tabla-va th{background:color-mix(in srgb, var(--s1) 14%, var(--surface)); font-weight:700;}
.tabla-va td.hl{background:color-mix(in srgb, var(--s4) 28%, var(--surface)); box-shadow:inset 0 0 0 2px var(--s4);}
.tabla-va td.ok{background:color-mix(in srgb, var(--s2) 20%, var(--surface));}
.barras{margin:1.2rem 0;}

/* ============ Diagramas entidad-relación (mer.js) ============ */
/* El SVG tiene viewBox de 1000 unidades de ancho y escala solo, así que no
   necesita repintado ni al cambiar de tema ni al redimensionar: los colores
   son var() y el navegador reescala el viewBox. Lo único que sí necesita es
   un ancho mínimo — a 400px de pantalla, un texto de 15 unidades del viewBox
   quedaría en 6px, ilegible. Por eso min-width + scroll horizontal propio,
   igual que .tabla-va: el diagrama se desliza, la página no. */
.mer{margin:1.4rem 0; overflow-x:auto;}
.mer-svg{display:block; width:100%; min-width:660px; height:auto;}

.mer-entidad{fill:var(--surface); stroke:var(--s1); stroke-width:2;}
.mer-rel{fill:color-mix(in srgb, var(--s4) 14%, var(--surface)); stroke:var(--s4); stroke-width:2;}
.mer-attr{fill:color-mix(in srgb, var(--s5) 12%, var(--surface)); stroke:var(--s5); stroke-width:1.6;}
.mer-attr.derivado{stroke-dasharray:5 4;}
.mer-linea{stroke:var(--axis); stroke-width:1.6;}
.mer-linea.punteada{stroke-dasharray:5 4;}
.mer-cardfondo{fill:var(--surface); stroke:var(--border); stroke-width:1;}

.mer-txt{fill:var(--ink); font-family:inherit; font-size:15px;}
.mer-txt.fuerte{font-weight:700;}
.mer-txt.pk{text-decoration:underline; text-underline-offset:2px;}
.mer-txt.comp{font-style:italic;}
.mer-txt.card{font-weight:800; font-size:14px;}

/* Un nodo resaltado por el Stepper: mismo ámbar que .arbol-nodo.hl y
   .tabla-va td.hl, para que "esto es lo que estamos mirando ahora" se lea
   igual en las tres primitivas. */
.mer-nodo :is(.mer-entidad,.mer-rel,.mer-attr){transition:fill .3s, stroke .3s;}
.mer-nodo.hl :is(.mer-entidad,.mer-rel,.mer-attr){
  fill:color-mix(in srgb, var(--s4) 30%, var(--surface)); stroke:var(--s4); stroke-width:3;
}
/* Revelado paso a paso: visibility además de opacity para que un nodo todavía
   no revelado tampoco lo lea un lector de pantalla. */
.mer-nodo,.mer-linea{transition:opacity .35s;}
.oculto{opacity:0; visibility:hidden;}

/* ============ Diccionario de datos (mer.js) ============ */
/* .tabla-va centra todo (está pensada para tablas de probabilidad, que son
   numéricas). Acá las tres primeras columnas son texto y se leen mucho mejor
   alineadas a la izquierda; solo PK/FK/Nulo quedan centradas. */
.dicc td{text-align:left;}
.dicc td.centro{text-align:center; font-weight:700;}
.dicc th{white-space:nowrap;}

/* ============ Fichas de temas pendientes ============ */
.ficha-pendiente{
  border:1px dashed var(--axis); border-radius:16px; padding:1.5rem 1.7rem; margin:1.5rem 0;
  background:color-mix(in srgb, var(--ink) 2.5%, var(--surface));
}
.ficha-pendiente h3{margin:0 0 .9rem; font-size:1.05rem; color:var(--ink2);}
.ficha-pendiente ul{margin:.5rem 0 1.3rem; padding-left:1.3rem; color:var(--ink2); font-size:.92rem;}
.ficha-pendiente li{margin-bottom:.45rem;}
.ficha-pendiente .puente{
  display:inline-block; margin-top:.3rem; font-size:.88rem; font-weight:600;
  color:var(--s1); text-decoration:none; border-bottom:1px solid currentColor;
}
/* Tercer registro: la atribución. El filete la despega del contenido y la
   marca como pie de tarjeta; sin cursiva, porque son párrafos largos y la
   cursiva los vuelve más difíciles de leer, no menos importantes. */
.fuente{
  margin:1.9rem 0 0; padding-top:.75rem; border-top:1px solid var(--border);
  font-size:.78rem; line-height:1.55; color:var(--muted);
}
/* El filete tiene que cruzar la tarjeta entera (es el pie de la tarjeta), pero
   el texto no debe llegar a 150 caracteres por línea. max-width cortaría también
   el borde; el padding-right no: border-top abarca la caja de borde, así que la
   línea sigue completa y solo el texto se angosta. El max() lo desactiva en
   pantallas donde la tarjeta ya mide menos de 86ch.
   OJO con la especificidad: tiene que ser `.card .fuente` (0,2,0) y no `.fuente`
   (0,1,0), porque `.card p` (0,1,1) le gana a esta última. Con `.fuente` a
   secas, max-width:66ch seguía vigente y, como todo el sitio es border-box, el
   padding-right se comía la caja entera: el texto salía a una palabra por
   línea. */
.card .fuente{
  max-width:none; padding-right:max(0px, calc(100% - 86ch));
}
.fuente.suelta{border-top:none; padding-top:0; margin-top:1.2rem;}

/* ============ Mapa del curso (módulo Inicio) ============ */
/* Índice por unidad: solo los nombres de los temas, en columnas. Es una
   pantalla para orientarse de un vistazo, así que no lleva descripciones —
   el texto largo vive dentro de cada tema. */
/* Cada tema es una casilla con borde propio, no texto suelto dentro de la
   tarjeta grande de la unidad: con el borde permanente se lee de un vistazo
   cuántos temas hay y dónde termina cada uno. El hover sigue subiendo el
   borde y el fondo a color de acento. */
.temas-lista{display:grid; grid-template-columns:repeat(auto-fill,minmax(16.5rem,1fr)); gap:.5rem;}
.tema-item{
  display:block; padding:.6rem .8rem; border-radius:10px; text-decoration:none;
  color:var(--ink); border:1px solid var(--border); font-size:.96rem; font-weight:600;
  background:color-mix(in srgb, var(--ink) 2%, transparent);
  transition:background .15s, border-color .15s;
}
.tema-item:hover{
  background:color-mix(in srgb, var(--s1) 8%, transparent);
  border-color:color-mix(in srgb, var(--s1) 35%, transparent);
}
.tema-item.pend{color:var(--pendiente); font-weight:500; font-style:italic;}
.tema-item.pend::before{content:"○ ";}

@media (max-width:700px){
  /* .pasos-linea pasa a columna en el teléfono (una caja de fórmula arriba,
     la nota abajo — no entran una al lado de la otra). Eso cambia el eje
     principal del flex de horizontal a vertical, y flex:1 1 20rem/16rem
     (pensado como ancho mínimo en la disposición horizontal) pasa a leerse
     como ALTO mínimo: cada caja queda fija en 320px/256px de alto sin
     importar que el contenido real mida ~24px. flex:0 1 auto vuelve a
     dimensionar por el contenido (alto natural), en las dos disposiciones. */
  .pasos-linea{flex-direction:column; gap:.2rem;}
  .pasos-tex,.pasos-nota{flex:0 1 auto;}
  /* Indicio visual de scroll horizontal en .pasos-tex: una fórmula KaTeX
     más ancha que la pantalla del teléfono va a existir siempre, por más
     que Pasos() la parta en pasos cortos, y .pasos-tex ya tiene scroll
     interno (overflow-x:auto, ver arriba) — pero sin ninguna señal el
     usuario no sabe que puede deslizar y la lee como "cortada". Técnica de
     sombra de scroll con doble capa de degradé por borde: una capa con
     background-attachment:local (viaja con el CONTENIDO, ancla en sus dos
     extremos verdaderos) tapa la sombra; otra con :scroll (fija al
     VIEWPORT de la caja, ancla en sus dos bordes visibles) la dibuja. Donde
     el contenido no excede la caja ambos extremos coinciden y la tapa cubre
     toda sombra; donde sí excede, la tapa del lado ya recorrido se corre
     fuera de vista y deja ver la sombra de ESE lado nomás. Sin JS: la caja
     de arriba (--pasos-cover) sigue el fondo real detrás de .pasos-tex,
     que cambia con .actual (franja ámbar) — ver override debajo. */
  .pasos-tex,.formula{
    --pasos-cover:var(--surface);
    background-repeat:no-repeat;
    background-size:22px 100%, 22px 100%, 10px 100%, 10px 100%;
    background-position:left, right, left, right;
    background-attachment:local, local, scroll, scroll;
    background-image:
      linear-gradient(to right, var(--pasos-cover) 45%, transparent),
      linear-gradient(to left, var(--pasos-cover) 45%, transparent),
      linear-gradient(to right, color-mix(in srgb, var(--ink) 25%, transparent), transparent),
      linear-gradient(to left, color-mix(in srgb, var(--ink) 25%, transparent), transparent);
  }
  .pasos-linea.actual .pasos-tex{--pasos-cover:color-mix(in srgb, var(--s4) 16%, var(--surface));}
  /* .formula tiene su propio fondo (celeste, no var(--surface) liso) — la
     tapa que oculta la sombra en los bordes sin scroll debe seguir ESE
     color, si no repite el mismo desajuste visto en .pasos-tex. */
  .formula{--pasos-cover:color-mix(in srgb, var(--s1) 7%, var(--surface));}
}

/* ============ Unidades de un solo tema: botón directo, sin panel ============
   Inicio y Laboratorio miden lo que su rótulo; las unidades se reparten el
   resto. Con cinco columnas iguales los nombres de las unidades se cortaban. */
.unidad.solo .unidad-menu{display:none!important;}
.unidad-nav{display:flex;}
.unidad{flex:1 1 0;}
.unidad.solo{flex:0 0 auto;}
@media (max-width:700px){
  .unidad{flex:0 0 auto; min-width:8.5rem;}
  .unidad.solo{min-width:0;}
}

/* ============ Laboratorio ============ */
.lab-ec{display:flex; gap:.5rem; flex-wrap:wrap; align-items:center; margin:1rem 0 .4rem;}
.lab-ec input[type=text]{flex:1 1 16rem; min-width:0; font-size:.95rem; padding:.4rem .6rem;}
.lab-ej{font-size:.85rem; color:var(--muted); margin:0 0 .2rem;}
.lab-ej button{border:none; background:none; color:var(--s1); padding:0; font:inherit;
  border-bottom:1px dotted currentColor; cursor:pointer;}
.lab-grid{display:grid; grid-template-columns:minmax(0,1fr) 22rem; gap:1.2rem; align-items:start; margin:1.6rem 0;}
.lab-grid>.card{margin:0;}
@media (max-width:900px){ .lab-grid{grid-template-columns:1fr;} }
.lab-lista{display:flex; flex-direction:column; gap:.25rem; margin:0 0 1.2rem;}
.lab-obj{display:flex; align-items:center; gap:.5rem; width:100%; padding:.35rem .5rem;
  border-radius:9px; border:1px solid transparent; color:var(--ink); font-size:.9rem; cursor:pointer;}
.lab-obj:hover{background:color-mix(in srgb, var(--s1) 8%, transparent);}
.lab-obj.on{background:color-mix(in srgb, var(--s1) 14%, var(--surface)); border-color:color-mix(in srgb, var(--s1) 45%, transparent);}
.lab-obj .sw{width:.8rem; height:.8rem; border-radius:3px; flex:0 0 auto;}
.lab-obj .nm{font-weight:700; min-width:1.7rem;}
.lab-obj .tp{flex:1; color:var(--ink2);}
.lab-obj.oculto .nm,.lab-obj.oculto .tp{opacity:.45; text-decoration:line-through;}
.lab-obj .btn{font-size:.72rem; padding:.05rem .45rem;}
.lab-det h4{margin:0 0 .3rem; font-size:.95rem; display:flex; align-items:center; gap:.4rem;}
.lab-det h4 .sw{width:.8rem; height:.8rem; border-radius:3px;}
.lab-det .etq{font-size:.72rem; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin:.7rem 0 0;}
.lab-det .formula{margin:.3rem 0 0; padding:.7rem .9rem;}
.lab-det .formula .katex{font-size:.88em;}
.lab-det .lectura{grid-template-columns:1fr 1fr; max-width:none; margin:.9rem 0;}
.lab-det .controls{margin:.3rem 0; flex-wrap:nowrap;}
.lab-det .controls label{min-width:1.3rem;}
.lab-det .controls input[type=range]{flex:1 1 auto; min-width:0;}
.lab-rel{margin-top:1.2rem;}
.lab-rel h4{margin:0 0 .3rem; font-size:.95rem;}
.lab-fila{display:grid; grid-template-columns:4.5rem minmax(0,1fr); gap:.1rem .8rem;
  padding:.45rem 0; border-bottom:1px solid var(--border); font-size:.88rem;}
.lab-fila:last-child{border-bottom:none;}
.lab-fila>*{white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0;}
.lab-fila .par{font-weight:700; grid-row:span 2;}
.lab-fila .par i{font-style:normal;}
.lab-fila .pts{grid-column:2; font-family:ui-monospace,Consolas,monospace; font-size:.78rem; color:var(--ink2);}
.lab-estado{display:inline-block; font-size:.72rem; font-weight:700; padding:0 .5rem; border-radius:999px;
  border:1px solid var(--border); margin-right:.4rem;}
.lab-estado.corta{color:var(--s2); border-color:color-mix(in srgb, var(--s2) 45%, transparent); background:color-mix(in srgb, var(--s2) 10%, transparent);}
.lab-estado.toca{color:var(--s4); border-color:color-mix(in srgb, var(--s4) 50%, transparent); background:color-mix(in srgb, var(--s4) 12%, transparent);}
.lab-estado.nada{color:var(--muted);}
