/* ============================================================
   BUTTON LOADING STATE — admin and public

   A button that is already sending its form spins instead of
   accepting a second click, so nothing is submitted twice.

   Shared by both layouts: one implementation, identical everywhere.
   ============================================================ */

.is-loading {
    position: relative;
    pointer-events: none;
}

/*
   The label is wrapped in a span by the script and hidden rather than
   recoloured, for two reasons: the spinner needs the button's own colour, and
   hiding keeps the button exactly the size it was. The wrapper keeps the
   button's inner layout while it is visible.
*/
.is-loading > .btn-label {
    display: inline-flex;
    align-items: center;
    gap: inherit;
    visibility: hidden;
}

.is-loading::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 1.05em;
    height: 1.05em;
    margin: -0.525em 0 0 -0.525em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: btnSpin 0.65s linear infinite;
}

@keyframes btnSpin {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    /* A spin is exactly what this setting asks us to avoid, so the state is
       shown as a pulse instead. */
    .is-loading::after {
        animation: btnPulse 1.1s ease-in-out infinite;
        border-top-color: currentColor;
    }
}

@keyframes btnPulse {
    0%,
    100% {
        opacity: 0.3;
    }
    50% {
        opacity: 1;
    }
}
