/* Tanda 3 — las cinco transiciones que faltaban (informe de refinamiento, ítem #2).
 *
 * Ponderación: Jakub primario (pulido de producción), Jhey secundario, Emil selectivo en
 * menú y formulario, que son las interacciones frecuentes.
 *
 * NO SE INVENTA VOCABULARIO. Todo lo que sigue sale de lo que el export ya usa:
 *   · curva firma  cubic-bezier(0.16, 1, 0.3, 1)   — 7 usos en el export
 *   · entrada      @keyframes upran-expand 320ms   — ya definida, para el form de cierre
 *   · micro        transition: opacity 160ms       — ya en los dos submits
 *   · "no accionable" = opacity 0.45 + cursor default — ya en el submit inválido
 *
 * Y una regla del export que no es obvia hasta que se la mira de cerca: reparte las curvas
 * POR PROPIEDAD, no por elemento. En una misma declaración escribe
 *     transition: opacity 260ms ease, transform 900ms cubic-bezier(0.33,1,0.68,1)
 * o sea: lo espacial —width, transform, min-height— lleva bezier; lo que sólo cambia de
 * intensidad —opacity, color, filter, box-shadow— lleva ease. Acá se respeta esa repartija.
 * No es un detalle académico: la curva firma es una ease-out exponencial muy agresiva y a un
 * tercio del recorrido ya va por 0,90 de opacidad. Un crossfade con esa curva dura 200 ms en
 * el motor y unos 70 en el ojo. Se vio en el filmstrip, donde tres fotogramas seguidos daban
 * idénticos; la tabla de duraciones no lo mostraba.
 * La tanda extiende ese idioma a los estados que se habían quedado sin él. Ningún estado en
 * REPOSO cambia: cambia cómo se llega, no dónde se llega.
 *
 * CERO DELAYS, a propósito. La regla global de prefers-reduced-motion colapsa
 * animation-duration y transition-duration con !important, pero NO resetea los delays: un
 * solo delay dejaría motion vivo con reduced-motion activo y tiraría abajo el 4/4 de esa
 * dimensión. Sin delays, todo lo de acá queda cubierto por la regla que ya existe.
 */

@keyframes upran-entrada {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes upran-entrada-aviso {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* ── 1 · Panel del menú mobile ──────────────────────────────────────────────
 * display no es animable, así que va por transition-behavior: allow-discrete más
 * @starting-style — no por un transition: display a secas, que no hace nada.
 * Si el navegador no soporta allow-discrete, la declaración entera se descarta y el panel
 * aparece de golpe: exactamente el comportamiento de hoy. Degrada a lo que ya había.
 *
 * Emil manda acá: es lo más tocado del sitio en mobile. 180 ms, y los 6 px son un
 * asentamiento, no un viaje. El translateX(-50%) del centrado se conserva en los tres
 * estados; perderlo descoloca el panel media pantalla.
 * El cerrado también transiciona: el panel se va como vino. */
@media (max-width: 767px) {
  .upran-nav-links {
    opacity: 0;
    transform: translateX(-50%) translateY(-6px) !important;
    /* El transform lleva !important porque el responsive lo declara así y hay que empatarle.
     * El transition NO, y no es un descuido: la regla global de reduced-motion es
     * `* { transition-duration: 0.01ms !important }`, y entre dos !important gana la
     * especificidad. Con !important acá, .upran-nav-links (0,1,0) le ganaba a `*` (0,0,0) y
     * el panel seguía animando con reduced-motion activo. Lo cazó motion-tanda3.mjs: era el
     * único punto de los cinco que no colapsaba. Sin !important, la regla global gana y
     * colapsa como debe. */
    transition: opacity 180ms ease,
                transform 180ms cubic-bezier(0.16, 1, 0.3, 1),
                display 180ms allow-discrete;
  }

  .upran-menu-abierto .upran-nav-links {
    opacity: 1;
    transform: translateX(-50%) translateY(0) !important;
  }

  @starting-style {
    .upran-menu-abierto .upran-nav-links {
      opacity: 0;
      transform: translateX(-50%) translateY(-6px) !important;
    }
  }
}

/* ── 2 · Las 7 pantallas de la demo · y 4 · la ficha del lead ───────────────
 * El runtime REEMPLAZA el nodo de la pantalla al cambiar de sección (medido con
 * MutationObserver: nodos creados y quitados, no atributos mutados). Una animación de
 * entrada dispara sola sobre un nodo recién creado; una transition no, porque no hay estado
 * previo del que partir. Por eso animation y no transition.
 *
 * Y por eso la regla se escribe sobre el hijo del panel de contenido: sólo corre cuando ese
 * hijo es nuevo. Si una pantalla se queda y sólo cambia por dentro, no parpadea. La propia
 * mecánica de las animaciones CSS hace de filtro.
 *
 * Opacidad pura, sin desplazamiento: el informe lo pide explícito para los puntos frecuentes
 * y el panel tiene alto fijo — cualquier translate ahí adentro se lee como un tirón.
 * La ficha del lead entra por esta misma regla: es el mismo nodo reemplazado del mismo panel.
 * Darle motion propio sería inventar una segunda gramática para el mismo gesto. */
/* SIN fill-mode, y es deliberado. Con `both` la animación queda en estado filling para
 * siempre, y con ella la capa de composición promovida; eso cambia el rasterizado del texto
 * del panel y ensuciaba 825 px en el diff en reposo contra producción, sin que se moviera
 * nada. Como el delay es 0 y el fotograma final ES el estado estático, el fill-mode no
 * aportaba nada: sacándolo, la capa se libera al terminar y el reposo vuelve a ser idéntico. */
[data-demo] [style*="flex: 1 1 0%"] > div {
  animation: upran-entrada 200ms ease;
}

/* ── 3 · Error del formulario ───────────────────────────────────────────────
 * role="alert" es el selector, no el color: --sem-red también pinta contenido de la demo
 * ("Se está enfriando", los deltas negativos) que no debe animarse. El rol identifica
 * exactamente lo que es un aviso, y alcanza a los dos formularios —el brief de la demo y el
 * de cierre— sin nombrar a ninguno.
 *
 * El contenedor aria-live de la Tanda 1 persiste; lo que entra es el span. 4 px de
 * asentamiento hacia abajo: el mensaje llega, no aparece de la nada. */
[role="alert"] {
  animation: upran-entrada-aviso 200ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* ── 5 · Estado "enviando" ──────────────────────────────────────────────────
 * Medido: durante el vuelo el botón no cambiaba en nada —ni texto, ni opacidad, ni
 * disabled—. El usuario tocaba y no pasaba nada visible hasta que el fetch resolvía.
 *
 * El sistema YA tiene su forma de decir "esto no se puede tocar ahora": el mismo submit,
 * cuando el formulario está incompleto, va a opacity 0.45 con cursor default, y ya trae su
 * transition: opacity 160ms inline. Acá se reusa esa forma tal cual. No hay spinner, no hay
 * texto nuevo, no hay duración nueva: es el estado que el sistema ya sabía dibujar, aplicado
 * al momento en que faltaba.
 *
 * La clase vive en <html> y la pone contacto.js, que es el único punto por donde pasan los
 * dos envíos. En un nodo del nav o del form no sobreviviría: el runtime re-renderiza y se la
 * lleva puesta (misma lección que nav.js).
 *
 * pointer-events: none no es cosmético — sin él, dos toques seguidos mandan dos leads. */
.upran-enviando button[style*="transition: opacity 160ms"] {
  opacity: 0.45 !important;
  cursor: default !important;
  pointer-events: none !important;
}
