.page-transition-shell {
    /* Sem transform/will-change no estado de repouso: qualquer transform
       num antecessor cria um novo bloco de contenção para descendentes com
       position: fixed — e é exatamente isso que as modais do Bootstrap
       usam para se centrarem no ecrã. Por isso o transform só existe
       durante a animação (@keyframes), nunca em repouso.

       Só entrada, sem atraso a sair: a página atual mantém-se visível até
       o browser navegar mesmo (sem tempo extra em branco); a página nova
       recebe um fade rápido e discreto assim que carrega. */
    animation: pageTransitionEnter 180ms ease-out;
}

@keyframes pageTransitionEnter {
    from {
        opacity: 0;
        transform: translateY(4px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .page-transition-shell {
        animation: none;
    }
}

/* Outgoing counterpart, toggled by JS (animatePageLeave) right before a
   full-page form submit/navigation - same shared #pageTransitionShell
   element, same central duration/easing tokens (app.tailwind.css) as the
   entry animation above, so the two halves of the transition stay in sync
   without either page re-declaring its own timing. */
.page-transition-shell.page-leave {
    animation: pageTransitionLeave var(--app-duration-fast, 0.2s) var(--app-ease-standard, ease-in) both;
}

@keyframes pageTransitionLeave {
    from {
        opacity: 1;
        transform: translateY(0);
    }

    to {
        opacity: 0;
        transform: translateY(-4px);
    }
}

@media (prefers-reduced-motion: reduce) {
    .page-transition-shell.page-leave {
        animation: none;
    }
}
