/* Local controls only: route entrance belongs to motion-routes.css. */
.banner,.alert,.empty{animation:controls-feedback-in .22s var(--stage-ease)}
.swap,.shell-main .swap{animation:controls-feedback-in .28s var(--stage-ease)}
@keyframes controls-feedback-in{from{opacity:0}to{opacity:1}}
/* Keep the accessible label and the intrinsic small-button width while busy. */
.btn{position:relative}
.btn-label{display:inline-flex;align-items:center;justify-content:center;gap:inherit;max-width:100%;opacity:1;transition:opacity .18s var(--stage-ease)}
.btn-busy{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;opacity:0;transition:opacity .18s var(--stage-ease)}
.btn-busy .spinner{animation-play-state:paused}
.btn[aria-busy=true] .btn-label{opacity:0}
.btn[aria-busy=true] .btn-busy{opacity:1}
.btn[aria-busy=true] .spinner{animation-play-state:running}
.btn:disabled:active{transform:none}
/* Keep the existing universal reduced-motion override authoritative. */
/* Preserve the settled sign-in anchor; async error/config/mode height must not recenter it. */
.login-body{justify-content:flex-start;padding-block-start:max(24px,calc((100dvh - 616px)/2));padding-block-end:24px}
/* A toast already owns its one fade; its alert must not fade a second time. */
.stage-toast .banner,.stage-toast .alert{animation:none}
