/* public/css/transitions.css */

/* ══════════════════════════════════════
   CONTENIDO — entrada/salida
   ══════════════════════════════════════ */
/* overflow:hidden SOLO durante las animaciones de entrada/salida del SPA.
   Si lo dejamos permanente, clippea verticalmente el contenido de cada
   sección y .main no llega a scrollear (su único hijo .content no rebasa
   nunca su altura). Antes esto se compensaba sección por sección con
   `.content:has(.xx-shell){overflow:visible}` — frágil; ahora la clase
   ::has() abajo cubre solo el momento de la transición.

   `.content` es flex-column para que `.content-inner` pueda estirarse y
   ocupar todo el alto disponible. Sin esto, cuando el contenido es más
   corto que el viewport, queda una franja al fondo (padding del .content
   + .main background) que NO es parte del elemento animado: la transición
   de entrada SPA "no llega" hasta abajo. Con `.content-inner` flex:1 el
   elemento que se anima cubre toda el área, y la animación se ve hasta
   el borde inferior. */
.content{position:relative;display:flex;flex-direction:column}
.content:has(.content-inner.--entering),
.content:has(.content-inner.--exiting){overflow:hidden}

.content-inner{
    position:relative;z-index:1;
    flex:1 1 auto;
    display:flex;flex-direction:column;gap:18px
}

/* Solo opacidad — el translateY competía con el motion del curtain (la cortina
   se va al mismo tiempo que el contenido se desliza desde 16px), y como el
   slide ocurría detrás del curtain solo se veía el último tramo: parecía un
   "snap" al final. Con fade puro el contenido se resuelve fluido a medida que
   la cortina lo descubre. */
/* --tx-mul lo setea transitions.js al inicializar y al cambiar la pref de
   duración (default 1). Permite que enter/exit del contenido respeten la
   misma escala de tiempo que la cortina sin tener que setear inline cada vez. */
.content-inner.--entering{animation:contentEnter calc(.42s * var(--tx-mul, 1)) var(--ease-silk) both;will-change:opacity}
.content-inner.--exiting{animation:contentExit calc(.28s * var(--tx-mul, 1)) var(--ease-silk) both;pointer-events:none;will-change:opacity}

@keyframes contentEnter{
    0%{opacity:0}
    100%{opacity:1}
}
@keyframes contentExit{
    0%{opacity:1}
    100%{opacity:0}
}

/* ══════════════════════════════════════
   CAPAS DE TRANSICIÓN LOCAL (AJAX nav)
   vt-1 = top layer — orange accent flash
   Cubre sólo el área de la sección, dejando
   topbar y sidebar intactos durante la transición.

   Top stays at `var(--top-h)` (deja el topbar visible — el cuál ya cubre
   safe-top). Bottom usa offset negativo a través de safe-bottom para que
   la cortina llegue hasta el borde físico de la pantalla, sin cortar en
   el área de los controles de navegación / home indicator.
   ══════════════════════════════════════ */
.view-transition{
    position:fixed;
    /* top arranca donde TERMINA el topbar (incluye safe-top del notch).
       En mobile el topbar mide top-h + safe-top, así que con solo top-h
       la cortina arrancaba dentro del topbar y lo cubría parcialmente. */
    top:calc(var(--top-h) + var(--safe-top, 0px));
    left:var(--sidebar-w);
    right:calc(0px - var(--safe-right, 0px));
    bottom:calc(0px - var(--safe-bottom, 0px));
    pointer-events:none;overflow:hidden;
    transition:left .36s var(--ease-expo);
    /* Promueve el contenedor a su propia capa GPU — los keyframes
       internos (transforms en .vt-layer + opacity) se compositean sin
       layout/paint, manteniendo el frame budget bajo en 120Hz. */
    will-change:transform;
    transform:translateZ(0);
}
.sidebar.collapsed ~ .view-transition{left:var(--sidebar-c)}
@media(max-width:768px){
    /* En mobile el sidebar vive offscreen (translateX(-100%)) y .main/.topbar
       son full-width. Si el sidebar quedó con `.collapsed` (pref "iniciar
       colapsado"), la regla de arriba aplicaba aquí también y dejaba una
       franja vacía a la izquierda del cover por --sidebar-c px. */
    .view-transition,
    .sidebar.collapsed ~ .view-transition{left:0}
}

