/* SLASHED — core/motion.css
   @layer slashed.motion
   Global transitions, view transitions, keyframes, and motion helpers.
   Respects prefers-reduced-motion and --sf-motion-scale. */

@layer slashed.motion {

  @media (prefers-reduced-motion: no-preference) {

    html {
      scroll-behavior: smooth;
    }

    a, button, input, select, textarea, summary {
      transition-property:
        color, background-color, border-color, text-decoration-color,
        fill, stroke, opacity, box-shadow, transform, scale, translate,
        filter, backdrop-filter;
      transition-duration:        var(--sf-duration-fast);
      transition-timing-function: var(--sf-ease-out);
    }

    @supports (view-transition-name: none) {
      ::view-transition-old(root),
      ::view-transition-new(root) {
        animation-duration:        var(--sf-duration-normal);
        animation-timing-function: var(--sf-ease-out);
      }
    }

    
    .sf-fade-in        { animation: var(--sf-animation-fade-in); }
    .sf-fade-out       { animation: var(--sf-animation-fade-out); }
    .sf-slide-in-up    { animation: var(--sf-animation-slide-in-up); }
    .sf-slide-in-down  { animation: var(--sf-animation-slide-in-down); }
    .sf-slide-in-left  { animation: var(--sf-animation-slide-in-left); }
    .sf-slide-in-right { animation: var(--sf-animation-slide-in-right); }
    .sf-scale-up       { animation: var(--sf-animation-scale-up); }
    .sf-scale-down     { animation: var(--sf-animation-scale-down); }

    
    /* animation-duration set unconditionally: without it, engines without
       animation-timeline snap to end state at 0s. */
    .sf-entrance--fade,
    .sf-entrance--fade-up,
    .sf-entrance--fade-down,
    .sf-entrance--fade-left,
    .sf-entrance--fade-right,
    .sf-entrance--scale-up {
      animation-duration:        var(--sf-duration-slow);
      animation-timing-function: var(--sf-ease-out);
      animation-fill-mode:       both;
    }
    @supports (animation-timeline: view()) {
      .sf-entrance--fade,
      .sf-entrance--fade-up,
      .sf-entrance--fade-down,
      .sf-entrance--fade-left,
      .sf-entrance--fade-right,
      .sf-entrance--scale-up {
        animation-fill-mode: forwards;
        animation-timeline: view();
        animation-range:    var(--sf-scroll-timeline-range-start, entry 0%)
                            var(--sf-scroll-timeline-range-end,   cover 30%);
      }
    }
    .sf-entrance--fade        { animation-name: sf-fade-in; }
    .sf-entrance--fade-up     { animation-name: sf-slide-in-up; }
    .sf-entrance--fade-down   { animation-name: sf-slide-in-down; }
    .sf-entrance--fade-left   { animation-name: sf-slide-in-left; }
    .sf-entrance--fade-right  { animation-name: sf-slide-in-right; }
    .sf-entrance--scale-up    { animation-name: sf-scale-up; }


    /* Exit counterpart of .sf-entrance--*: fires as the element scrolls
       out of view. Every declaration below lives inside @supports —
       unlike entrance (where a non-scroll fallback still ends visible),
       an unconditional exit animation would leave content permanently
       hidden on engines without animation-timeline: view() support, so
       there is deliberately no fallback declaration outside @supports. */
    @supports (animation-timeline: view()) {
      .sf-exit--fade,
      .sf-exit--fade-up,
      .sf-exit--fade-down,
      .sf-exit--fade-left,
      .sf-exit--fade-right,
      .sf-exit--scale-down {
        animation-duration:        var(--sf-duration-slow);
        animation-timing-function: var(--sf-ease-in);
        animation-fill-mode:       both;
        animation-timeline: view();
        animation-range:    var(--sf-scroll-timeline-range-exit-start, cover 70%)
                            var(--sf-scroll-timeline-range-exit-end,   exit 100%);
      }
      .sf-exit--fade        { animation-name: sf-fade-out; }
      .sf-exit--fade-up     { animation-name: sf-slide-out-up; }
      .sf-exit--fade-down   { animation-name: sf-slide-out-down; }
      .sf-exit--fade-left   { animation-name: sf-slide-out-left; }
      .sf-exit--fade-right  { animation-name: sf-slide-out-right; }
      .sf-exit--scale-down  { animation-name: sf-scale-down; }
    }

    /* Stagger — choreography, not the animation itself. Put .sf-stagger on a
       PARENT; every direct child gets an incrementing animation-delay so a
       time-based entrance (.sf-fade-in / .sf-slide-in-*) plays in sequence.
       Children WITHOUT an animation just carry an inert delay (no-op), so you
       opt in per child with no need to exclude anything. Pairs with time-based
       animations only — the .sf-entrance / .sf-exit families are scroll-driven
       (their rhythm is animation-range, not animation-delay).

       One formula, one knob (--sf-stagger-step); the two paths below only
       differ in how --sf-stagger-i (the child's index) is set. */
    .sf-stagger > * {
      animation-delay: calc(
        var(--sf-stagger-i, 0) * var(--sf-stagger-step, 75ms) * var(--sf-motion-scale, 1)
      );
    }

    /* Modern path: real, unbounded index straight from the DOM position. */
    @supports (animation-delay: calc(sibling-index() * 1ms)) {
      .sf-stagger > * { --sf-stagger-i: calc(sibling-index() - 1); }
    }

    /* Fallback (no sibling-index()): a bounded :nth-child ramp reusing the same
       formula. The default is the plateau value, so the 9th child onward shares
       the last rung's delay instead of growing without limit — 8 rungs cover a
       4-column grid's first two visible rows. Split into @supports not(...) so
       the modern override above wins cleanly without a specificity fight
       (:nth-child would otherwise out-specify the sibling-index rule). */
    @supports not (animation-delay: calc(sibling-index() * 1ms)) {
      .sf-stagger > *              { --sf-stagger-i: 8; }
      .sf-stagger > *:nth-child(1) { --sf-stagger-i: 0; }
      .sf-stagger > *:nth-child(2) { --sf-stagger-i: 1; }
      .sf-stagger > *:nth-child(3) { --sf-stagger-i: 2; }
      .sf-stagger > *:nth-child(4) { --sf-stagger-i: 3; }
      .sf-stagger > *:nth-child(5) { --sf-stagger-i: 4; }
      .sf-stagger > *:nth-child(6) { --sf-stagger-i: 5; }
      .sf-stagger > *:nth-child(7) { --sf-stagger-i: 6; }
      .sf-stagger > *:nth-child(8) { --sf-stagger-i: 7; }
    }

  }

  @keyframes sf-fade-in   { from { opacity: 0; } to { opacity: 1; } }
  @keyframes sf-fade-out  { from { opacity: 1; } to { opacity: 0; } }
  @keyframes sf-slide-in-up    { from { opacity: 0; translate: 0 1rem;  } to { opacity: 1; translate: 0 0; } }
  @keyframes sf-slide-in-down  { from { opacity: 0; translate: 0 -1rem; } to { opacity: 1; translate: 0 0; } }
  @keyframes sf-slide-in-left  { from { opacity: 0; translate: 1rem 0;  } to { opacity: 1; translate: 0 0; } }
  @keyframes sf-slide-in-right { from { opacity: 0; translate: -1rem 0; } to { opacity: 1; translate: 0 0; } }
  @keyframes sf-scale-up   { from { opacity: 0; scale: 0.92; } to { opacity: 1; scale: 1; } }
  @keyframes sf-scale-down { from { opacity: 1; scale: 1; } to { opacity: 0; scale: 0.92; } }

  @keyframes sf-slide-out-up    { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 0 -1rem; } }
  @keyframes sf-slide-out-down  { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 0 1rem;  } }
  @keyframes sf-slide-out-left  { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: -1rem 0; } }
  @keyframes sf-slide-out-right { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 1rem 0;  } }

  @keyframes sf-spin    { to { rotate: 360deg; } }
  @keyframes sf-shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } }

  @keyframes sf-ping  { 75%, 100% { scale: 2; opacity: 0; } }
  @keyframes sf-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }
  @keyframes sf-float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -0.5rem; } }

  
  @supports (@property --x { syntax: "<color>"; inherits: false; initial-value: white; }) {
    @keyframes sf-color-pulse {
      0%   { --sf-color-primary-source-light: var(--sf-color-primary-source-light); }
      50%  { --sf-color-primary-source-light: oklch(from var(--sf-color-primary-source-light) clamp(0, calc(l + 0.25), 0.9) c h); }
      100% { --sf-color-primary-source-light: var(--sf-color-primary-source-light); }
    }

    @media (prefers-reduced-motion: no-preference) {
      .sf-color-pulse {
        animation: var(--sf-animation-color-pulse);
      }
    }
  }

  @supports (transition-behavior: allow-discrete) {
    dialog, [popover] {
      transition: var(--sf-transition-overlay);
    }
  }

}
