# motion-system Specification

## Requirements

### Requirement: Motion library initialization

The system MUST bundle GSAP and ScrollTrigger via npm and initialize them from `resources/js/` on
every page using the public layout. A single motion module MUST own initialization and MUST be safe
to run more than once.

#### Scenario: Motion initializes on public pages

- GIVEN a visitor loads a public page with JavaScript enabled
- WHEN the motion module runs
- THEN GSAP and ScrollTrigger are registered and animations are attached

#### Scenario: Initialization is idempotent

- GIVEN the motion module has already initialized
- WHEN it runs again
- THEN it does not duplicate timelines or ScrollTrigger instances

### Requirement: Scroll-driven animations

The system MUST provide a staggered hero entrance timeline, scroll-reveal for `[data-animate]`
elements, parallax on the hero background, staggered card reveals, animated numeric counters for
trust metrics, and micro-interactions on buttons, cards, and links.

#### Scenario: Hero entrance is staggered

- GIVEN the home page hero
- WHEN it enters the viewport on load
- THEN its elements animate in as a staggered timeline

#### Scenario: Elements reveal on scroll

- GIVEN an element marked `[data-animate]` below the fold
- WHEN it enters the viewport
- THEN it reveals once and does not re-animate on subsequent scrolls

#### Scenario: Hero background parallaxes

- GIVEN the home hero with a background image
- WHEN the visitor scrolls
- THEN the background moves at a different rate than the foreground

#### Scenario: Counters animate to their target

- GIVEN a trust metric with a numeric value
- WHEN it enters the viewport
- THEN it counts up to the value and stops at the target

### Requirement: Reduced motion support

When `prefers-reduced-motion: reduce` is active, the system MUST disable or simplify animations and
MUST keep all content visible and usable without motion.

#### Scenario: Reduced motion disables animation

- GIVEN a visitor with `prefers-reduced-motion: reduce`
- WHEN a page with animations loads
- THEN entrance, reveal, parallax, and counter animations are disabled or reduced to a static state

#### Scenario: Content is visible without motion

- GIVEN reduced motion is active
- WHEN the page renders
- THEN all animated content is immediately visible and not hidden

### Requirement: Livewire lifecycle and cleanup

The system MUST re-initialize animations after Livewire DOM updates and MUST NOT leak ScrollTriggers.

#### Scenario: Animations re-initialize after Livewire update

- GIVEN a Livewire island such as `ProductCatalog` updates the DOM
- WHEN the update completes
- THEN new `[data-animate]` elements receive their animations

#### Scenario: ScrollTriggers are cleaned up

- GIVEN a Livewire island or page section is removed or replaced
- WHEN it unmounts
- THEN its ScrollTrigger instances are killed and not retained

### Requirement: Motion performance

Animations MUST animate only `transform` and `opacity`, MUST NOT cause layout thrash, and MUST remain
smooth on mobile.

#### Scenario: Only compositor-friendly properties animate

- GIVEN any animation in the motion system
- WHEN it runs
- THEN it changes only `transform` and/or `opacity`

#### Scenario: No layout thrash or overflow on mobile

- GIVEN a page with animations on a mobile viewport
- WHEN animations run
- THEN no horizontal overflow occurs and no layout reflow is triggered per frame

### Requirement: Graceful degradation

If JavaScript fails, is blocked, or is disabled, all content MUST remain visible and usable.

#### Scenario: Content is visible without JavaScript

- GIVEN a visitor with JavaScript disabled
- WHEN a public page loads
- THEN all content is visible and interactive elements remain usable

#### Scenario: A JS error does not hide content

- GIVEN the motion module throws at runtime
- WHEN the page renders
- THEN no content is hidden or left in a pre-animation hidden state
