/* =============================================================================
   Interaction motion — the app-wide response layer.
   =============================================================================

   Sits alongside core-animations.css (the keyframe library) and
   table-animations.css (which applies those keyframes to rows). Where those two
   animate things that ENTER or LEAVE, this file animates what happens while a
   surface is being used: hovered, focused, pressed, opened.

   Why it exists: motion.css already gave the app a native-feeling page push, but
   it stops at the page boundary. Below it, an interaction either changed state
   instantly or changed it over the 300ms theme wash (see below) — dead or
   sluggish, never responsive. This layer is the missing middle.

   REACH: it deliberately targets Bootstrap's own class names rather than bespoke
   ones, because those are what the templates already use — .card appears in 246
   templates, .badge in 141, .form-control in 112. That is the whole design: one
   stylesheet, no markup to add, and every screen picks it up at once.

   CONFIGURING IT: there is nothing to tune in this file. Every duration, easing
   and geometry value is a role read from tokens/semantic.css, so retuning the
   app's entire tactile feel is one edit there, not a sweep through here. A
   literal in this file is a bug — tests/unit/test_design_token_ratchet.py fails
   on an unnamed curve, and the same rule applies by convention to the rest.

   OPTING OUT: [data-no-motion] on an element or any ancestor stops all of it.

   ========================================================================== */

/* ── The response-speed correction ─────────────────────────────────────────
   themes/base.css applies a global `* { transition: background-color, color,
   border-color var(--dur-theme-swap) }`. That rule is correct for what it was
   written for — swapping theme should read as a slow wash — but `*` does not
   distinguish a theme swap from a hover, so EVERY colour response in the app
   was also taking 300ms on a plain `ease`. That is the real reason interactions
   felt unresponsive: not missing motion, but motion tuned for the wrong event.

   Interactive elements are pulled back to the interaction scale here. box-shadow
   joins the list because Bootstrap's focus ring is a shadow, and the `*` rule
   never covered it — focus was snapping in with no transition at all.

   Specificity, not load order, is what makes this win: these selectors are
   (0,1,0) against the `*` rule's (0,0,0), so it holds wherever this file loads.

   The trade: a theme swap now moves these elements at --dur-fast while the rest
   of the page washes at --dur-theme-swap. Accepted deliberately — the mismatch
   is invisible on a once-a-session theme change, whereas the 300ms lag was felt
   on every hover, focus and tap. */
a,
.btn,
.card,
.stat-card,
.action-tile,
.list-group-item,
.nav-link,
.dropdown-item,
.form-control,
.form-select,
.form-check-input,
.badge,
.alert {
    transition: background-color var(--dur-fast) var(--ease-standard),
                border-color var(--dur-fast) var(--ease-standard),
                color var(--dur-fast) var(--ease-standard),
                box-shadow var(--dur-fast) var(--ease-standard),
                transform var(--dur-fast) var(--ease-standard);
}

/* ── Press ─────────────────────────────────────────────────────────────────
   Compression under a finger is the single strongest "this is native" cue, and
   the app already proves the pattern: motion.css compresses list rows via
   `tbody[id] > tr:active`. This extends the same gesture to the other things a
   thumb lands on, at the same scale token, so a row and a tile answer alike.

   Scoped to genuinely interactive elements ONLY — a.card and
   .list-group-item-action, never a bare .card. A .card is a layout box in most
   of those 246 templates; compressing one that does nothing when tapped would
   promise an action that never arrives, which reads as a broken control rather
   than a responsive one. [data-pressable] is the opt-in for anything this list
   misses, e.g. a div wired up by a Stimulus controller.

   Coarse pointers only. On a mouse there is no sustained press to speak of, and
   the hover lift below does that job instead. */
@media (hover: none) and (pointer: coarse) {
    .btn:active,
    a.card:active,
    .card[data-pressable]:active,
    .stat-card:active,
    .action-tile:active,
    .list-group-item-action:active,
    [role="button"]:active,
    [data-pressable]:active {
        transform: scale(var(--press-scale));
    }
}

/* ── Hover ─────────────────────────────────────────────────────────────────
   The mouse equivalent: the surface rises slightly toward the pointer. Same
   selector discipline as press — only things that actually do something.
   --ease-decel because it is an arrival: quick away from rest, soft into place. */
@media (hover: hover) and (pointer: fine) {
    a.card:hover,
    .card[data-pressable]:hover,
    .stat-card:hover,
    .action-tile:hover,
    .list-group-item-action:hover {
        transform: translateY(var(--hover-lift));
        transition-timing-function: var(--ease-decel);
    }
}

/* ── Menus ─────────────────────────────────────────────────────────────────
   Bootstrap toggles .show with no transition, so a dropdown appears as a hard
   cut. OPACITY ONLY, deliberately: Popper positions these with an inline
   `transform: translate3d(...)`, and an inline style beats a stylesheet rule —
   animating transform here would either be silently ignored or fight Popper for
   the element's position. Fading is the part that is safe to own. */
.dropdown-menu.show {
    animation: fade-in var(--dur-fast) var(--ease-standard) both;
}

/* ── Flash messages ────────────────────────────────────────────────────────
   An alert is nearly always the result of something the user just did — a save,
   a validation failure. Arriving under its own motion marks it as new; a bare
   repaint leaves it looking like it was always on the page.

   .alert carries no `both` fill: it must end at its natural state so that a
   later theme change or dismissal is not fighting a retained keyframe. */
.alert {
    animation: rise-in var(--dur-base) var(--ease-decel);
}

/* Keyframes owned here rather than in core-animations.css because they exist
   only to serve the rules above. core-animations.css already carries six
   overlapping entrance keyframes (fadeIn, fadeInUp, fadeInScale, rise,
   rowFadeIn, bounceIn) — adding a seventh name to that pile would make the
   duplication worse. Consolidating those six is its own piece of work. */
@keyframes fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes rise-in {
    from {
        opacity: 0;
        /* Tied to the press geometry so the distance a thing travels and the
           distance a thing compresses stay one decision. */
        transform: translateY(calc(var(--hover-lift) * -3));
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* ── Opt-out ───────────────────────────────────────────────────────────────
   One escape hatch for both the author and the user.

   tokens/semantic.css already zeroes every --dur-* under prefers-reduced-motion,
   which silences the transitions above on its own. The animations and the
   transforms need explicit handling: a running @keyframes keeps the duration it
   started with, and a :hover/:active transform with a zeroed duration still
   JUMPS to its end state rather than easing there. Reduced motion has to mean
   "does not move", not "moves instantly". */
[data-no-motion],
[data-no-motion] * {
    animation: none !important;
    transition: none !important;
}

@media (prefers-reduced-motion: reduce) {
    .dropdown-menu.show,
    .alert {
        animation: none;
    }

    .btn:active,
    a.card:active,
    .card[data-pressable]:active,
    .stat-card:active,
    .action-tile:active,
    .list-group-item-action:active,
    [role="button"]:active,
    [data-pressable]:active,
    a.card:hover,
    .card[data-pressable]:hover,
    .stat-card:hover,
    .action-tile:hover,
    .list-group-item-action:hover {
        transform: none;
    }
}
