/* ============================================================
   motion.css · 2026-09-14
   Movimiento del chat. Archivo aparte a propósito: index.html
   tiene 7,800 líneas y se toca lo mínimo.
   Carga DESPUÉS del <style> del documento, así que gana por orden.
   ============================================================ */

/* ── 1 · Entrada de los mensajes ──────────────────────────────
   msgin movía 4px y no se percibía. 14px con una curva de salida
   suave se lee como "acaba de llegar" sin distraer.            */
@keyframes gm-msgin {
  from { opacity: 0; transform: translateY(14px) }
  to   { opacity: 1; transform: none }
}
/* tutor-chunks.css usa `.chat-msgs .msg-row` (0,2,0) y ganaba a `.msg-row` (0,1,0).
   Igualo la especificidad; como motion.css carga después, manda esta. */
.chat-msgs .msg-row { animation: gm-msgin .42s cubic-bezier(.22,1,.36,1) both }

/* el avatar entra un pelo antes y con rebote: da sensación de vida */
@keyframes gm-avatar {
  from { opacity: 0; transform: scale(.55) }
  to   { opacity: 1; transform: scale(1) }
}
.chat-msgs .msg-row .msg-avatar { animation: gm-avatar .44s cubic-bezier(.34,1.5,.64,1) both }
.chat-msgs .msg-row .msg-body { animation: gm-msgin .42s cubic-bezier(.22,1,.36,1) .06s both }

/* la burbuja del usuario entra desde su lado */
@keyframes gm-user {
  from { opacity: 0; transform: translateY(10px) translateX(8px) }
  to   { opacity: 1; transform: none }
}
.chat-msgs .msg-row.user .bubble-user { animation: gm-user .38s cubic-bezier(.22,1,.36,1) both }

/* ── 2 · El avatar respira mientras se genera ─────────────────
   Se activa cuando hay indicador de escritura en la conversación. */
@keyframes gm-latido {
  0%,100% { transform: scale(1);    filter: none }
  50%     { transform: scale(1.09); filter: brightness(1.08) }
}
.chat-msgs:has(.typing) .msg-row:last-child .msg-avatar.ai,
.chat-msgs:has([class*="typing"]) .msg-row:last-child .msg-avatar.ai {
  animation: gm-latido 1.6s ease-in-out infinite;
}

/* ── 3 · Las fuentes entran escalonadas ───────────────────────
   Llegan después de la respuesta: el escalonado se lee como
   "y aquí está el respaldo".                                    */
@keyframes gm-fuente {
  from { opacity: 0; transform: translateY(9px) }
  to   { opacity: 1; transform: none }
}
.sources-block { animation: gm-msgin .4s cubic-bezier(.22,1,.36,1) .12s both }
.sources-list .source-card { animation: gm-fuente .38s cubic-bezier(.22,1,.36,1) both }
.sources-list .source-card:nth-child(1) { animation-delay:.16s }
.sources-list .source-card:nth-child(2) { animation-delay:.22s }
.sources-list .source-card:nth-child(3) { animation-delay:.28s }
.sources-list .source-card:nth-child(4) { animation-delay:.34s }
.sources-list .source-card:nth-child(5) { animation-delay:.40s }
.sources-list .source-card:nth-child(n+6){ animation-delay:.46s }