/* .vt-layer = MÁSCARA. Es lo que se desliza/clippea, pero ya no carga la
   textura: la lleva su hijo .vt-tex. overflow:hidden es CRÍTICO — recorta
   la textura interior a los límites actuales del layer (post-transform),
   creando el efecto Chowder de "ventana móvil sobre patrón estático". */
.vt-layer{
    position:absolute;inset:0;
    transform:translateY(200%);
    will-change:transform;
    transition:none;
    overflow:hidden;
}
/* .vt-tex = TEXTURA. Hijo del layer, mismo tamaño (inset:0). Recibe en JS
   el INVERSE del transform del padre via _inverseTranslate(), de modo que
   la composición padre*hijo da identity y el patrón queda visualmente fijo
   en el espacio del .view-transition mientras solo la máscara se mueve. */
.vt-tex{
    position:absolute;inset:0;
    will-change:transform;
}

/* ─── PALETAS — viven sobre .vt-tex (no sobre .vt-N) ───────────────────
   Static texture mapping: como la textura ya no se mueve con la cortina,
   los gradientes pueden ser MÁS ricos y direccionales sin riesgo de
   "estiramiento" visible. Aprovechamos para subir la cuota de accent y
   diferenciar más cada capa. */
/* ─── Patterns como blobs orgánicos (clouds-style) ────────────────────
   En vez de gradientes estáticos hardcoded, cada pattern es un set de
   hijos .vt-blob (creados por populateTex en transitions.js) con
   posiciones y tiempos aleatorios. Cada blob deriva lentamente y la
   composición global se siente VIVA — como nubes flotando en el espacio
   estático del .view-transition (Chowder mapping intacto). Cada cortina
   tiene su propio set random → no hay dos transiciones idénticas. */

