/* Shared motion vocabulary. Components choose properties, never their own timing. */
:root {
  --motion-fast: 140ms;
  --motion-normal: 200ms;
  --motion-state: 280ms;
  --motion-exit: 160ms;
  --motion-ease: cubic-bezier(.2, .7, .2, 1);
  --motion-distance: 8px;
}
html { scrollbar-gutter: stable; }
html.ui-modal-open { overflow: hidden; }
html.ui-modal-open body { position: fixed; top: var(--ui-scroll-top); left: 0; right: 0; }
/* Focused controls use their surface/color, never a second frame around the UI. */
:where(button, a, input, textarea, select):focus { outline: none; }
button:focus-visible { filter: brightness(.9); }
a:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
.modal-card [tabindex="-1"]:focus { outline: none; }
[hidden] { display: none !important; }

/* Native dialog stays in the top layer while its exit transition finishes.
   Older browsers keep the same behavior with an immediate open / close. */
dialog.modal-card {
  opacity: 0;
  transform: translateY(var(--motion-distance));
  /* Visual viewport updates can arrive in one step when a keyboard opens. */
  transition: opacity var(--motion-exit) var(--motion-ease), transform var(--motion-exit) var(--motion-ease),
    top var(--motion-normal) var(--motion-ease), bottom var(--motion-normal) var(--motion-ease);
  transition: opacity var(--motion-exit) var(--motion-ease), transform var(--motion-exit) var(--motion-ease),
    top var(--motion-normal) var(--motion-ease), bottom var(--motion-normal) var(--motion-ease),
    display var(--motion-exit) allow-discrete, overlay var(--motion-exit) allow-discrete;
}
dialog.modal-card:not([open]) { pointer-events: none; }
dialog.modal-card[open] {
  opacity: 1;
  transform: translateY(0);
  transition-duration: var(--motion-normal);
}
dialog.modal-card::backdrop {
  opacity: 0;
  transition: opacity var(--motion-exit) var(--motion-ease),
    display var(--motion-exit) allow-discrete, overlay var(--motion-exit) allow-discrete;
}
dialog.modal-card[open]::backdrop { opacity: 1; }
@starting-style {
  dialog.modal-card[open] { opacity: 0; transform: translateY(var(--motion-distance)); }
  dialog.modal-card[open]::backdrop { opacity: 0; }
}

@keyframes ui-enter {
  from { opacity: 0; transform: translateY(var(--motion-distance)); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes ui-spin { to { transform: rotate(360deg); } }
@keyframes ui-progress { from { transform: translateX(-100%); } to { transform: translateX(350%); } }
@keyframes ui-skeleton { 50% { opacity: .5; } }

/* Animate newly revealed content, not every message or list row on refresh. */
:where(.support-notice:not([hidden]), .email-auth-status:not([hidden]), .payment-result:not([hidden]),
  .payment-amount-step:not([hidden]), .payment-method-step:not([hidden]),
  #emailAuthEmailStep:not([hidden]), #emailAuthCodeStep:not([hidden])) {
  animation: ui-enter var(--motion-normal) var(--motion-ease);
}
/* State changes immediately; display remains only for the visual exit. */
:where(.nav-menu, .dev-dropdown, .server-dd, .support-category__options) {
  opacity: 0;
  transform: translateY(var(--motion-distance));
  pointer-events: none;
  transition: opacity var(--motion-exit) var(--motion-ease), transform var(--motion-exit) var(--motion-ease);
  transition: opacity var(--motion-exit) var(--motion-ease), transform var(--motion-exit) var(--motion-ease),
    display var(--motion-exit) allow-discrete;
}
:where(.nav-menu-open, .dev-dropdown.open, .server-dd.open, .support-category__options:not([hidden])) {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
  transition-duration: var(--motion-normal);
}
@starting-style {
  :where(.nav-menu-open, .dev-dropdown.open, .server-dd.open, .support-category__options:not([hidden])) {
    opacity: 0;
    transform: translateY(var(--motion-distance));
  }
}
:where(.payment-presets button, .payment-method i, .admin-customer-tabs button,
  .support-category__button, .support-category__option, .support-ticket) {
  transition: background-color var(--motion-fast) var(--motion-ease),
    border-color var(--motion-fast) var(--motion-ease), color var(--motion-fast) var(--motion-ease);
}
.ui-load-error { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin: 12px 0; color: #b91c1c; font-size: .875rem; }
.ui-busy { cursor: progress; }
button.ui-busy { position: relative; }
.btn.ui-busy, .btn.ui-busy:disabled { color: transparent !important; }
.btn.ui-busy > * { opacity: 0; }
.btn.ui-busy::after {
  content: ""; position: absolute; left: calc(50% - 7px); top: calc(50% - 7px);
  width: 14px; height: 14px; border: 2px solid #fff; border-right-color: transparent;
  border-radius: 50%; animation: ui-spin .8s linear infinite;
}
.ui-skeleton { min-height: 92px; padding: 16px; list-style: none; }
.ui-skeleton::before, .ui-skeleton::after {
  content: ""; display: block; height: 14px; border-radius: 7px;
  background: rgba(148, 163, 184, .2); width: 65%; margin: 8px 0 16px;
  animation: ui-skeleton 1.4s var(--motion-ease) .16s infinite;
}
.ui-skeleton::after { width: 40%; }
.ui-skeleton--compact { min-height: 0; padding: 0; }
.ui-skeleton--compact::before, .ui-skeleton--compact::after {
  height: .65em; margin: .5em 0; width: 75%;
}
.ui-skeleton--compact::after { width: 50%; }
.ui-skeleton-value {
  position: relative; display: inline-block; min-width: 2em;
  color: transparent !important;
}
.ui-skeleton-value::after {
  content: ""; position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  width: 100%; height: .4em; border-radius: 999px; background: rgba(148, 163, 184, .2);
  animation: ui-skeleton 1.4s var(--motion-ease) .16s infinite;
}
.ui-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.ui-navigation-progress {
  position: fixed; inset: 0 0 auto; height: 3px; z-index: 10000;
  overflow: hidden; pointer-events: none;
}
.ui-navigation-progress::after {
  content: ""; display: block; height: 100%; width: 30%; background: #2563eb;
  animation: ui-progress 1.2s var(--motion-ease) infinite;
}

/* No page-wide fade: initial content and back/forward restoration remain immediate. */
@media (prefers-reduced-motion: reduce) {
  :root { --motion-fast: 0s; --motion-normal: 0s; --motion-state: 0s; --motion-exit: 0s; --motion-distance: 0px; }
  *, *::before, *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
  .ui-navigation-progress::after { width: 100%; }
}
