/* Optional motion for the server-rendered onboarding walk.
 *
 * Navigation remains an ordinary anchor or form submission. Browsers that
 * support cross-document View Transitions add one quiet handoff; every other
 * browser ignores these rules and follows the same Django response directly.
 */

/* Kept, and only defensible now that it is paired. A cross-document view
 * transition holds the outgoing document painted while the browser waits for
 * the next one, which on a slow connection is precisely the frozen screen this
 * layer is supposed to prevent -- unless something on that held screen is
 * moving. The pending state below is that something: it starts before the hold
 * and animates through it, so the page the person is looking at during the
 * wait is one that is visibly working. Remove one and the other stops being
 * worth having. */
@view-transition {
  navigation: auto;
}

[data-motion-surface="screen"] {
  view-transition-name: onboarding-screen;
}

.enroll-progress {
  view-transition-name: onboarding-progress;
}

::view-transition-old(root),
::view-transition-old(onboarding-screen) {
  animation: onboarding-screen-exit var(--motion-fast) var(--ease-exit) both;
}

::view-transition-new(root),
::view-transition-new(onboarding-screen) {
  animation: onboarding-screen-enter var(--motion-standard) var(--ease-standard) both;
}

@keyframes onboarding-screen-exit {
  to {
    opacity: 0;
  }
}

@keyframes onboarding-screen-enter {
  from {
    opacity: 0;
    transform: translateY(8px);
  }

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

.enroll-progress__fill {
  transition: width 200ms var(--ease-standard);
}

.enroll-button,
.enroll-textlink,
.enroll-option,
.enroll-chip,
.enroll-multi-choice,
.enroll-slot,
.enroll-consent__input {
  transition:
    transform var(--motion-fast) var(--ease-standard),
    opacity var(--motion-fast) var(--ease-standard),
    background-color var(--motion-fast) var(--ease-standard),
    border-color var(--motion-fast) var(--ease-standard),
    box-shadow var(--motion-fast) var(--ease-standard),
    color var(--motion-fast) var(--ease-standard);
}

.enroll-tier,
.enroll-field__input {
  transition:
    transform var(--motion-standard) var(--ease-standard),
    background-color var(--motion-fast) var(--ease-standard),
    border-color var(--motion-fast) var(--ease-standard),
    box-shadow var(--motion-standard) var(--ease-standard),
    color var(--motion-fast) var(--ease-standard);
}

.enroll-consent__input::before {
  transition: transform var(--motion-fast) var(--ease-standard);
}

:where(.enroll-button, .enroll-textlink, .enroll-option, .enroll-chip, .enroll-multi-choice, .enroll-slot):active {
  transform: translateY(1px) scale(0.99);
}

[data-auto-advance][aria-busy="true"] :where(.enroll-option, .enroll-tier__cta) {
  pointer-events: none;
}

/* The one unbounded animation in the walk, and the exception is the reason the
 * budget exists rather than a hole in it: this runs only while a navigation is
 * in flight and dies with the document that started it. It is deliberately not
 * a progress bar -- the server reports no progress, and a bar that invents some
 * is a worse lie on a slow connection than an honest "still working". */
@keyframes enroll-pending-spin {
  from {
    transform: rotate(0turn);
  }

  to {
    transform: rotate(1turn);
  }
}

.enroll-button[data-state="pending"]::after,
.enroll-textlink[data-state="pending"]::after,
.enroll-option[data-state="pending"]::after {
  animation: enroll-pending-spin 700ms linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .enroll-progress__fill,
  .enroll-button,
  .enroll-textlink,
    .enroll-option,
    .enroll-chip,
    .enroll-multi-choice,
    .enroll-slot,
  .enroll-tier,
  .enroll-field__input,
  .enroll-consent__input,
  .enroll-consent__input::before {
    transition-duration: 0.01ms;
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
  }

  /* Written out rather than wrapped in `:where()`. The press rule this cancels
     carries `:active`, so a zero-specificity reset loses to it and the press
     still moves for somebody who asked for no movement -- a media query adds
     no specificity of its own. Matching `:active` here is what makes the
     cancellation actually apply. */
  .enroll-button:active,
  .enroll-textlink:active,
  .enroll-option:active,
  .enroll-chip:active,
  .enroll-multi-choice:active,
  .enroll-slot:active,
  .enroll-tier:active {
    transform: none;
  }

  /* The mark stays; only the turning goes. A ring closed on all four sides
     reads as a state rather than a stalled spinner, and the swapped label is
     what actually carries the message. */
  .enroll-button[data-state="pending"]::after,
  .enroll-textlink[data-state="pending"]::after,
  .enroll-option[data-state="pending"]::after {
    border-block-start-color: currentColor;
    animation: none;
  }

  ::view-transition-old(root),
  ::view-transition-new(root),
  ::view-transition-old(onboarding-screen),
  ::view-transition-new(onboarding-screen),
  ::view-transition-old(onboarding-progress),
  ::view-transition-new(onboarding-progress) {
    animation: none;
  }
}
