/* ============================================================================
   MOVIMIENTO -- capa compartida del ERP (y de la tienda, que la carga igual).
   Guia de uso: sistema/docs/MOVIMIENTO.md

   Reglas de la capa:
   - Cada movimiento dice algo (entro, cambio, se guardo, fallo, se esta
     cargando). Nada se mueve para adornar.
   - Solo transform y opacity en lo que se repite (listas, toasts, modales):
     el compositor lo mueve sin recalcular el diseno.
   - No cambia colores: todo sale de los tokens existentes (--t-* en el ERP,
     --brand en la tienda) o de negro/blanco con alfa.
   - prefers-reduced-motion: no hay desplazamientos; el cambio de estado se
     sigue viendo (color, check, borde) y la carga late en vez de girar.
   ============================================================================ */

/* ---------- 0. Tokens ----------
   Tres duraciones y cuatro curvas. Los --t-dur-* viejos siguen valiendo; estos
   son los que usa la capa y los que deben usar las pantallas nuevas. */
:root {
    --mov-rapido: 150ms;    /* respuesta al dedo: pulsado, hover, foco, salir */
    --mov-base: 220ms;      /* cambio de estado, entrar, menus */
    --mov-enfasis: 320ms;   /* modal, hoja, confirmacion, lo que aparece de la nada */
    --mov-paso: 35ms;       /* escalonado entre elementos de una lista */
    --mov-entrar: cubic-bezier(.2, 0, 0, 1);      /* frena al llegar */
    --mov-salir: cubic-bezier(.4, 0, 1, 1);       /* acelera al irse */
    --mov-estandar: cubic-bezier(.2, 0, 0, 1);
    /* Resorte suave (5% de rebote) para CONFIRMACIONES: toast, check, tarjeta
       que se acomoda. Nunca para un cambio de estado comun. */
    --mov-resorte: cubic-bezier(.34, 1.36, .64, 1);
    --mov-acento: var(--t-accent, var(--brand, #3b63ac));
    --mov-ok: var(--t-ok, #16a34a);
    --mov-sombra-levantada: 0 18px 38px -12px rgb(15 23 42 / .32), 0 4px 10px -4px rgb(15 23 42 / .18);
}
@supports (transition-timing-function: linear(0, 1)) {
    :root {
        --mov-resorte: linear(0, .06 2.8%, .22 6%, .5 11%, .76 16.6%, .92 22%, 1.01 28%, 1.045 34%,
                              1.05 38%, 1.04 44%, 1.015 54%, 1 64%, .997 76%, 1);
    }
}

@keyframes mov-entra    { from { opacity: 0; transform: translateY(8px); } }
@keyframes mov-entra-fila { from { opacity: 0; transform: translateY(6px); } }
@keyframes mov-sale     { to { opacity: 0; transform: translateY(-4px) scale(.98); } }
@keyframes mov-pop      { from { opacity: 0; transform: scale(.6); } }
@keyframes mov-salto    { 40% { transform: scale(1.22); } }
@keyframes mov-cambio   { 40% { transform: scale(1.06); } }
@keyframes mov-sacude   { 15%, 55% { transform: translateX(-6px); } 35%, 75% { transform: translateX(5px); } 90% { transform: translateX(-2px); } }
@keyframes mov-giro     { to { transform: rotate(360deg); } }
@keyframes mov-respira  { 50% { opacity: .35; } }
@keyframes mov-trazo    { to { stroke-dashoffset: 0; } }
@keyframes mov-brillo   { from { opacity: 1; } to { opacity: 0; } }
@keyframes mov-barrido  { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
@keyframes mov-barra    { from { transform: scaleX(0); } 60% { transform: scaleX(.7); } to { transform: scaleX(.92); } }
@keyframes mov-acomoda  { from { transform: scale(1.03); box-shadow: var(--mov-sombra-levantada); } }
@keyframes mov-tiempo   { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* ---------- 1. Utilidades declarativas (data-mov) ---------- */

/* entrar: aparece al cargar o al insertarse. */
[data-mov~="entrar"], .mov-entra { animation: mov-entra var(--mov-base) var(--mov-entrar) backwards; }

/* lista: los hijos entran escalonados (tope en el 12, lo de abajo no espera).
   Los hijos que se agregan despues con JS entran solos (movimiento.js). */
[data-mov~="lista"] > * { animation: mov-entra var(--mov-base) var(--mov-entrar) backwards; }
[data-mov~="lista"] > :nth-child(2)  { animation-delay: calc(var(--mov-paso) * 1); }
[data-mov~="lista"] > :nth-child(3)  { animation-delay: calc(var(--mov-paso) * 2); }
[data-mov~="lista"] > :nth-child(4)  { animation-delay: calc(var(--mov-paso) * 3); }
[data-mov~="lista"] > :nth-child(5)  { animation-delay: calc(var(--mov-paso) * 4); }
[data-mov~="lista"] > :nth-child(6)  { animation-delay: calc(var(--mov-paso) * 5); }
[data-mov~="lista"] > :nth-child(7)  { animation-delay: calc(var(--mov-paso) * 6); }
[data-mov~="lista"] > :nth-child(8)  { animation-delay: calc(var(--mov-paso) * 7); }
[data-mov~="lista"] > :nth-child(n+9) { animation-delay: calc(var(--mov-paso) * 8); }
[data-mov~="lista"] > :nth-child(n+13) { animation: none; }
[data-mov~="lista"] > .mov-entra { animation: mov-entra var(--mov-base) var(--mov-entrar) backwards; }

/* contador: numeros de ancho fijo para que no bailen mientras cuentan. */
[data-mov~="contador"], .t-kpi-value, .tab-resumen-num { font-variant-numeric: tabular-nums; }

/* check: un circulo y una palomita que se dibujan (lo inserta movimiento.js). */
.mov-check { display: inline-block; width: 1.25em; height: 1.25em; vertical-align: -.25em; flex: none; }
.mov-check circle { stroke-dasharray: 64; stroke-dashoffset: 64; animation: mov-trazo .36s var(--mov-entrar) forwards; }
.mov-check path   { stroke-dasharray: 18; stroke-dashoffset: 18; animation: mov-trazo .24s var(--mov-entrar) .26s forwards; }
.mov-check { animation: mov-pop var(--mov-enfasis) var(--mov-resorte) backwards; }

/* cambio-estado: el chip cambia de color con transicion y da un pulso corto. */
[data-mov~="cambio-estado"], [data-estado], .ficha-chip, .t-chip, .t-badge,
body.t-skin main span.rounded[class*=" bg-"], body.t-skin main span.rounded-full[class*=" bg-"] {
    transition: background-color var(--mov-base) var(--mov-estandar), color var(--mov-base) var(--mov-estandar),
                border-color var(--mov-base) var(--mov-estandar), box-shadow var(--mov-base) var(--mov-estandar);
}
.mov-cambio { animation: mov-cambio var(--mov-enfasis) var(--mov-resorte); }

/* brillo-nuevo: lo recien agregado o recien cambiado se ilumina y se apaga. */
[data-mov~="brillo-nuevo"]:not(tr), .mov-brillo:not(tr) { position: relative; }
[data-mov~="brillo-nuevo"]:not(tr)::after, .mov-brillo:not(tr)::after {
    content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
    background: color-mix(in srgb, var(--mov-acento) 16%, transparent);
    box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--mov-acento) 45%, transparent);
    animation: mov-brillo 1.6s var(--mov-salir) .3s forwards;
}
tr[data-mov~="brillo-nuevo"] > td, tr.mov-brillo > td { animation: mov-brillo-celda 1.9s var(--mov-salir); }
@keyframes mov-brillo-celda { from, 20% { background-color: color-mix(in srgb, var(--mov-acento) 14%, transparent); } }

/* sacudir-error: el campo invalido dice "no" con la cabeza. */
.mov-sacude { animation: mov-sacude .34s var(--mov-estandar); }

/* esqueleto: bloque gris con un barrido de luz mientras llega el contenido.
   Con contenido deja de ser esqueleto solo (:empty). */
.mov-esqueleto, [data-mov~="esqueleto"]:empty {
    position: relative; overflow: hidden; display: block;
    min-height: var(--mov-alto, 4.5rem); border-radius: 8px;
    background:
        linear-gradient(var(--t-bg-3, #e2e8f0) 0 0) 0 0 / 70% .75rem no-repeat,
        linear-gradient(var(--t-bg-3, #e2e8f0) 0 0) 0 1.5rem / 100% .75rem no-repeat,
        linear-gradient(var(--t-bg-3, #e2e8f0) 0 0) 0 3rem / 45% .75rem no-repeat;
    opacity: .9;
}
.mov-esqueleto::after, [data-mov~="esqueleto"]:empty::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent, rgb(255 255 255 / .55), transparent);
    animation: mov-barrido 1.3s ease-in-out infinite;
}
[data-theme="dark"] :is(.mov-esqueleto, [data-mov~="esqueleto"]:empty)::after { background: linear-gradient(90deg, transparent, rgb(255 255 255 / .08), transparent); }

/* sale: lo que se quita se va en vez de desaparecer de golpe (mov.salir). */
.mov-sale { animation: mov-sale var(--mov-rapido) var(--mov-salir) forwards; pointer-events: none; }

/* ---------- 2. Botones: pulsado, cargando y listo ---------- */
body.t-skin main :is(button, a)[class*="bg-"][class*="rounded"]:not(:disabled) {
    transition-property: transform, background-color, border-color, color, box-shadow, opacity;
    transition-duration: var(--mov-rapido);
    transition-timing-function: var(--mov-estandar);
}
body.t-skin main :is(button, a)[class*="bg-"][class*="rounded"]:not(:disabled):active { transform: scale(.98); }

/* Cargando: el texto queda transparente (el boton no cambia de ancho) y un
   aro gira en el centro. pointer-events: none evita el doble envio. */
.mov-cargando { pointer-events: none; color: transparent !important; cursor: progress; }
.mov-rel { position: relative; }
.mov-cargando > :not(.mov-giro):not(.mov-check) { visibility: hidden; }
.mov-giro, .mov-cargando > .mov-check {
    position: absolute; inset: 0; margin: auto; visibility: visible;
    color: var(--mov-color, currentColor);
}
.mov-giro {
    width: 1.1em; height: 1.1em; border-radius: 50%;
    border: 2px solid var(--mov-color, currentColor); border-right-color: transparent;
    animation: mov-giro .7s linear infinite;
}
input.mov-cargando { color: inherit !important; opacity: .7; }

/* ---------- 3. Navegacion entre paginas ----------
   Chrome/Edge/Android: @view-transition (ya declarado en taller-design.css)
   cruza la pagina vieja con la nueva. La barra, el menu y las pestanas tienen
   nombre propio para quedarse quietas: solo cambia el contenido. */
.t-topbar { view-transition-name: mov-barra; }
#sidebar { view-transition-name: mov-menu; }
.nav-inferior { view-transition-name: mov-pestanas; }
.rail { view-transition-name: mov-riel; }
::view-transition-old(root) { animation: mov-vt-sale var(--mov-rapido) var(--mov-salir) both; }
::view-transition-new(root) { animation: mov-vt-entra var(--mov-base) var(--mov-entrar) both; }
::view-transition-group(mov-barra), ::view-transition-group(mov-menu),
::view-transition-group(mov-pestanas), ::view-transition-group(mov-riel) { animation-duration: var(--mov-base); }
@keyframes mov-vt-sale  { to { opacity: 0; } }
@keyframes mov-vt-entra { from { opacity: 0; transform: translateY(6px); } }

/* Barra de progreso arriba mientras el servidor responde (todas las
   plataformas): el toque tiene respuesta en menos de 100 ms. */
.mov-progreso {
    position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 2000; pointer-events: none;
    background: var(--mov-acento); transform-origin: 0 50%; transform: scaleX(0); opacity: 0;
    transition: opacity var(--mov-base) var(--mov-salir);
}
.mov-progreso.activa { opacity: 1; animation: mov-barra 2.4s var(--mov-entrar) forwards; }

/* La entrada de la pantalla (taller-design.css: main > * con t-fade-up de
   500 ms y hasta 380 ms de espera) se acorta: 320 ms y 40 ms entre bloques.
   Con 900 ms la pantalla se sentia lenta aunque el servidor fuera rapido. */
body.t-skin main > * { animation-duration: var(--mov-enfasis); }
body.t-skin main > *:nth-child(1) { animation-delay: 0s; }
body.t-skin main > *:nth-child(2) { animation-delay: 40ms; }
body.t-skin main > *:nth-child(3) { animation-delay: 80ms; }
body.t-skin main > *:nth-child(4) { animation-delay: 120ms; }
body.t-skin main > *:nth-child(5) { animation-delay: 160ms; }
body.t-skin main > *:nth-child(n+6) { animation-delay: 200ms; }

/* Filas de las tablas: las primeras 12 entran escalonadas despues de su
   bloque. Lo de abajo del pliegue no espera.
   SOLO SE DESPLAZAN, NUNCA SE ESCONDEN (03/10/2026): con opacidad 0 de partida,
   las filas fuera de la vista quedaban en blanco (captura completa, impresion,
   historial de turnos de caja con 9 filas vacias) hasta hacer scroll. */
@keyframes mov-entra-fila-visible { from { transform: translateY(6px); } }
body.t-skin main tbody > tr:nth-child(-n+12) { animation: mov-entra-fila-visible var(--mov-base) var(--mov-entrar) backwards; }
body.t-skin main tbody > tr:nth-child(2)  { animation-delay: calc(60ms + var(--mov-paso) * 1); }
body.t-skin main tbody > tr:nth-child(3)  { animation-delay: calc(60ms + var(--mov-paso) * 2); }
body.t-skin main tbody > tr:nth-child(4)  { animation-delay: calc(60ms + var(--mov-paso) * 3); }
body.t-skin main tbody > tr:nth-child(5)  { animation-delay: calc(60ms + var(--mov-paso) * 4); }
body.t-skin main tbody > tr:nth-child(6)  { animation-delay: calc(60ms + var(--mov-paso) * 5); }
body.t-skin main tbody > tr:nth-child(7)  { animation-delay: calc(60ms + var(--mov-paso) * 6); }
body.t-skin main tbody > tr:nth-child(8)  { animation-delay: calc(60ms + var(--mov-paso) * 7); }
body.t-skin main tbody > tr:nth-child(n+9) { animation-delay: calc(60ms + var(--mov-paso) * 8); }

/* Tablero: las tarjetas KPI entran con el mismo pop, mas corto. */
body.t-skin .t-kpi { animation-duration: var(--mov-enfasis); }

/* ---------- 4. Toasts ----------
   Entran con resorte (en telefono desde arriba, como un aviso del sistema) y
   salen acelerando. La barrita de abajo dice cuanto falta para que se vaya. */
#toasts-container > * {
    position: relative; overflow: hidden;
    transition: transform var(--mov-enfasis) var(--mov-resorte), opacity var(--mov-base) var(--mov-entrar);
}
#toasts-container > .translate-x-full { transition: transform var(--mov-base) var(--mov-salir), opacity var(--mov-rapido) var(--mov-salir); }
@media (max-width: 640px) {
    #toasts-container > .translate-x-full { transform: translateY(-18px) scale(.96); }
}
#toasts-container .bi-x-octagon-fill, #toasts-container .bi-exclamation-triangle-fill { animation: mov-sacude .4s var(--mov-estandar) .2s; }
.mov-toast-tiempo {
    position: absolute; left: 0; right: 0; bottom: 0; height: 2px; pointer-events: none;
    background: rgb(255 255 255 / .45); transform-origin: 0 50%;
    animation: mov-tiempo linear forwards;
}

/* ---------- 5. Modales y hojas ----------
   El fondo se oscurece con fundido y el panel sube (telefono: hoja desde
   abajo, eso ya lo hace tailwind-extra.css) o crece un poco (tablet y
   escritorio). Al cerrar se van en 150 ms en vez de desaparecer de golpe:
   transition-behavior: allow-discrete retiene el display mientras sale.
   Navegadores sin soporte: abren y cierran como antes. */
.t-modal {
    transition: background-color var(--mov-base) var(--mov-entrar), display var(--mov-base) allow-discrete, overlay var(--mov-base) allow-discrete;
}
/* Solo el FONDO se funde (el panel no se vuelve transparente mientras sube). */
.t-modal.hidden, .t-modal[hidden] { background-color: transparent; pointer-events: none; transition-duration: var(--mov-rapido); transition-timing-function: var(--mov-salir); }
.t-modal .t-modal-panel { transition: transform var(--mov-enfasis) var(--mov-entrar), opacity var(--mov-base) var(--mov-entrar); }
.t-modal:is(.hidden, [hidden]) .t-modal-panel:not(.inset-0) { opacity: 0; transform: translateY(6px) scale(.97); transition-duration: var(--mov-rapido); transition-timing-function: var(--mov-salir); }
@starting-style {
    .t-modal { background-color: transparent; }
    .t-modal .t-modal-panel:not(.inset-0) { opacity: 0; transform: translateY(10px) scale(.96); }
}
@media (max-width: 640px) {
    .t-modal:is(.hidden, [hidden]) .t-modal-panel:not(.inset-0) { opacity: 1; transform: translateY(100%); transition-duration: var(--mov-base); }
    @starting-style { .t-modal .t-modal-panel:not(.inset-0) { opacity: 1; transform: translateY(100%); } }
}
/* <dialog> nativo: mismo fundido al abrir y al cerrar. */
dialog { transition: opacity var(--mov-base) var(--mov-entrar), translate var(--mov-enfasis) var(--mov-entrar), display var(--mov-base) allow-discrete, overlay var(--mov-base) allow-discrete; }
dialog:not([open]) { opacity: 0; transition-duration: var(--mov-rapido); }
dialog::backdrop { transition: opacity var(--mov-base) var(--mov-entrar), display var(--mov-base) allow-discrete, overlay var(--mov-base) allow-discrete; }
@starting-style {
    dialog[open] { opacity: 0; translate: 0 10px; }
    dialog[open]::backdrop { opacity: 0; }
}

/* Menus y desplegables: caen 4 px y se funden. */
:is([role="menu"], [role="listbox"], .gh-menu) { transition: opacity var(--mov-base) var(--mov-entrar), translate var(--mov-base) var(--mov-entrar); }
@starting-style { :is([role="menu"], [role="listbox"], .gh-menu) { opacity: 0; translate: 0 -4px; } }

/* ---------- 6. Formularios ---------- */
body :is(input:not([type=checkbox]):not([type=radio]):not([type=range]), select, textarea) {
    transition: border-color var(--mov-rapido) var(--mov-estandar), box-shadow var(--mov-rapido) var(--mov-estandar),
                background-color var(--mov-rapido) var(--mov-estandar);
}

/* ---------- 7. Pestanas: el subrayado se desliza ----------
   movimiento.js pone .mov-indicador en las .ficha-pestanas (de subrayado) y
   en cualquier [data-mov~="pestanas"]; mientras tanto el subrayado propio
   de cada pestana se apaga para no verse doble. */
.mov-con-indicador { position: relative; }
.mov-con-indicador > .mov-indicador {
    position: absolute; left: 0; bottom: 0; width: 100px; height: 2px; border-radius: 2px;
    background: var(--mov-acento); transform-origin: 0 50%; pointer-events: none; opacity: 0;
    transition: transform var(--mov-enfasis) var(--mov-entrar);
}
.mov-con-indicador > .mov-indicador.sin-transicion { transition: none; }
.mov-con-indicador > :is(.activa, [aria-selected="true"]) { border-bottom-color: transparent !important; }

/* ---------- 8. Acordeones: <details> abre y cierra con altura animada ----------
   interpolate-size permite animar hasta height:auto (Chrome 129+); donde no
   exista abre de golpe como siempre. */
:root { interpolate-size: allow-keywords; }
details::details-content {
    block-size: 0; overflow-y: clip;
    transition: block-size var(--mov-base) var(--mov-estandar), content-visibility var(--mov-base) allow-discrete;
}
details[open]::details-content { block-size: auto; }
summary .bi-chevron-down, summary .bi-chevron-right { display: inline-block; transition: transform var(--mov-base) var(--mov-estandar); }
details[open] > summary .bi-chevron-down { transform: rotate(180deg); }
details[open] > summary .bi-chevron-right { transform: rotate(90deg); }

/* ---------- 9. Tooltips (data-mov-tip y los iconos de la barra superior) ---------- */
.mov-tip {
    position: fixed; top: 0; left: 0; z-index: 2100; max-width: 240px; pointer-events: none;
    padding: 6px 10px; border-radius: 8px; font: 600 12px/1.3 var(--t-sans, system-ui, sans-serif);
    color: #fff; background: rgb(15 23 42 / .92); box-shadow: 0 6px 16px -6px rgb(0 0 0 / .35);
    opacity: 0; scale: .96; transition: opacity var(--mov-rapido) var(--mov-salir), scale var(--mov-rapido) var(--mov-salir);
}
.mov-tip.visible { opacity: 1; scale: 1; transition-timing-function: var(--mov-entrar); }

/* ---------- 10. Globitos y contadores ---------- */
.mov-salto { animation: mov-salto var(--mov-enfasis) var(--mov-resorte); }
.mov-pop { animation: mov-pop var(--mov-enfasis) var(--mov-resorte) backwards; }

/* ---------- 11. Arrastrar: la tarjeta se levanta y al soltar se acomoda ---------- */
[draggable="true"] { transition: box-shadow var(--mov-base) var(--mov-entrar), transform var(--mov-base) var(--mov-entrar), opacity var(--mov-base); }
.mov-levantada { transform: scale(1.02) rotate(-.6deg); box-shadow: var(--mov-sombra-levantada); cursor: grabbing; }
.mov-origen { opacity: .35; transform: none; box-shadow: none; outline: 2px dashed color-mix(in srgb, var(--mov-acento) 50%, transparent); outline-offset: -2px; }
.mov-acomoda { animation: mov-acomoda var(--mov-enfasis) var(--mov-resorte); }
.mov-zona-destino { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--mov-acento) 45%, transparent); transition: box-shadow var(--mov-rapido); }

/* ---------- 12. Imagenes que llegan con fundido (sin saltos: solo opacidad) ---------- */
img.mov-img-carga { opacity: 0; }
img.mov-img-lista { transition: opacity var(--mov-enfasis) var(--mov-entrar); }

/* ---------- 13. Menos movimiento ----------
   Nada se desplaza, pero los estados se siguen viendo: el check aparece ya
   dibujado, el chip cambia de color (sin pulso) y la carga late en vez de
   girar (girar marea; latir no). En el ERP la red de seguridad de
   taller-design.css ya apaga animaciones y transiciones con !important. */
@media (prefers-reduced-motion: reduce) {
    .mov-check circle, .mov-check path { stroke-dashoffset: 0 !important; animation: none !important; }
    .mov-giro { animation: mov-respira 1.6s ease-in-out infinite !important; border-right-color: var(--mov-color, currentColor); opacity: .6; }
    .mov-progreso.activa { animation: none !important; transform: scaleX(1); opacity: .7; }
    .mov-esqueleto::after, [data-mov~="esqueleto"]:empty::after { animation: none !important; }
    .mov-sacude { animation: none !important; outline: 2px solid var(--t-danger, #dc2626); outline-offset: 1px; }
    [data-mov~="brillo-nuevo"]::after, .mov-brillo::after { animation: none !important; opacity: .6; }
    ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
    .mov-toast-tiempo { display: none; }
    /* Fuera del ERP (tienda) no hay red de seguridad global: se apaga aca lo propio. */
    :is(.t-modal, .t-modal-panel, dialog, details::details-content, [role="menu"], [role="listbox"], .gh-menu, .mov-indicador,
        [draggable="true"], .mov-tip, img.mov-img-lista, #toasts-container > *) { transition: none !important; }
    :is(dialog, .t-modal)::backdrop { transition: none !important; }
    :is([data-mov~="entrar"], [data-mov~="lista"] > *, .mov-entra, .mov-cambio, .mov-salto, .mov-pop, .mov-acomoda, .mov-check, .mov-sale) { animation: none !important; }
}