.source-card {
  transition: border-color .16s ease, box-shadow .2s ease, transform .16s cubic-bezier(.34,1.4,.64,1);
}
.source-card:hover {
  border-color: var(--emerald, #10B981);
  box-shadow: 0 4px 14px rgba(16,185,129,.13);
  transform: translateY(-2px);
}

/* ── 4 · Los chips de grado y escala ──────────────────────────  */
@keyframes gm-chip {
  from { opacity: 0; transform: scale(.82) }
  to   { opacity: 1; transform: scale(1) }
}
.chip-grade, .chip-scale { animation: gm-chip .3s cubic-bezier(.34,1.5,.64,1) both }

/* ── 5 · Pantalla de bienvenida y sugerencias ─────────────────  */
.chat-welcome { animation: gm-msgin .5s cubic-bezier(.22,1,.36,1) both }
.chat-suggestion { animation: gm-fuente .4s cubic-bezier(.22,1,.36,1) both;
  transition: border-color .15s, background .15s,
              transform .15s cubic-bezier(.34,1.4,.64,1), box-shadow .18s }
.chat-suggestions .chat-suggestion:nth-child(1){ animation-delay:.10s }
.chat-suggestions .chat-suggestion:nth-child(2){ animation-delay:.16s }
.chat-suggestions .chat-suggestion:nth-child(3){ animation-delay:.22s }
.chat-suggestions .chat-suggestion:nth-child(4){ animation-delay:.28s }
.chat-suggestions .chat-suggestion:nth-child(n+5){ animation-delay:.34s }
.chat-suggestion:hover { transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(16,185,129,.16) }
.chat-suggestion:active { transform: translateY(0) }

/* ── 6 · Botón de enviar ──────────────────────────────────────  */
.chat-send {
  position: relative; overflow: hidden;
  transition: transform .15s cubic-bezier(.34,1.4,.64,1), box-shadow .18s, background .16s;
}
.chat-send:hover:not(:disabled) {
  transform: translateY(-1.5px);
  box-shadow: 0 5px 15px rgba(4,120,87,.28);
}
.chat-send:active:not(:disabled) { transform: translateY(0) scale(.97) }
.chat-send::after {
  content: ""; position: absolute; inset: 0; transform: translateX(-125%);
  background: linear-gradient(105deg, transparent 35%, rgba(255,255,255,.4) 50%, transparent 65%);
  pointer-events: none;
}
@keyframes gm-sheen { to { transform: translateX(125%) } }
.chat-send:hover:not(:disabled)::after { animation: gm-sheen .8s ease-out }

/* el campo se ilumina al escribir */
.chat-input-wrap:focus-within {
  box-shadow: inset 0 1px 0 var(--emerald, #10B981);
  transition: box-shadow .2s ease;
}

/* ── 7 · Quien pidió menos movimiento, no lo tiene ────────────
   index.html no tenía ni una sola guarda. Ésta las cubre todas. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   Segunda tanda · 2026-09-14
   ============================================================ */

/* ── 8 · La respuesta aparece por párrafos ────────────────────
   Da sensación de que se está redactando, sin tocar el JS:
   cada párrafo entra un poco después que el anterior.          */
@keyframes gm-parrafo {
  from { opacity: 0; transform: translateY(6px) }
  to   { opacity: 1; transform: none }
}
.chat-msgs .msg-row.ai .content > p,
.chat-msgs .msg-row.ai .content > ul,
.chat-msgs .msg-row.ai .content > ol,
.chat-msgs .msg-row.ai .content > table { animation: gm-parrafo .38s cubic-bezier(.22,1,.36,1) both }
.chat-msgs .msg-row.ai .content > *:nth-child(1){ animation-delay:.04s }
.chat-msgs .msg-row.ai .content > *:nth-child(2){ animation-delay:.11s }
.chat-msgs .msg-row.ai .content > *:nth-child(3){ animation-delay:.18s }
.chat-msgs .msg-row.ai .content > *:nth-child(4){ animation-delay:.25s }
.chat-msgs .msg-row.ai .content > *:nth-child(n+5){ animation-delay:.32s }

/* ── 9 · Acciones del mensaje: copiar, pulgar ─────────────────  */
.msg-action-icon {
  transition: transform .15s cubic-bezier(.34,1.5,.64,1), color .15s, opacity .15s;
}
.msg-action-icon:hover  { transform: scale(1.18) }
.msg-action-icon:active { transform: scale(.9) }
/* cuando el JS marque .hecho tras copiar, confirma con un salto */
@keyframes gm-hecho {
  0%   { transform: scale(1) }
  45%  { transform: scale(1.45); color: var(--emerald, #10B981) }
  100% { transform: scale(1);    color: var(--emerald, #10B981) }
}
.msg-action-icon.hecho, .msg-action-icon.copiado {
  animation: gm-hecho .5s cubic-bezier(.34,1.5,.64,1) both;
}

/* ── 10 · La estrella respira en la bienvenida ────────────────  */
@keyframes gm-respira {
  0%,100% { transform: scale(1)     }
  50%     { transform: scale(1.055) }
}
.chat-welcome svg, .chat-welcome img { animation: gm-respira 5s ease-in-out 1s infinite;
  transform-origin: center }

/* ── 11 · Contador de fuentes ─────────────────────────────────  */
@keyframes gm-cuenta {
  from { opacity: 0; transform: translateY(-5px) }
  to   { opacity: 1; transform: none }
}
.sources-count { display: inline-block;
  animation: gm-cuenta .34s cubic-bezier(.34,1.4,.64,1) .1s both }

/* ── 12 · Desplegar una fuente ────────────────────────────────
   Si el JS alterna .abierta / [open], la altura se abre suave.  */
.source-card .cuerpo, .source-card details > *:not(summary) {
  animation: gm-abre .3s cubic-bezier(.22,1,.36,1) both;
}
@keyframes gm-abre {
  from { opacity: 0; transform: translateY(-6px) }
  to   { opacity: 1; transform: none }
}

/* ── 13 · Botón de bajar al final ─────────────────────────────  */
@keyframes gm-sube-boton {
  from { opacity: 0; transform: translateY(14px) scale(.85) }
  to   { opacity: 1; transform: none }
}
.scroll-bottom, .ir-al-final, [class*="scroll-to-bottom"] {
  animation: gm-sube-boton .32s cubic-bezier(.34,1.45,.64,1) both;
  transition: transform .16s cubic-bezier(.34,1.4,.64,1), box-shadow .2s;
}
.scroll-bottom:hover, .ir-al-final:hover, [class*="scroll-to-bottom"]:hover {
  transform: translateY(-2px); box-shadow: 0 6px 18px rgba(9,9,11,.16);
}

/* ── 14 · El avatar mientras se genera ────────────────────────
   Pon .pensando en el <svg> del avatar mientras llega la
   respuesta. La estrella gira despacio y le sale una chispa.    */
/* Se anima el SVG entero, no un `.e1` de dentro: el avatar de la app usa
   `#i-sparkle-fill`, un <path> sin clase. Buscar `.e1` no encontraba nada
   y la estrella nunca giraba. */
.msg-avatar.ai svg.pensando,
.msg-avatar.ai svg.pensando .e1 {
  animation: gm-pensar 2.6s cubic-bezier(.45,0,.55,1) infinite;
  transform-origin: 50% 50%;
  transform-box: fill-box;
}
.msg-avatar.ai svg.pensando { transform-box: view-box }
@keyframes gm-pensar {
  0%,100% { transform: rotate(0)      scale(1)    }
  50%     { transform: rotate(180deg) scale(1.13) }
}

/* ── 15 · El logotipo de la cabecera ──────────────────────
   `.brand-logo` traía la estrella sola sobre un cuadrado emerald
   (`#i-sparkle-fill`). El logotipo de la marca es el documento con la
   estrella. motion.js le cambia el SVG; aquí se quita el cuadrado. */
.brand-logo.gpc-logotipo {
  background: transparent !important;
  border-radius: 0 !important;
}
.brand-logo.gpc-logotipo svg { width: 30px; height: 30px }

/* ── 16 · Un solo indicador ───────────────────────────────
   La app ya tenía tres puntos (.typing-dots). Con la estrella girando
   son dos cosas diciendo lo mismo. Se esconden los puntos mientras
   la estrella está en marcha.
   Para volver a los puntos y quitar la estrella: cambiar este bloque
   por `.msg-avatar.ai svg.pensando { animation: none }`. */
.chat-msgs .msg-row:has(svg.pensando) .typing-dots { display: none !important }

/* Sin :has() —navegadores viejos— se quedan los dos. Mal menor. */

/* ═══════════════════════════════════════════════════════════
   17 · UN SOLO GROSOR DE TRAZO
   ───────────────────────────────────────────────────────────
   EL GROSOR ES 1.5 SOBRE LIENZO DE 24. Sin excepciones.
   Es lo que ya usaban 90 de los 96 símbolos; lo demás eran
   restos: 1.6, 1.8, 2, 2.2 y 2.5 repartidos sin criterio.

   «Sobre lienzo de 24» importa: el grosor en píxeles de
   pantalla NO es el mismo en todos los iconos, y no debe
   serlo. Un icono de 64 px pinta 4 px de trazo y uno de 16 px
   pinta 1 px — los dos son 1.5 en unidades de diseño, que es
   la proporción que el ojo lee como «el mismo peso».

   Los <symbol> los corrige motion.js tocando el atributo:
   dentro de un <use>, el atributo del símbolo gana a la
   propiedad heredada del <svg> de fuera, así que desde CSS no
   se les llega. Aquí van solo los SVG sueltos, los que
   icoMap / ICO_MAP / INSTITUCIONES_GRUPOS inyectan a mano.  */
svg[stroke-width] { stroke-width: 1.5 }

/* ── 17b · Las nueve reglas sueltas de index.html ───────────
   index.html fija además el grosor desde CSS en nueve sitios.
   Tres de ellos NO se arreglan repitiendo el selector: viven
   en bloques <style> que van DESPUÉS del <link> de motion.css
   (esta hoja es la nº 4 de diez; cinco <style> quedan detrás),
   y a igualdad de especificidad gana la última. De ahí el
   !important, que aquí no tapa ninguna variante: es un valor
   viejo contra el único grosor de la app.

   Si algún día motion.css pasa a cargarse al final del
   documento, este bloque entero sobra.                      */
.rg-acc-icon svg,                        /* 17 px */
.rg-promo-icon svg,                      /* 18 px */
.rg-promo-arrow svg,                     /* 18 px, iba a 2.5 */
.card-row .arrow,                        /* 14 px, iba a 2.5 */
.menu-item .arrow,                       /* 14 px, iba a 2.5 */
.rg-tldr h2 svg                          /* 13 px, iba a 2   */
{ stroke-width: 1.5 }

#s-news-detail .nd-back svg,             /* 20 px, iba a 2.2 */
#s-news-detail .nd-action svg,           /* 20 px, iba a 2.2 */
#s-news .nx-feat .nx-feat-iconbig svg,   /* 28 px, iba a 2.2 */
#s-inst-doc .id-acc-chev svg             /* 14 px, iba a 2.5 */
{ stroke-width: 1.5 !important }

/* El logotipo queda fuera: sus trazos llevan grosor propio
   (1.5 el documento, 1.3 los renglones) calculado sobre un
   lienzo de 48, no de 24.                                   */
.brand-logo svg[stroke-width], .splash-logo svg[stroke-width],
.auth-logo svg[stroke-width] { stroke-width: revert }


/* ═══════════════════════════════════════════════════════════
   18 · EL CARRUSEL DE BIENVENIDA
   ───────────────────────────────────────────────────────────
   El cuadro era negro macizo (var(--primary) = #09090B) y el
   icono blanco. Pesaba mucho para ser lo primero que se ve, y
   no decía nada de la marca. Pasa a ser esmeralda sobre un
   fondo del mismo verde muy rebajado.                       */
.onb-slide .onb-icon {
  background: linear-gradient(145deg, rgba(52,211,153,.14), rgba(4,120,87,.10));
  color: var(--emerald);
  border: 1px solid rgba(16,185,129,.22);
  box-shadow: 0 10px 28px -14px rgba(16,185,129,.55);
  border-radius: 26px;
}
html[data-theme="dark"] .onb-slide .onb-icon {
  background: linear-gradient(145deg, rgba(52,211,153,.18), rgba(4,120,87,.12)) !important;
  color: #34D399 !important;
  border-color: rgba(52,211,153,.28) !important;
}
/* entra el icono un pelo antes que el texto */
.onb-slide.active .onb-icon { animation: gm-onb-ico .5s cubic-bezier(.34,1.4,.64,1) both }
.onb-slide.active h2       { animation: gm-onb-txt .45s cubic-bezier(.22,1,.36,1) .06s both }
.onb-slide.active p        { animation: gm-onb-txt .45s cubic-bezier(.22,1,.36,1) .12s both }
@keyframes gm-onb-ico { from { opacity:0; transform: scale(.82) translateY(10px) } }
@keyframes gm-onb-txt { from { opacity:0; transform: translateY(8px) } }

/* ── 17b · Las nueve reglas sueltas de index.html ───────────
   Además de los atributos, index.html fija el grosor desde
   CSS en nueve sitios, y tres llevan un #id delante, así que
   ganaban por especificidad a la regla de arriba. Se repiten
   con la misma especificidad; como motion.css carga después,
   mandan estas.

   El criterio es el mismo de siempre: 1.5, y 2 solo por
   debajo de 16 px, donde 1.5 se rompe contra el píxel.     */
.rg-acc-icon svg,                       /* 17 px */
.rg-promo-icon svg,                     /* 18 px */
.rg-promo-arrow svg,                    /* 18 px, iba a 2.5 */
#s-news-detail .nd-back svg,            /* 20 px, iba a 2.2 */
#s-news-detail .nd-action svg,          /* 20 px, iba a 2.2 */
#s-news .nx-feat .nx-feat-iconbig svg   /* 28 px, iba a 2.2 */
{ stroke-width: 1.5 }

.card-row .arrow,                       /* 14 px */
.menu-item .arrow,                      /* 14 px */
.rg-tldr h2 svg,                        /* 13 px */
#s-inst-doc .id-acc-chev svg            /* 14 px */
{ stroke-width: 2 }

/* Los títulos parten en dos líneas a 320 px de ancho. `balance`
   reparte las palabras en vez de dejar una suelta abajo. */
.onb-slide h2 { text-wrap: balance }
.onb-slide p  { text-wrap: pretty }
