/* AprendiendoLSU — tema compartido (fuente: 04_diseno/marca/brand.html v02).
   Profundidad SOLO con borde inferior sólido; sin box-shadow difusas ni gradientes. */

:root{
  --c-primary:#1cb0f6; --c-primary-deep:#1899d6; --c-primary-soft:#ddf4ff;
  --c-cobalt:#1e40af;  --c-cobalt-deep:#00288e;  --c-cobalt-text:#1e40af;
  --c-gold:#ffc800;    --c-gold-deep:#e5a600;
  --c-streak:#ff9600;  --c-streak-deep:#e07f00;
  --c-success:#22c55e; --c-success-deep:#16a34a; --c-success-soft:#dcfce7;
  --c-danger:#ef4444;  --c-danger-deep:#dc2626;  --c-danger-soft:#fee2e2;
  /* Texto sobre los fondos suaves. No se reusa *-deep para esto: sobre el fondo
     suave da 3,0:1 (verde) y 3,95:1 (rojo), por debajo del 4,5:1 que pide AA
     para texto de 18 px en negrita. Estos dan 6,5:1 y 5,3:1. Aparte: *-deep es
     el borde inferior de los botones 3D, así que cambiarlo movería otra cosa. */
  --c-success-text:#166534;
  --c-danger-text:#b91c1c;
  --c-ink:#243342;     --c-ink-muted:#5c718a;
  --c-line:#e5e9ef;    --c-line-deep:#cdd5df;
  --c-paper:#ffffff;   --c-page:#f7fbff;
}
.dark{
  --c-primary-soft:#0b3550;
  --c-cobalt-text:#b8c4ff;
  --c-success-soft:#0c2e1c; --c-danger-soft:#3d1414;
  --c-success-text:#86efac; --c-danger-text:#fca5a5;
  --c-ink:#e8f1f8;   --c-ink-muted:#93a8b8;
  --c-line:#37464f;  --c-line-deep:#22303a;
  --c-paper:#16232e; --c-page:#0f1a24;
}

html{ scroll-behavior:smooth; scroll-padding-top:5rem; }
body{
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
  background:var(--c-page); color:var(--c-ink);
}

/* Aviso inicial del landing. En celular forma parte del documento: la versión
   flotante tapaba los dos llamados a la acción en la primera visita. */
.landing-privacy{
  position:fixed; bottom:1rem; left:50%; transform:translateX(-50%); z-index:80;
  width:calc(100% - 2rem); max-width:42rem;
}
@media (max-width:639px){
  .landing-privacy{
    position:relative; bottom:auto; left:auto; transform:none; z-index:1;
    width:auto; max-width:none; margin:12px 16px 0;
  }
}
/* En 320 px no entran a la vez la marca completa, el tema y el CTA. La mano y
   LSU conservan la identidad; el nombre completo vuelve desde 360 px. */
@media (max-width:359px){ .landing-page .brand-long{ display:none; } }
body.landing-page{ overflow-x:clip; }
@supports not (overflow-x:clip){ body.landing-page{ overflow-x:hidden; } }

.material-symbols-outlined{ font-variation-settings:'FILL' 0,'wght' 400,'GRAD' 0,'opsz' 24; }
.material-symbols-outlined.fill{ font-variation-settings:'FILL' 1; }

