@layer components {
  /* A three-dot loader shown on a .btn while its form submits — Turbo sets [aria-busy] on the
     form and disables the button. Wrap the button's label in an element so it hides cleanly. */
  @keyframes submitting {
    0%    { -webkit-mask-position: 0% 0%,   50% 0%,   100% 0%;   mask-position: 0% 0%,   50% 0%,   100% 0% }
    12.5% { -webkit-mask-position: 0% 50%,  50% 0%,   100% 0%;   mask-position: 0% 50%,  50% 0%,   100% 0% }
    25%   { -webkit-mask-position: 0% 100%, 50% 50%,  100% 0%;   mask-position: 0% 100%, 50% 50%,  100% 0% }
    37.5% { -webkit-mask-position: 0% 100%, 50% 100%, 100% 50%;  mask-position: 0% 100%, 50% 100%, 100% 50% }
    50%   { -webkit-mask-position: 0% 100%, 50% 100%, 100% 100%; mask-position: 0% 100%, 50% 100%, 100% 100% }
    62.5% { -webkit-mask-position: 0% 50%,  50% 100%, 100% 100%; mask-position: 0% 50%,  50% 100%, 100% 100% }
    75%   { -webkit-mask-position: 0% 0%,   50% 50%,  100% 100%; mask-position: 0% 0%,   50% 50%,  100% 100% }
    87.5% { -webkit-mask-position: 0% 0%,   50% 0%,   100% 50%;  mask-position: 0% 0%,   50% 0%,   100% 50% }
    100%  { -webkit-mask-position: 0% 0%,   50% 0%,   100% 0%;   mask-position: 0% 0%,   50% 0%,   100% 0% }
  }

  form[aria-busy] .btn:disabled { position: relative; }
  form[aria-busy] .btn:disabled > * { visibility: hidden; }
  form[aria-busy] .btn:disabled::after {
    --mask: no-repeat radial-gradient(#000 68%, #0000 71%);
    --size: 1.25em;
    content: "";
    position: absolute;
    inset: 50%;
    inline-size: var(--size);
    aspect-ratio: 8 / 5;
    margin-block: calc(var(--size) / -3);
    margin-inline: calc(var(--size) / -2);
    background: currentColor;
    -webkit-mask: var(--mask), var(--mask), var(--mask);
    -webkit-mask-size: 28% 45%;
    mask: var(--mask), var(--mask), var(--mask);
    mask-size: 28% 45%;
    animation: submitting 1s infinite linear;
  }
}
