# Interaction Runtime Contract Use this contract after `interaction-map.json` and `scene-contract.json` identify one or more core scenes. ## Principle Implement the observed state machine, not the source library. A source may use GSAP, Framer Motion, Web Animations, CSS transitions, or custom JavaScript. The clone may use a different mechanism as long as its content, visual states, transition shape, responsive behavior, and interaction invariants match the evidence. Do not translate a scene contract to more descriptive prose. Start from the declared primitive, bind extracted content and media to it, and expose the QA markers below. ## Required markers Every core scene: ```html
...
``` Active-card rail: ```html
``` Content switcher: ```html
...
``` Scroll scene: ```html
...
...
...
``` Markers are implementation instrumentation. Do not copy source selectors into them. `site-clone.mjs` installs framework-source copies and browser-ready public copies. The default and safest startup is the exact contract bootstrap, included once by the page: ```html ``` Do not use an inline raw-browser import of `../lib/rp-interactions.mjs`; a browser URL is not resolved from `src/`. The bootstrap auto-binds complete marker contracts after DOM readiness. `data-rp-initialized` is runtime evidence, not a marker to hard-code in markup. Source-specific CSS and content still belong in the page/component implementation. ## Primitive rules ### `active-card-rail` - Render every captured repeated item in source order. - Keep a real horizontal track inside a separate scroll viewport; do not substitute a grid. - Put `overflow-x` and scrolling on `data-rp-viewport`; put `width: max-content` or the equivalent row sizing on `data-rp-track`. A max-content track cannot be its own viewport. - Use the exact captured initial active indexes. Set `data-rp-initial-state="all-collapsed"` when source has none; runtime initialization must not auto-open an item. - Reproduce the measured separation mechanism: track gap, geometric gap, item margin, divider, inner inset, or flush. - Preserve the observed expanded/collapsed width ratio within the contract tolerance. - Controls and direct item activation must update the same state. - Mark the source hover surface with `data-rp-hover-target` and reproduce its visual hover treatment with keyboard-equivalent focus styling. - Reproduce nested reveal order and approximate settle time from the captured timeline. ### `content-switcher` - Use visible controls with one current state. - Keep all captured state content in the implementation. - Change the visible `data-rp-panel` content and active marker on activation. - Preserve keyboard access and relevant ARIA state. - Give controls and panels matching `data-rp-state` values so `bindContentSwitcher` can manage state deterministically. ### `scroll-scene` - Derive progress from the scene's own scroll range. - Preserve entry/approach phases and internal pinned phases in their captured order. - Keep `data-rp-phase` synchronized with the visual state. - Expose continuous `--rp-entry-progress` and `--rp-scroll-progress` custom properties. - Mark only the measured pinned/cropped/scaled visual wrapper with `data-rp-visual`. - Mark independently positioned copy with `data-rp-content` and keep it outside visual scaling unless source content geometry explicitly scales. - Reproduce large sibling reveal layers from `sceneLayers`, including paired curtains or masks. Mark observed curtains with `data-rp-curtain` so reveal distance is verified even when the media itself stays full-width. - Do not flatten the scene into static columns when source evidence includes pinning. - Pass captured phase names/progress thresholds to `bindScrollScene`; combine it with `bindContentSwitcher` when tabs share the same scene. ### `sticky-header` and `media-scene` - Preserve the captured trigger threshold and material chrome changes. - Keep source-backed media interactive with the recorded URL and playback flags. - Treat a poster or background with `fallbackPolicy: "fallback-only"` as mutually exclusive with a successfully loaded video/iframe. Do not leave it as a visible layer behind media. ## Runtime QA Start the clone locally, then run: ```bash node scripts/verify-interactions.mjs \ --out projects/ \ --clone-url http://127.0.0.1:4321 \ --project-root ``` The command writes `docs/site-clone/interaction-qa.json` and exits non-zero if a core scene is absent, lacks its runtime initialization token, or violates its assertions. Fix failures before final visual polish.