/* Base oscura sobre la que viven los blobs */
.view-transition[data-tx-theme="dark"] .vt-1 .vt-tex{
    background: linear-gradient(160deg, #131a2c 0%, #0a0f1c 60%, #16203a 100%);
    container-type: inline-size;  /* habilita unidades cqw para los blobs */
}

/* Blob base — deriva lenta, posición/tamaño/timing random vía CSS vars
   seteadas por populateTex. mix-blend-mode:screen hace que blobs que se
   superponen brillen más, simulando nubes con densidad variable. */
.vt-blob{
    position:absolute;
    left:var(--x, 50%); top:var(--y, 50%);
    width:var(--size, 40cqw); height:var(--size, 40cqw);
    border-radius:50%;
    background:radial-gradient(circle,
        color-mix(in srgb, var(--accent) 28%, transparent) 0%,
        color-mix(in srgb, var(--accent) 12%, transparent) 35%,
        transparent 70%);
    opacity:var(--alpha, .35);
    transform:translate(-50%, -50%);
    animation:vtBlobDrift var(--dur, 14s) ease-in-out infinite alternate var(--delay, 0s);
    pointer-events:none;
    will-change:transform, opacity;
    mix-blend-mode:screen;
    filter:blur(8px);
}
@keyframes vtBlobDrift{
    0%   {transform:translate(-50%, -50%) translate(0, 0) scale(.92); opacity:calc(var(--alpha, .35) * .8)}
    100% {transform:translate(-50%, -50%) translate(var(--dx, 0%), var(--dy, 0%)) scale(1.08); opacity:var(--alpha, .35)}
}

/* SPOTLIGHT center — un foco dominante que respira. Sin filter blur
   porque ya es muy difuso; solo el scale-breathe. */
.vt-blob[data-kind="center-bright"]{
    left:50%; top:50%;
    width:75cqw; height:75cqw;
    background:radial-gradient(circle,
        color-mix(in srgb, var(--accent) 50%, transparent) 0%,
        color-mix(in srgb, var(--accent) 22%, transparent) 25%,
        color-mix(in srgb, var(--accent) 6%, transparent) 50%,
        transparent 70%);
    opacity:.75;
    filter:blur(4px);
    animation:vtBlobBreathe 7s ease-in-out infinite alternate;
}

/* RAYS center — un foco SUTIL para que los rayos tengan un origen visual */
.vt-blob[data-kind="center-soft"]{
    left:50%; top:50%;
    width:35cqw; height:35cqw;
    background:radial-gradient(circle,
        color-mix(in srgb, var(--accent) 45%, transparent) 0%,
        color-mix(in srgb, var(--accent) 15%, transparent) 40%,
        transparent 70%);
    opacity:.55;
    filter:blur(6px);
    animation:vtBlobBreathe 9s ease-in-out infinite alternate;
}
@keyframes vtBlobBreathe{
    0%   {transform:translate(-50%, -50%) scale(.9); opacity:.5}
    100% {transform:translate(-50%, -50%) scale(1.08); opacity:.85}
}

/* MESH grid — rejilla que se desliza lentamente */
.vt-blob[data-kind="grid"]{
    left:0; top:0;
    width:100%; height:100%;
    transform:none;
    border-radius:0;
    filter:none;
    background:
        linear-gradient(to right, color-mix(in srgb, var(--accent) 14%, transparent) 1px, transparent 1px),
        linear-gradient(to bottom, color-mix(in srgb, var(--accent) 14%, transparent) 1px, transparent 1px);
    background-size:42px 42px, 42px 42px;
    opacity:.55;
    mix-blend-mode:normal;
    animation:vtGridDrift 28s linear infinite;
}
@keyframes vtGridDrift{
    0%   {background-position:0 0, 0 0}
    100% {background-position:42px 42px, 42px 42px}
}

/* RAYS — blobs elongados (rayos) que rotan alrededor del centro */
.vt-blob[data-kind="ray"]{
    left:50%; top:50%;
    width:var(--size, 90cqw);
    height:5cqw;
    border-radius:50%;
    background:linear-gradient(90deg,
        transparent 0%,
        color-mix(in srgb, var(--accent) 28%, transparent) 30%,
        color-mix(in srgb, var(--accent) 28%, transparent) 70%,
        transparent 100%);
    filter:blur(6px);
    transform:translate(-50%, -50%) rotate(var(--rotate-start, 0deg));
    animation:vtRayRotate var(--dur, 24s) linear infinite var(--delay, 0s);
}
@keyframes vtRayRotate{
    from {transform:translate(-50%, -50%) rotate(var(--rotate-start, 0deg))}
    to   {transform:translate(-50%, -50%) rotate(calc(var(--rotate-start, 0deg) + 360deg))}
}

/* Reduce motion — congela los blobs respetando la pref del usuario */
.dx-reduce-motion .vt-blob{
    animation:none !important;
    transform:translate(-50%, -50%) !important;
}
.dx-reduce-motion .vt-blob[data-kind="grid"]{
    transform:none !important;
}

/* ══════════════════════════════════════════════════════════════════════
   HANDOFF CURTAIN — login ↔ dashboard
   ──────────────────────────────────────────────────────────────────────
   Hereda toda la maquinaria de .view-transition (3 layers + .vt-tex con
   blob system + applyMode driving los transforms), pero con dos diferencias:
     1) Posición FULL-SCREEN (no respeta topbar ni safe-areas — la cortina
        debe TRAGAR todo en la salida del login y abrirse desde todo en la
        entrada al dashboard).
     2) Z-index dominante para superponer al sidebar / topbar / toasts /
        cualquier UI persistente.
     3) display:none por default — se enciende solo durante el handoff.
   Un logo central aparece durante el momento "covered" como pieza visual
   que conecta los dos lados (cover + reveal forman un solo gesto). */
.dx-handoff{
    display:none;
    /* override del posicionamiento de .view-transition — necesario !important
       porque .view-transition setea top/left/right/bottom en otra regla. */
    position:fixed !important;
    top:calc(0px - var(--safe-top, 0px)) !important;
    left:calc(0px - var(--safe-left, 0px)) !important;
    right:calc(0px - var(--safe-right, 0px)) !important;
    bottom:calc(0px - var(--safe-bottom, 0px)) !important;
    z-index:99999 !important;
    transition:none !important;  /* sin animación de left (sidebar collapse) */
}
.dx-handoff.is-active{
    display:block;
    pointer-events:all;
}

/* Logo central — el "puente visual" entre cover y reveal. Aparece con
   un fade+blur durante el cover, se queda visible durante el momento
   "covered" mientras el navegador hace el redirect físico, y vuelve a
   aparecer en el dashboard durante el reveal antes de desvanecerse. */
.dx-handoff-logo{
    position:absolute;inset:0;
    display:flex;align-items:center;justify-content:center;
    z-index:10;
    pointer-events:none;
    opacity:0;
    /* will-change garantiza GPU layer para opacity/transform/filter
       desde el primer frame — sin esto el primer paint del fade-in podía
       generar un micro-stutter al promover la capa al GPU mid-animación. */
    will-change:opacity, transform, filter;
}
.dx-handoff-logo img{
    /* El logo nativo es 1024×420 (≈2.44:1, wordmark ancho). aspect-ratio
       refleja esa proporción real para que el browser reserve la altura
       correcta antes de decodificar el png — evita layout shift sin
       deformar la imagen. */
    width:clamp(180px, 32vw, 360px);
    height:auto;
    aspect-ratio:1024 / 420;
    filter:
        drop-shadow(0 0 30px rgba(255,255,255,.35))
        drop-shadow(0 0 80px color-mix(in srgb, var(--accent) 60%, transparent));
    /* Translate3d fuerza GPU layer también en la imagen, suaviza filter. */
    transform:translateZ(0);
}
.dx-handoff.is-covering .dx-handoff-logo{
    animation:dxHandoffLogoIn 1.1s cubic-bezier(.2,.8,.2,1) .25s forwards;
}
.dx-handoff.is-revealing .dx-handoff-logo{
    /* En la entrada al dashboard, el logo arranca visible (estado final del
       cover) y se desvanece con el reveal. Sin delay para que coincida con
       el inicio del retroceso de las layers. */
    opacity:1;
    animation:dxHandoffLogoOut .55s cubic-bezier(.5,0,.7,.4) forwards;
}
@keyframes dxHandoffLogoIn{
    0%   {opacity:0; transform:scale(.85); filter:blur(14px)}
    55%  {opacity:1; transform:scale(1.02); filter:blur(0)}
    100% {opacity:1; transform:scale(1); filter:blur(0)}
}
@keyframes dxHandoffLogoOut{
    0%   {opacity:1; transform:scale(1); filter:blur(0)}
    100% {opacity:0; transform:scale(1.18); filter:blur(10px)}
}

/* Reduce motion: el logo aparece y desaparece sin blur/scale (instant) */
.dx-reduce-motion .dx-handoff-logo{animation:none !important; opacity:1}
.dx-reduce-motion .dx-handoff.is-revealing .dx-handoff-logo{opacity:0}
.view-transition[data-tx-theme="dark"] .vt-2 .vt-tex{
    background:
        radial-gradient(ellipse 60% 50% at 50% 50%,
            color-mix(in srgb, var(--accent) 96%, #fff 4%) 0%,
            transparent 70%),
        linear-gradient(135deg,
            color-mix(in srgb, var(--accent) 88%, #fff 12%) 0%,
            var(--accent) 50%,
            color-mix(in srgb, var(--accent) 65%, #000 35%) 100%);
}
.view-transition[data-tx-theme="dark"] .vt-3 .vt-tex{
    background:
        radial-gradient(ellipse 70% 60% at 35% 35%,
            color-mix(in srgb, var(--accent) 28%, transparent) 0%,
            transparent 60%),
        linear-gradient(140deg,
            color-mix(in srgb, var(--accent) 38%, #102540) 0%,
            #0d1c34 50%,
            color-mix(in srgb, var(--accent) 18%, #0a1428) 100%);
}

.view-transition[data-tx-theme="light"] .vt-1 .vt-tex{
    background:
        radial-gradient(ellipse 75% 60% at 50% 35%,
            rgba(255,255,255,.22) 0%,
            transparent 65%),
        linear-gradient(135deg,
            color-mix(in srgb, var(--accent) 88%, #fff 12%) 0%,
            var(--accent) 55%,
            color-mix(in srgb, var(--accent) 72%, #000 28%) 100%);
}
.view-transition[data-tx-theme="light"] .vt-2 .vt-tex{
    background:
        radial-gradient(ellipse 80% 70% at 50% 50%,
            rgba(255,255,255,.5) 0%,
            transparent 70%),
        linear-gradient(160deg, #ece9e4 0%, #f3f0ec 100%);
}
.view-transition[data-tx-theme="light"] .vt-3 .vt-tex{
    background:
        radial-gradient(ellipse 75% 60% at 50% 50%,
            #ffffff 0%,
            #f8f5f0 70%),
        #faf8f5;
}

.vt-1{z-index:3}
.vt-2{z-index:2}
.vt-3{z-index:1}

/* ══════════════════════════════════════
   TOPBAR FADE
   ══════════════════════════════════════ */
.tb-title,.tb-pill{
    transition:opacity .22s var(--ease-silk), transform .22s var(--ease-silk)
}
.tb-title.--fade,.tb-pill.--fade{
    opacity:0;transform:translateY(-4px)
}

/* ══════════════════════════════════════
   SIDEBAR — 4 ESTADOS DE NAVEGACIÓN
   .active               → sección actual
   .active.--leaving     → abandonando sección (mismo layout, tono gris)
   .--loading            → cargando (click intermedio, texto gris + pulso icono)
   .--loading-latest     → próxima sección (accent + barra con progreso)
   ══════════════════════════════════════ */
@keyframes navPulse{0%,100%{opacity:1}50%{opacity:.3}}

/* .active.--leaving: mismo estilo de active pero gris */
.nav-it.active.--leaving,
.sb-dash.active.--leaving{
    color:var(--t3);
    background:var(--inp)
}
.nav-it.active.--leaving::before,
.sb-dash.active.--leaving::before{background:var(--t4)}
.nav-it.active.--leaving .nav-ic svg,
.sb-dash.active.--leaving .nav-ic svg{filter:none}

/* .--loading: cargando en cola */
.nav-it.--loading,
.sb-dash.--loading{color:var(--t3)}
.nav-it.--loading .nav-ic svg,
.sb-dash.--loading .nav-ic svg{animation:navPulse .55s ease-in-out infinite}

/* .--loading-latest: próxima sección (sobrescribe .--loading) */
.nav-it.--loading-latest,
.sb-dash.--loading-latest{
    color:var(--accent);
    background:var(--accent-soft)
}
.nav-it.--loading-latest::before,
.sb-dash.--loading-latest::before{
    content:'';position:absolute;left:0;top:50%;
    width:3px;height:20px;
    background:var(--accent);border-radius:0 3px 3px 0;
    animation:navTargetLoad 1.1s ease-in-out infinite
}
.nav-it.--loading-latest .nav-ic svg,
.sb-dash.--loading-latest .nav-ic svg{animation:navPulse .55s ease-in-out infinite}
@keyframes navTargetLoad{
    0%,100%{transform:translateY(-50%) scaleY(.25);opacity:.5}
    50%    {transform:translateY(-50%) scaleY(1);   opacity:1}
}

/* ══════════════════════════════════════
   THEME CURTAIN
   Colors injected inline from JS

   Cortina full-screen para el cambio de tema. Usa offsets negativos contra
   los safe-areas para extender la cortina a TODO el área física de la
   pantalla (notification bar arriba, controles de navegación abajo, notch
   en los lados). Sin esto, en mobile la cortina se cortaba en los bordes
   del viewport "seguro" y se notaba un margen sin animar.
   ══════════════════════════════════════ */
.theme-curtain{
    position:fixed;
    top:calc(0px - var(--safe-top, 0px));
    right:calc(0px - var(--safe-right, 0px));
    bottom:calc(0px - var(--safe-bottom, 0px));
    left:calc(0px - var(--safe-left, 0px));
    z-index:10001;
    pointer-events:none;overflow:hidden
}
.tc-layer{
    position:absolute;inset:0;
    transform:translateY(200%);
    will-change:transform;
    transition:none
}
.tc-1{z-index:3}
.tc-2{z-index:2}
.tc-3{z-index:1}

/* ══════════════════════════════════════
   PAGE ENTRANCE — tsE (5 layers)
   ALL layers rise upward to reveal.
   Front layer (z:5) rises first, exposing
   the orange accent (z:4), then progressively
   lighter darks, until the page is revealed.
   ══════════════════════════════════════ */
/* Page entrance — full-screen, mismo tratamiento de safe-areas que la
   theme-curtain: las capas se extienden al área física completa. */
.ts{
    position:fixed;
    top:calc(0px - var(--safe-top, 0px));
    right:calc(0px - var(--safe-right, 0px));
    bottom:calc(0px - var(--safe-bottom, 0px));
    left:calc(0px - var(--safe-left, 0px));
    z-index:10000;pointer-events:none
}
.ts.--ent{pointer-events:all}
.tl{position:absolute;inset:0;will-change:transform}

/* z-index por capa (z:5 es la que visualmente domina al cubrir). */
.tl.--e1{z-index:5}
.tl.--e2{z-index:4;opacity:.92}
.tl.--e3{z-index:3}
.tl.--e4{z-index:2}
.tl.--e5{z-index:1}

/* DARK → capa dominante (e1) oscura; flash de acento como capa intermedia.
   Color "naranja" derivado del acento del usuario via color-mix. */
[data-theme="dark"] .tl.--e1{background:linear-gradient(160deg,#0f1117 0%,#0a0d14 100%)}
[data-theme="dark"] .tl.--e2{
    background:linear-gradient(135deg,
        color-mix(in srgb, var(--accent) 92%, #fff 8%),
        color-mix(in srgb, var(--accent) 70%, #000 30%));
}
[data-theme="dark"] .tl.--e3{background:#131620}
[data-theme="dark"] .tl.--e4{background:#17192a}
[data-theme="dark"] .tl.--e5{background:linear-gradient(200deg,#0f1117,#17192a)}

/* LIGHT → capa dominante (e1) acento; cream en capas intermedias. */
[data-theme="light"] .tl.--e1{
    background:linear-gradient(135deg,
        color-mix(in srgb, var(--accent) 88%, #fff 12%) 0%,
        var(--accent) 60%,
        color-mix(in srgb, var(--accent) 70%, #000 30%) 100%);
}
[data-theme="light"] .tl.--e2{background:#faf8f5}
[data-theme="light"] .tl.--e3{background:#f7f4f0}
[data-theme="light"] .tl.--e4{background:#ece9e4}
[data-theme="light"] .tl.--e5{background:linear-gradient(200deg,#f3f0ec,#ece9e4)}

/* Front layer (z:5) rises first → back layer (z:1) rises last */
.ts.--ent.--go .tl.--e1{animation:tsRise .86s var(--ease-expo) .05s forwards}
.ts.--ent.--go .tl.--e2{animation:tsRise .86s var(--ease-expo) .14s forwards}
.ts.--ent.--go .tl.--e3{animation:tsRise .88s var(--ease-expo) .24s forwards}
.ts.--ent.--go .tl.--e4{animation:tsRise .88s var(--ease-expo) .34s forwards}
.ts.--ent.--go .tl.--e5{animation:tsRise .90s var(--ease-expo) .44s forwards}

@keyframes tsRise{to{transform:translateY(-105%)}}

/* ══════════════════════════════════════
   PAGE EXIT — tsX (5 layers, logout)
   ALL layers cascade DOWN from above.
   Back layer (z:1) falls first, accent
   orange (z:4) penultimate, solid dark
   (z:5) falls last → screen fully covered.
   Mirror of entrance: entrance starts from
   this covered state and rises.
   ══════════════════════════════════════ */
/* z-index + posición inicial (arriba del viewport). Colores se asignan por tema abajo. */
.tl.--x1{z-index:1;transform:translateY(-105%)}
.tl.--x2{z-index:2;transform:translateY(-105%)}
.tl.--x3{z-index:3;transform:translateY(-105%)}
.tl.--x4{z-index:4;transform:translateY(-105%);opacity:.96}
.tl.--x5{z-index:5;transform:translateY(-105%)}

/* DARK → capa dominante (x5) oscura; flash de acento en x4. */
[data-theme="dark"] .tl.--x1{background:#17192a}
[data-theme="dark"] .tl.--x2{background:#131620}
[data-theme="dark"] .tl.--x3{background:#0f1117}
[data-theme="dark"] .tl.--x4{
    background:linear-gradient(135deg,
        color-mix(in srgb, var(--accent) 92%, #fff 8%),
        color-mix(in srgb, var(--accent) 65%, #000 35%));
}
[data-theme="dark"] .tl.--x5{background:linear-gradient(160deg,#0f1117,#0a0d14)}

/* LIGHT → capa dominante (x5) acento; cream en las demás. */
[data-theme="light"] .tl.--x1{background:#faf8f5}
[data-theme="light"] .tl.--x2{background:#f7f4f0}
[data-theme="light"] .tl.--x3{background:#f3f0ec}
[data-theme="light"] .tl.--x4{background:#ece9e4}
[data-theme="light"] .tl.--x5{
    background:linear-gradient(135deg,
        color-mix(in srgb, var(--accent) 88%, #fff 12%) 0%,
        var(--accent) 60%,
        color-mix(in srgb, var(--accent) 70%, #000 30%) 100%);
}

/* Back layer (z:1) falls first → front layer (z:5) falls last */
.ts.--xit .tl.--x1{animation:tsFall .60s var(--ease-expo) 0s    forwards}
.ts.--xit .tl.--x2{animation:tsFall .60s var(--ease-expo) .08s  forwards}
.ts.--xit .tl.--x3{animation:tsFall .62s var(--ease-expo) .15s  forwards}
.ts.--xit .tl.--x4{animation:tsFall .60s var(--ease-expo) .22s  forwards}
.ts.--xit .tl.--x5{animation:tsFall .62s var(--ease-expo) .28s  forwards}

@keyframes tsFall{to{transform:translateY(0)}}