/* ---------- botones 3D ---------- */
.btn-3d{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  font-family:Nunito,system-ui,sans-serif; font-weight:800; font-size:17px; letter-spacing:.02em;
  padding:13px 26px; border-radius:16px; cursor:pointer; text-decoration:none;
  border:2px solid transparent; border-bottom-width:4px;
  transition:filter .12s; -webkit-user-select:none; user-select:none;
}
.btn-3d:hover{ filter:brightness(1.06); }
.btn-3d:active{ transform:translateY(3px); border-bottom-width:2px; }
.btn-3d:focus-visible{ outline:3px solid var(--c-cobalt-text); outline-offset:2px; }
.btn-3d.primary{ background:var(--c-primary); border-bottom-color:var(--c-primary-deep); color:#fff; }
.btn-3d.cobalt { background:var(--c-cobalt);  border-bottom-color:var(--c-cobalt-deep);  color:#fff; }
.btn-3d.danger { background:var(--c-danger);  border-bottom-color:var(--c-danger-deep);  color:#fff; }
.btn-3d.success{ background:var(--c-success); border-bottom-color:var(--c-success-deep); color:#fff; }
.btn-3d.ghost{
  background:var(--c-paper); color:var(--c-cobalt-text);
  border:2px solid var(--c-line); border-bottom:4px solid var(--c-line-deep);
}
.btn-3d[disabled], .btn-3d.disabled{
  background:var(--c-line); color:var(--c-ink-muted);
  border:2px solid transparent; cursor:not-allowed; transform:none; filter:none;
}
.btn-3d.sm{ font-size:15px; padding:8px 16px; border-radius:12px; }

/* ---------- cards ---------- */
.card-duo{ background:var(--c-paper); border:2px solid var(--c-line); border-radius:16px; }
.card-duo.pressable{ border-bottom-width:4px; cursor:pointer; }
.card-duo.pressable:active{ transform:translateY(2px); border-bottom-width:2px; }

/* ---------- chips ---------- */
.chip{
  display:inline-flex; align-items:center; gap:7px;
  font-family:Nunito,system-ui,sans-serif; font-weight:800; font-size:15px;
  border-radius:9999px; padding:7px 16px; cursor:pointer;
  background:var(--c-paper); border:2px solid var(--c-line); color:var(--c-ink-muted);
}
.chip.selected{ background:var(--c-primary-soft); border-color:var(--c-primary); color:var(--c-cobalt-text); }

/* ---------- progreso ---------- */
.progress{ height:16px; border-radius:9999px; background:var(--c-line); overflow:hidden; }
.progress>span{ display:block; height:100%; border-radius:9999px; background:var(--c-primary); transition:width .4s ease-out; }

/* ---------- feedback ---------- */
.fb-banner{
  display:flex; align-items:center; gap:12px;
  border-radius:16px; padding:14px 18px; font-weight:700; font-size:18px;
  /* El estado neutro va acá y no como clases de Tailwind en el HTML: la hoja de
     Tailwind se inyecta después de esta, así que `bg-primary-soft` le ganaba a
     .fb-ok/.fb-bad y el banner nunca cambiaba de fondo. En esta pantalla todo el
     feedback es visual —no hay sonido del que depender—, así que el color es
     información, no decoración. */
  background:var(--c-primary-soft); color:var(--c-cobalt-text);
}
.fb-banner .ico{
  width:34px; height:34px; border-radius:9999px; display:grid; place-items:center;
  background:var(--c-paper); font-family:Nunito,system-ui,sans-serif; font-weight:900; flex:none;
}
.fb-ok { background:var(--c-success-soft); color:var(--c-success-text); }
.fb-ok .ico{ color:var(--c-success-text); }
.fb-bad{ background:var(--c-danger-soft);  color:var(--c-danger-text); }
.fb-bad .ico{ color:var(--c-danger-text); }
.dark .fb-ok .ico{ background:var(--c-success); color:#fff; }
.dark .fb-bad .ico{ background:var(--c-danger); color:#fff; }

/* ---------- nodos del mapa ---------- */
.node{
  /* Ficha redondeada, no círculo: es el detalle que más despega el camino del
     original que lo inspiró, sin tocar la estructura en zigzag. El radio va en
     porcentaje para que .node y .node-lg queden con la misma curva. */
  width:74px; height:74px; border-radius:28%;
  display:grid; place-items:center;
  font-family:Nunito,system-ui,sans-serif; font-weight:900; font-size:24px; color:#fff;
  border:none; border-bottom:6px solid rgba(0,0,0,.22); background-clip:padding-box; cursor:pointer;
}
.node:active{ transform:translateY(3px); border-bottom-width:3px; }
.node.done  { background:var(--c-primary); }
.node.active{ background:var(--c-cobalt); outline:4px solid var(--c-primary-soft); outline-offset:3px; }
.node.locked{ background:var(--c-line); color:var(--c-ink-muted); border-bottom-color:var(--c-line-deep); cursor:not-allowed; }

/* Línea del camino. Antes era una sola punteada gris de punta a punta, igual
   para todo el recorrido. Ahora va por tramos y cada uno se pinta según lo que
   ya recorriste, así el camino muestra dónde estás sin una sola palabra. */
#path-svg .camino-fondo{
  fill:none; stroke:var(--c-line); stroke-width:6; stroke-linecap:round;
}
#path-svg .camino-hecho{
  fill:none; stroke:var(--c-primary); stroke-width:6; stroke-linecap:round;
  /* El path lleva pathLength="1", así que el largo real de la curva no importa:
     alcanza con stroke-dashoffset = 1 − avance, sin medir nada. */
  stroke-dasharray:1;
  transition:stroke-dashoffset .5s ease-out;
}
@media (prefers-reduced-motion: reduce){
  #path-svg .camino-hecho{ transition:none; }
}

/* ---------- indicador de nivel con la mascota ----------
   El nivel se muestra con la etapa de la mascota (bebé / joven / adulto) en vez
   de un escudo: la mascota ya evoluciona con el nivel, así que el dibujo dice lo
   mismo que el número y es propio del proyecto. Va del tamaño de un icono para
   no competir con la mascota grande de la tarjeta. */
.stat-pet{ display:inline-flex; width:30px; height:30px; }
.stat-pet img, .stat-pet svg{ width:100%; height:100%; display:block; object-fit:contain; }

/* ---------- navegación inferior (celular) ----------
   En celular no hay barra lateral: sin esto, la única forma de pasar de una
   pantalla a otra era el botón "atrás" del navegador. La barra la inyecta
   LSU_UI.navInferior() para que las cuatro entradas sean idénticas en todas las
   páginas en vez de estar copiadas en cada una. Desde lg no aparece: ahí manda
   la barra lateral. */
.lsu-nav-inferior{ display:none; }
.lsu-nav-inferior a{
  display:flex; flex-direction:column; align-items:center; gap:2px;
  font-family:Nunito,system-ui,sans-serif; font-weight:800; font-size:11px;
  color:var(--c-ink-muted); text-decoration:none;
  padding:5px 12px; border-radius:12px;
  /* 64×48 mínimo: área táctil cómoda, no el tamaño del texto */
  min-width:64px; min-height:48px; justify-content:center;
}
.lsu-nav-inferior a.active{ color:var(--c-cobalt-text); background:var(--c-primary-soft); }

@media (max-width: 1023px){
  .lsu-nav-inferior{
    position:fixed; bottom:0; left:0; right:0; z-index:60;
    display:flex; justify-content:space-around; align-items:center;
    background:var(--c-paper); border-top:2px solid var(--c-line);
    padding:4px 0 max(4px, env(safe-area-inset-bottom));
  }
  /* Lo que estaba pegado abajo tiene que subir, o la barra lo tapa: el final de
     la página, el aviso de privacidad y los toasts. La altura va en una variable
     para que los tres usen exactamente la misma. */
  body.con-nav-inferior{
    --nav-inferior: calc(64px + env(safe-area-inset-bottom));
    padding-bottom:var(--nav-inferior);
  }
  body.con-nav-inferior #privacy-banner{ bottom:calc(var(--nav-inferior) + 12px); }
  body.con-nav-inferior #lsu-toasts{ bottom:calc(var(--nav-inferior) + 12px); }
}

/* ---------- toasts (usados por ui.js) ---------- */
#lsu-toasts{
  position:fixed; bottom:18px; left:50%; transform:translateX(-50%);
  z-index:120; display:flex; flex-direction:column; gap:10px; align-items:center;
  width:calc(100% - 2rem); max-width:480px; pointer-events:none;
}
.toast{
  display:inline-flex; align-items:center; gap:10px; pointer-events:auto;
  background:var(--c-paper); border:2px solid var(--c-line); border-bottom-width:4px;
  border-radius:16px; padding:12px 18px; font-weight:700; color:var(--c-ink);
  animation:toast-in .3s ease-out;
}
.toast .dot{
  width:26px; height:26px; border-radius:9999px; display:grid; place-items:center;
  color:#fff; font-family:Nunito,system-ui,sans-serif; font-weight:900; font-size:15px; flex:none;
}
.toast.ok .dot{ background:var(--c-success); }
.toast.bad .dot{ background:var(--c-danger); }
.toast.info .dot{ background:var(--c-primary); }
@keyframes toast-in{ from{ opacity:0; transform:translateY(12px); } to{ opacity:1; transform:none; } }

/* ---------- modal (ui.js) ---------- */
.lsu-modal-backdrop{
  position:fixed; inset:0; z-index:110; background:rgba(6,20,32,.55);
  display:grid; place-items:center; padding:1rem; overflow-y:auto;
}
.lsu-modal{
  background:var(--c-paper); border:2px solid var(--c-line); border-bottom-width:4px;
  border-radius:24px; padding:28px; max-width:420px; width:100%; color:var(--c-ink);
  max-height:calc(100dvh - 2rem); overflow-y:auto; position:relative;
}
.lsu-modal h3{ font-family:Nunito,system-ui,sans-serif; font-weight:900; font-size:22px; margin:0 0 8px; }
.lsu-modal p{ margin:0 0 20px; color:var(--c-ink-muted); }
.lsu-modal .row{ display:flex; gap:12px; justify-content:flex-end; flex-wrap:wrap; }
body.modal-abierto{ overflow:hidden; }
.perfil-modal-close{
  position:absolute; top:10px; right:10px; z-index:2;
  width:44px; height:44px; border-radius:9999px;
  display:grid; place-items:center; color:var(--c-ink-muted);
  background:var(--c-paper); border:2px solid transparent;
}
.perfil-modal-close:hover,
.perfil-modal-close:focus-visible{
  color:var(--c-cobalt-text); background:var(--c-primary-soft);
  border-color:var(--c-primary); outline:none;
}

/* ---------- revelado al scroll (M8) ----------
   Cada elemento entra desde la dirección que corresponde a su lugar en el
   layout: la ilustración de la derecha entra desde la derecha, el texto de la
   izquierda desde la izquierda. La curva es la misma para todos (0.16,1,0.3,1:
   arranca rápido y frena), de modo que la página se siente como un solo gesto. */
.reveal{ opacity:0; transform:translateY(24px); }
.reveal[data-reveal="left"]  { transform:translateX(-38px); }
.reveal[data-reveal="right"] { transform:translateX(38px); }
.reveal[data-reveal="scale"] { transform:scale(.88); }
.reveal[data-reveal="up"]    { transform:translateY(40px); }
.reveal.in{
  opacity:1; transform:none;
  transition:opacity .6s ease-out, transform .7s cubic-bezier(.16,1,.3,1);
}
.reveal.in[data-reveal-delay="1"]{ transition-delay:.10s; }
.reveal.in[data-reveal-delay="2"]{ transition-delay:.20s; }
.reveal.in[data-reveal-delay="3"]{ transition-delay:.30s; }
.reveal.in[data-reveal-delay="4"]{ transition-delay:.40s; }
/* En una columna angosta las dos mitades ya no están una al lado de la otra, así
   que entrar desde el costado no se lee como tal: solo empuja el bloque fuera de
   la caja y aparece scroll horizontal en toda la página. Debajo del breakpoint
   donde el landing deja de estar en dos columnas, la entrada lateral pasa a ser vertical. */
@media (max-width:1023px){
  .reveal[data-reveal="left"],
  .reveal[data-reveal="right"]{ transform:translateY(24px); }
}

/* Parallax: el elemento se desplaza a distinta velocidad que la página.
   El desplazamiento lo escribe theme-boot.js en --py (píxeles). */
.parallax{ transform:translate3d(0, var(--py, 0px), 0); will-change:transform; }

/* Ilustración de sección: acompaña al texto sin competirle. */
.section-art{ width:100%; max-width:340px; height:auto; display:block; margin-inline:auto; }
@media (min-width:768px){ .section-art{ max-width:400px; } }

/* ---------- ilustraciones y mascotas en dark ----------
   El arte tiene contornos azul marino (RGB 21,27,73): 16:1 de contraste sobre
   blanco pero 1.02:1 sobre el papel oscuro. En dark se separa con un halo
   MUY fino: con más grosor el brillo invade los detalles chicos (ojos, nariz)
   y los borra. El borde limpio lo da el propio PNG (alfa des-compuesto). */
.dark .lsu-pet,
.dark .section-art{
  filter: drop-shadow(0 1px 0 rgba(255,255,255,.5))
          drop-shadow(0 -1px 0 rgba(255,255,255,.5))
          drop-shadow(1px 0 0 rgba(255,255,255,.5))
          drop-shadow(-1px 0 0 rgba(255,255,255,.5));
}

/* ---------- marca ----------
   El logo es la mano sola, sin el marco blanco que traía antes, así que ya no
   aporta su propio fondo. Su contorno es cobalto (#1e40af): sobre papel claro
   se lee solo, pero sobre el pie cobalto queda a 1:1 (mismo color) y en dark a
   1.7:1. Se le da el mismo halo fino que a las mascotas, y solo donde hace
   falta, para no ensuciarlo en claro. */
.logo-mark{ display:block; }
.bg-cobalt .logo-mark,
.dark .logo-mark{
  filter: drop-shadow(0 1px 0 rgba(255,255,255,.55))
          drop-shadow(0 -1px 0 rgba(255,255,255,.55))
          drop-shadow(1px 0 0 rgba(255,255,255,.55))
          drop-shadow(-1px 0 0 rgba(255,255,255,.55));
}

/* ---------- video de marca ----------
   Se muestra solo si el navegador lo reproduce; con movimiento reducido
   queda el poster (misma composición, sin animación). */
.lsu-video{ display:block; width:100%; height:auto; border-radius:16px; }
@media (prefers-reduced-motion: reduce){
  .lsu-video{ display:none; }
  .lsu-video-fallback{ display:block !important; }
}
.lsu-video-fallback{ display:none; width:100%; height:auto; border-radius:16px; }

/* ---------- vida idle (M9) ---------- */
@keyframes lsu-float{ 0%,100%{ transform:translateY(0); } 50%{ transform:translateY(-8px); } }
.anim-float{ animation:lsu-float 5s ease-in-out infinite; }
.anim-float-slow{ animation:lsu-float 7s ease-in-out infinite; }

/* Respiración: escala mínima, imperceptible como movimiento pero suficiente
   para que la ilustración no se lea como una estampa muerta. */
@keyframes lsu-breathe{
  0%,100%{ transform:scale(1) translateY(0); }
  50%    { transform:scale(1.018) translateY(-4px); }
}
/* Se aplica al contenedor para no pisar el translate del parallax (que va en
   la imagen); así conviven las dos transformaciones sin anularse. */
.art-alive{ animation:lsu-breathe 6s ease-in-out infinite; transform-origin:50% 85%; }
.art-alive.d1{ animation-duration:6.8s; animation-delay:-1.2s; }
.art-alive.d2{ animation-duration:7.4s; animation-delay:-2.4s; }

/* ---------- sprite de 3 cuadros, gobernado por el scroll ----------
   Antes los cuadros se alternaban solos con un temporizador de 1,6s. Ahora el
   cuadro visible lo elige la posición de scroll: initSprites() escribe data-f
   con 0, 1 o 2 y acá solo se cambia la opacidad. Sigue sin haber decodificación
   de video ni layout, únicamente compositing, así que el costo es el mismo.
   El recorrido es de ida y vuelta (a → b → c → b → a) a lo largo del tramo que
   la escena pasa por pantalla, de modo que la mascota "hace la seña" mientras
   se baja y la deshace mientras se sube. */
.sprite{ position:relative; aspect-ratio:1; }
.sprite img{
  position:absolute; inset:0; width:100%; height:100%; opacity:0;
  /* fundido corto entre cuadros: suficiente para que el cambio no se lea como
     un salto, corto como para que siga leyéndose como movimiento y no como
     transparencia de dos dibujos superpuestos */
  transition:opacity .12s linear;
}
.sprite img:first-child{ opacity:1; }   /* sin JS, o con movimiento reducido, queda el cuadro 1 */
.sprite[data-f] img{ opacity:0; }
.sprite[data-f="0"] img:nth-child(1),
.sprite[data-f="1"] img:nth-child(2),
.sprite[data-f="2"] img:nth-child(3){ opacity:1; }

/* Aterrizaje: al entrar en pantalla la ilustración cae con un rebote corto.
   Solo se dispara una vez, cuando el reveal la marca como visible. */
@keyframes lsu-land{
  0%  { transform:translateY(-18px) rotate(-2deg); }
  55% { transform:translateY(4px)   rotate(1deg); }
  100%{ transform:translateY(0)     rotate(0); }
}
.reveal.in .art-land{ animation:lsu-land .75s cubic-bezier(.34,1.4,.5,1) .15s both; }

@media (prefers-reduced-motion: reduce){
  .reveal, .reveal[data-reveal]{ opacity:1; transform:none; }
  .reveal.in{ transition:none; }
  .parallax{ transform:none; }
  .anim-float, .anim-float-slow, .art-alive{ animation:none; }
  .reveal.in .art-land{ animation:none; }
  /* sin movimiento: queda fijo el primer cuadro */
  /* Con movimiento reducido initSprites() no escribe data-f, así que alcanza con
     anular el fundido: queda el cuadro 1 fijo por la regla :first-child. */
  .sprite img{ transition:none; }
  html{ scroll-behavior:auto; }
}

/* ---------- ritmo de superficies ----------
   Una banda no cambia solo su fondo: redefine los tokens para lo que tiene
   adentro, así las cards, bordes y textos que contiene se adaptan solos sin
   una sola clase extra en el HTML. */
.banda-suave{
  background:var(--c-primary-soft);
  --c-page:var(--c-primary-soft);
}
.dark .banda-suave{ background:#12293a; --c-page:#12293a; }

.banda-cobalto{
  background:var(--c-cobalt);
  color:#fff;
  --c-ink:#ffffff;
  --c-ink-muted:rgba(255,255,255,.82);
  --c-paper:rgba(255,255,255,.10);
  --c-line:rgba(255,255,255,.24);
  --c-line-deep:rgba(0,0,0,.25);
  --c-cobalt-text:#ffffff;
  --c-page:var(--c-cobalt);
}
.dark .banda-cobalto{ background:var(--c-cobalt-deep); --c-page:var(--c-cobalt-deep); }

/* Resaltador: subraya una palabra con un bloque de color plano, como marcador.
   Va detrás del texto, por eso el z-index negativo sobre un contexto propio. */
.resalte{ position:relative; z-index:0; white-space:nowrap; }
.resalte::after{
  content:""; position:absolute; left:-.08em; right:-.08em; bottom:.06em;
  height:.34em; background:var(--c-gold); border-radius:4px; z-index:-1;
}
.resalte.celeste::after{ background:var(--c-primary); }

/* Subrayado que se dibuja con el scroll: el trazo crece de izquierda a derecha a
   medida que el título entra en pantalla, como marcado con resaltador. El progreso
   lo escribe el script del landing en --sub (0..1). Con movimiento reducido queda
   el subrayado completo, sin animación. */
.resalte-scroll::after{ transform:scaleX(var(--sub, 0)); transform-origin:left center; }
@media (prefers-reduced-motion: reduce){ .resalte-scroll::after{ transform:none; } }

/* ---------- la familia se junta (landing) ----------
   Los cinco animales entran desde los costados y se agrupan en el centro a medida
   que se hace scroll, como armando una escena. El desplazamiento de cada uno lo
   escribe el script en --fx; el bamboleo de "caminar" va en el <img> interior y el
   desplazamiento en el <span> exterior, porque una animación CSS de transform
   pisaría la traslación si vivieran en el mismo elemento. */
.familia-escena{
  display:flex; justify-content:center; align-items:flex-end;
  gap:clamp(8px, 2vw, 22px); margin-top:3.5rem;
  /* Los animales entran desde fuera de la caja y se recortan contra el borde, sin
     empujar el ancho del documento —que era el defecto anterior: scroll horizontal
     en toda la página—. Es `clip` y no `hidden` a propósito: `hidden` volvería la
     caja un contenedor de scroll y cortaría el bamboleo vertical. */
  overflow-x:clip;
}
/* `overflow-x:clip` es de 2022 (Safari 16). Donde no exista, `hidden` cumple la
   misma función de contención; el respiro vertical evita que recorte el bamboleo. */
@supports not (overflow-x: clip){
  .familia-escena{ overflow-x:hidden; padding-block:16px; }
}
.familia-pet{
  display:block;
  /* El script escribe las cuatro: entrada lateral, altura, escala e inclinación.
     Van juntas en un solo transform para que el compositor resuelva una matriz sola. */
  transform:translate3d(var(--fx, 0px), var(--fy, 0px), 0)
            rotate(var(--fr, 0deg)) scale(var(--fs, 1));
  opacity:var(--fo, 1);
  transform-origin:50% 90%;   /* pivota sobre las patas, no sobre el centro del dibujo */
  /* Muy corta: suaviza los saltos de la rueda del mouse sin amortiguar el rebote,
     que lo calcula el script. La original era de .25s y se sentía como arrastre. */
  transition:transform .09s linear, opacity .12s ease-out;
  will-change:transform, opacity;
}
.familia-pet{ position:relative; }
.familia-pet img{
  width:clamp(64px, 11vw, 116px); height:auto; display:block;
  transition:opacity .18s linear;
}
/* El festejo se apila sobre la caminata; el que manda el tamaño del <span> es el
   primero, en flujo normal. Al llegar se funde de uno al otro. */
.familia-pet .pet-llega{ position:absolute; left:0; top:0; width:100%; opacity:0; }
.familia-pet[data-llego] .pet-anda { opacity:0; }
.familia-pet[data-llego] .pet-llega{ opacity:1; }
.familia-pet:nth-child(1) img{ animation:lsu-float 5.2s ease-in-out infinite; }
.familia-pet:nth-child(2) img{ animation:lsu-float 6.1s ease-in-out .4s infinite; }
.familia-pet:nth-child(3) img{ animation:lsu-float 5.6s ease-in-out .8s infinite; }
.familia-pet:nth-child(4) img{ animation:lsu-float 6.4s ease-in-out .2s infinite; }
.familia-pet:nth-child(5) img{ animation:lsu-float 5.9s ease-in-out .6s infinite; }
@media (prefers-reduced-motion: reduce){
  .familia-pet{ transform:none; opacity:1; transition:none; }
  .familia-pet img{ animation:none; transition:none; }
  /* Sin movimiento no hay entrada que mostrar: queda directamente el festejo. */
  .familia-pet .pet-anda { opacity:0; }
  .familia-pet .pet-llega{ opacity:1; }
}
/* En oscuro el bloque de color detrás de texto blanco ensucia la lectura:
   pasa a ser un subrayado grueso por debajo de las letras. */
.dark .resalte::after{ height:.16em; bottom:-.1em; border-radius:3px; }

/* Número de paso como elemento gráfico, no como viñeta. */
.paso-num{
  font-family:Nunito,system-ui,sans-serif; font-weight:900;
  font-size:clamp(3.5rem, 7vw, 5rem); line-height:.85;
  color:var(--c-primary-soft); -webkit-text-stroke:2px var(--c-primary);
  display:block; margin-bottom:.25rem;
}
.dark .paso-num{ color:transparent; }

/* ---------- utilidades ---------- */
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
}

/* ---------- CSS funcional del ML (no tocar) ---------- */
/* ---------- práctica: referencia + cámara + feedback ----------
   Antes eran dos tarjetas apiladas: en un teléfono de 375×812 la cámara empezaba
   a 872 px del tope y "Comenzar" a 1363 px. Había que desplazarse para ver la
   seña, otra vez para verse en la cámara y una tercera para empezar. Ahora son
   tres zonas repartidas por grilla, y el feedback salió de adentro de la tarjeta
   de cámara para poder ocupar todo el ancho.

   Celular: cámara a todo el ancho y referencia superpuesta como picture-in-picture,
   para conservar las dos vistas sin reducir la mano a una miniatura ilegible.
   Escritorio: las dos columnas de siempre; la zona de feedback se pega al pie de
   la de cámara —bordes unidos— y el conjunto se sigue leyendo como una tarjeta.
   El relleno y los márgenes viven acá y no en clases de Tailwind porque el CSS
   de Tailwind se inyecta después de esta hoja y ganaría los empates. */
.practica-area{ display:grid; }
.zona-ref{ grid-area:ref; }
.zona-cam{ grid-area:cam; }
.zona-fb { grid-area:fb;  }

@media (max-width: 767px){
  .practica-area{ display:block; position:relative; }
  .practica-area .zona-cam{ padding:12px; }
  /* La referencia flota dentro del borde de la cámara. Su canvas pasa de 79×59
     a unos 115×86 px en 320 px y la cámara usa todo el ancho disponible. */
  .practica-area .zona-ref{
    position:absolute; z-index:4; top:62px; left:24px;
    width:min(48%, 150px); padding:8px; border-radius:12px;
  }
  .practica-area .cabecera-zona{ margin-bottom:8px; }
  .practica-area .zona-ref .pie-zona{ display:none; }
  .practica-area .etiqueta-zona{ padding:4px 7px; gap:3px; }
  .practica-area .etiqueta-zona .material-symbols-outlined{ font-size:16px; }
  .practica-area .zona-ref .etiqueta-zona .font-display{ display:none; }
  .practica-area #target{ font-size:1.6rem; }
  .practica-area .zona-ref .stage{ border-radius:10px; }
  /* Cuadrada: es la proporción con la que se sostiene un teléfono de frente, y
     deja un encuadre grande para la mano. */
  .zona-cam .stage{ aspect-ratio:1; }
  /* Los tres candidatos van en su propia línea y con el alto ya reservado: si
     aparecieran recién al detectar la mano, empujarían la cámara hacia abajo en
     plena práctica. */
  .zona-cam .cabecera-zona{ flex-wrap:wrap; row-gap:4px; }
  .zona-cam #top3{
    width:100%; justify-content:flex-start; align-items:center;
    flex-wrap:nowrap; overflow-x:auto; min-height:26px;
  }
  .zona-cam #top3 .chip{ font-size:11px; padding:3px 8px; flex:none; }
  /* Acá el feedback no es una tarjeta: es la franja de abajo, sin borde. */
  .practica-area .zona-fb > :first-child{ margin-top:0; }
}

/* Con seis columnas las letras quedan en 40×40 px en un teléfono de 320 px.
   Cinco columnas conservan un blanco táctil mínimo de 44 px. */
@media (max-width:340px){ #mapa{ grid-template-columns:repeat(5,minmax(0,1fr)); } }

@media (min-width: 768px){
  .practica-area{
    flex:1 1 auto;             /* ocupa el alto libre, como la grilla anterior */
    grid-template-columns:1fr 1fr;
    grid-template-areas:"ref cam"
                        "ref fb";
    column-gap:2rem; row-gap:0;
  }
  .practica-area .zona-ref{ padding:24px; }
  /* Abierta por abajo; la zona de feedback la cierra. Las dos juntas dibujan el
     mismo rectángulo que antes era una sola tarjeta. */
  .practica-area .zona-cam{
    padding:24px 24px 0;
    border-bottom:0; border-bottom-left-radius:0; border-bottom-right-radius:0;
  }
  .practica-area .zona-fb{
    background:var(--c-paper); border:2px solid var(--c-line); border-top:0;
    border-radius:0 0 16px 16px; padding:0 24px 24px;
  }
  .practica-area .cabecera-zona{ margin-bottom:20px; }
  .practica-area .pie-zona{ margin-top:16px; }
}

/* En una computadora la tarea principal tiene que entrar completa en la primera
   pantalla: referencia, cámara, devolución y botón Comenzar. El abecedario queda
   debajo para quien quiera desplazarse, pero no obliga a desplazarse para iniciar. */
@media (min-width:1024px){
  .practica-page main{
    max-width:1080px; padding-top:16px; padding-bottom:28px;
  }
  .practica-page #practice-intro{ margin-bottom:10px; }
  .practica-page #practice-intro h1{ font-size:2rem; line-height:1.1; }
  .practica-page #practice-intro p{
    font-size:15px; line-height:1.35; margin-top:6px; max-width:760px;
  }
  .practica-page #practice-stats{ margin-bottom:12px; row-gap:4px; }
  .practica-page .practica-area{
    flex:none; width:100%; max-width:960px; margin-inline:auto;
    grid-template-columns:1fr 1fr;
    grid-template-areas:"ref cam" "fb fb";
    column-gap:24px; row-gap:10px;
  }
  .practica-page .practica-area .zona-ref,
  .practica-page .practica-area .zona-cam{ padding:16px; }
  .practica-page .practica-area .zona-cam{
    border:2px solid var(--c-line); border-radius:16px;
  }
  .practica-page .practica-area .cabecera-zona{ margin-bottom:10px; }
  .practica-page .practica-area .pie-zona{ margin-top:8px; font-size:14px; }
  .practica-page .practica-area .stage{
    flex:0 0 clamp(175px, 25vh, 225px);
    height:clamp(175px, 25vh, 225px); aspect-ratio:auto;
  }
  .practica-page #pet-holder img{
    width:56px; height:56px;
  }
  .practica-page .practica-area .zona-fb{
    background:transparent; border:0; border-radius:0; padding:0;
    display:grid; grid-template-columns:minmax(0,1fr) auto;
    align-items:center; column-gap:18px;
  }
  .practica-page .practica-area .zona-fb > :first-child{ margin-top:0; }
  .practica-page .practica-area #leyenda-dedos{
    grid-column:1 / -1; margin-top:5px;
  }
  .practica-page .practica-area #status{
    margin-top:0; max-width:190px;
  }
  .practica-page #practice-controls{ margin-top:10px; }
  .practica-page #alphabet-section{ margin-top:32px; }
}

.stage{ position:relative; width:100%; aspect-ratio:4/3; background:#0b0d12; border-radius:16px; overflow:hidden; }
.stage video, .stage canvas{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
#cam{ transform:scaleX(-1); }
/* #ov NO se espeja por CSS: drawCam() ya lo espeja en el propio canvas. */
