/* Shared motion vocabulary: the single owner of every decorative keyframe.
   Feature styles consume the composite `--sp-motion-*` tokens from
   design-tokens.css (or the utility classes below) and never redeclare an
   equivalent keyframe, duration or easing. Opacity and transform only; no
   persistent transforms on page or table containers. */

/* ---- Keyframes -------------------------------------------------------- */

@keyframes ui-motion-fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

@keyframes ui-motion-fade-out {
    from {
        opacity: 1;
    }
    to {
        opacity: 0;
    }
}

@keyframes ui-motion-enter {
    from {
        opacity: 0;
        transform: translateY(var(--sp-motion-distance-sm));
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes ui-motion-swap {
    from {
        opacity: 0;
        transform: translateY(0.15rem);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Menus, popovers, pickers and hints grow out of their trigger. */
@keyframes ui-motion-pop-in {
    from {
        opacity: 0;
        transform: translateY(-0.2rem) scale(0.985);
    }
    to {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

@keyframes ui-motion-pop-out {
    from {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
    to {
        opacity: 0;
        transform: translateY(-0.2rem) scale(0.985);
    }
}

@keyframes ui-motion-dialog-in {
    from {
        opacity: 0;
        transform: translateY(var(--sp-motion-distance-sm)) scale(0.985);
    }
    to {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

@keyframes ui-motion-dialog-out {
    from {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
    to {
        opacity: 0;
        transform: translateY(var(--sp-motion-distance-sm)) scale(0.985);
    }
}

@keyframes ui-motion-drawer-in {
    from {
        opacity: 0;
        transform: translateX(var(--sp-motion-distance-panel));
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes ui-motion-drawer-out {
    from {
        opacity: 1;
        transform: translateX(0);
    }
    to {
        opacity: 0;
        transform: translateX(var(--sp-motion-distance-panel));
    }
}

@keyframes ui-motion-scale-in {
    from {
        opacity: 0;
        transform: scale(0.85);
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes ui-motion-remove {
    from {
        opacity: 1;
        transform: scale(1);
    }
    to {
        opacity: 0;
        transform: scale(0.985);
    }
}

/* Draws attention to a just-created or just-focused item, then settles on the
   element's own background. Set --sp-motion-highlight-tint to change tone. */
@keyframes ui-motion-highlight {
    from {
        background-color: var(--sp-motion-highlight-tint, color-mix(in srgb, var(--sp-color-brand) 18%, transparent));
    }
}

@keyframes ui-motion-spin {
    to {
        transform: rotate(360deg);
    }
}

@keyframes ui-motion-shimmer {
    from {
        background-position: 100% 0;
    }

    to {
        background-position: 0 0;
    }
}

/* Indeterminate progress: the segment is 40% of its track, so it starts fully
   before the track and ends fully after it. */
@keyframes ui-motion-progress {
    from {
        transform: translateX(-100%);
    }
    to {
        transform: translateX(250%);
    }
}

/* ---- Utility classes -------------------------------------------------- */

/* First appearance of a stable workspace or card. */
.ui-motion-enter {
    animation: var(--sp-motion-rise-in);
}

/* Newly mounted sections enter in reading order. Existing nodes keep their
   position during filtering and sorting; only their changed values may fade. */
.ui-motion-cascade > * {
    animation: var(--sp-motion-rise-in);
}
.ui-motion-cascade > :nth-child(2) {
    animation-delay: var(--sp-motion-stagger-step);
}
.ui-motion-cascade > :nth-child(3) {
    animation-delay: calc(2 * var(--sp-motion-stagger-step));
}
.ui-motion-cascade > :nth-child(4) {
    animation-delay: calc(3 * var(--sp-motion-stagger-step));
}
.ui-motion-cascade > :nth-child(n + 5) {
    animation-delay: calc(4 * var(--sp-motion-stagger-step));
}

/* Compact conditional content: tab panels, expanded rows, inline editors. */
.ui-motion-swap {
    animation: var(--sp-motion-swap-in);
}

/* Opacity-only appearance for surfaces that must not move. */
.ui-motion-fade {
    animation: var(--sp-motion-fade-in);
}

.ui-motion-fade.is-exiting {
    pointer-events: none;
    animation: var(--sp-motion-fade-out);
}

/* Menus, pickers and popovers anchored to a trigger. */
.ui-motion-pop {
    animation: var(--sp-motion-pop-in);
}

.ui-motion-pop.is-exiting {
    pointer-events: none;
    animation: var(--sp-motion-pop-out);
}

/* Always-mounted surfaces whose visibility is toggled with `is-closed`. */
.ui-motion-reveal {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: var(--sp-motion-transition-reveal);
}

.ui-motion-reveal.is-closed {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-0.35rem) scale(0.98);
    transition: var(--sp-motion-transition-conceal);
}

/* Owned disclosures: toggle `is-open`; the single child clips while collapsed
   and collapsed content is hidden from focus and assistive technology. */
.ui-motion-collapse {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    visibility: hidden;
    transition: var(--sp-motion-transition-collapse);
}

.ui-motion-collapse > * {
    min-height: 0;
    overflow: hidden;
}

.ui-motion-collapse.is-open {
    grid-template-rows: 1fr;
    opacity: 1;
    visibility: visible;
    transition: var(--sp-motion-transition-expand);
}

/* Cards that navigate or open something rise slightly under a fine pointer.
   Only transform and shadow move, so neighbouring cards keep their place. */
.ui-motion-lift {
    transition: var(--sp-motion-transition-lift);
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .ui-motion-lift:hover,
    .ui-motion-lift:has(:focus-visible) {
        transform: translateY(-2px);
        box-shadow: var(--sp-shadow-md);
    }
}

/* ---- Shared coverage -------------------------------------------------- */

/* Loading placeholders and route progress wait for the loading delay before
   they paint; content that arrives sooner replaces them without a flash. */
.ui-skeleton {
    animation: var(--sp-motion-skeleton);
}

.ui-progress-indeterminate > .ui-progress-bar {
    animation: var(--sp-motion-progress);
}

.route-navigation-loading {
    animation: var(--sp-motion-loading-in);
}

/* Opacity only: fixed dialogs and sticky headers keep their containing block. */
.ui-motion-page,
.ui-tabs-panel,
.ui-segmented-panel:not([hidden]),
.ui-loading-region > :not(.ui-skeleton, .ui-skeleton-layout, .ui-loading-region-live, .ui-loading-region-progress),
.ui-empty-state,
.ui-alert,
.validation-message,
.validation-summary-errors,
.directory-expansion,
details[open] > :not(summary),
/* Expanded table rows fade their content; the row geometry never animates. */
tr[data-table-detail="true"] > td > * {
    animation: var(--sp-motion-fade-in);
}

.dropdown-menu.show,
[popover]:popover-open,
.ui-button-hint,
/* A <details> menu anchored to its summary pops instead of the plain details fade. */
details[open] > .ui-motion-pop {
    animation: var(--sp-motion-pop-in);
}

/* Expand chevrons turn with their button's aria-expanded state instead of flipping. */
.ui-button .ui-action-icon[data-icon="expand"] {
    transition: var(--sp-motion-transition-transform);
}

/* Baseline hover and focus feedback for every custom control. Zero specificity:
   a component's own transition always wins. */
:where(a[href], button, summary, [role="button"], [role="tab"], [role="option"], [role="menuitem"], .list-group-item) {
    transition: var(--sp-motion-transition-control);
}

/* Shared primitives and Bootstrap controls: outranks Bootstrap's own transitions. */
:is(
    .ui-motion-interactive,
    .btn,
    .ui-button,
    .btn-close,
    .form-control,
    .form-select,
    .form-check-input,
    .nav-link,
    .dropdown-item,
    .list-group-item-action,
    .page-link,
    .ui-tabs-tab,
    .ui-disclosure-toggle,
    .ui-file-upload-trigger
) {
    transition: var(--sp-motion-transition-control);
}

/* Press feedback moves by one pixel with `translate`, which never affects layout. */
@media (prefers-reduced-motion: no-preference) {
    :is(.ui-motion-interactive, .btn, .ui-button, .ui-tabs-tab, .list-group-item-action, .page-link):not(:disabled):not(
            .disabled
        ):not([aria-disabled="true"]):active {
        translate: 0 1px;
    }
}

/* Lasting fill and inherited component selectors must never defeat this setting.
   Keep a tiny duration so animation-end based cleanup continues to run. This is
   the single reduced-motion switch; feature styles do not repeat it. */
@media (prefers-reduced-motion: reduce), print {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-delay: 0ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        transition-delay: 0ms !important;
        scroll-behavior: auto !important;
    }
}
