# Phase 2 - Motion

> Phase 2 - Motion: a note from the Obsidian vault behind https://saifsiddiqui.in.

Canonical URL: https://saifsiddiqui.in/brain/phase-2-motion/ (this is its markdown representation; the same URL serves HTML to browsers)
Other languages: hi: https://saifsiddiqui.in/hi/brain/phase-2-motion/ · kn: https://saifsiddiqui.in/kn/brain/phase-2-motion/ · ur: https://saifsiddiqui.in/ur/brain/phase-2-motion/ · te: https://saifsiddiqui.in/te/brain/phase-2-motion/ · ar: https://saifsiddiqui.in/ar/brain/phase-2-motion/ · hi-Latn: https://saifsiddiqui.in/hi-latn/brain/phase-2-motion/

**Type:** phase  
**Status:** done  
**Created:** 2026-07-17  
**Tags:** phase, build-log, motion

## ✅ Phase 2: Motion layer (built & deployed)

Premium scroll motion on the Phase 1 site. No content/SEO/i18n files touched (only motion attributes on existing markup). Deployed to Vercel preview.

**How it's built:**
- One island `MotionRoot.tsx` in base layout, `client:idle`, renders nothing; dynamic-imports Lenis + GSAP/ScrollTrigger **only when motion is allowed** (reduced-motion users don't run it). Lenis on GSAP rAF ticker, synced to ScrollTrigger; anchors route through `lenis.scrollTo`.
- Hero entrance = **pure CSS** keyframes (exact `cubic-bezier(0.16,1,0.3,1)`), no JS dep, removed wholesale under reduced-motion. `#hero-webgl-mount` untouched.
- Reveals: `data-reveal`; island **only arms elements still below the viewport** → anything being read stays put → zero flash, zero CLS by construction. `ScrollTrigger.batch`, enter-once, 0.6s expo-out, 0.08s stagger, will-change scoped + cleared. Contact form has no reveal hooks.
- Pinned work transition: each track's 2 featured cards → pinned stage (desktop + fine pointer + below-fold only); card 2 scrubbed cross-fade/parallax over card 1. Mobile/touch/reduced-motion → static stacked grid (class never applied).
- Magnetic cursor on hero scroll cue (inner wrapper, doesn't fight centering) + CSS hover lifts on work/brain cards, gated behind `(hover:hover)`, `(pointer:fine)`, `prefers-reduced-motion: no-preference`.

**Verified in real Chrome:** motion-on (Lenis active, 24 below-fold armed, 0 visible ever hidden, both stages pinned, reveals fire once + clear will-change); RTL pin exact mirror on /ar/ (card 2: +1267→0 LTR, −1267→0 RTL); reduced-motion tears down live to final states; touch 390px no pin/cursor; keyboard focus scrolls off-screen links into view with Lenis. **Lighthouse:** mobile 97/100/96/100, desktop 100/100/96/100, CLS 0, TBT 0ms (96 = localhost Speed Insights 404).

**⚠️ Watch item:** the first featured card intentionally ends at **opacity 0.5** behind the covering card at the pin's end state. Confirm in a later pass that card 1's links stay **keyboard-focusable + screen-reader reachable** (focusing it should scrub the pin back to reveal it), and that 0.5-opacity text isn't the only path to that case study. Low priority; note for Phase 3/QA.

Next: [Phase 3](/brain/roadmap/), WebGL hero + graph canvas + AI live demo.

## Linked from

- [Roadmap](/brain/roadmap/)

All notes: [/brain/](/brain/)

---

Mohd Saif, Product Engineer. I build solutions, not dead software.
Email: saifmd238@gmail.com · GitHub: https://github.com/Saif-09 · LinkedIn: https://www.linkedin.com/in/mohd-saif-134076141/ · Résumé (PDF): https://saifsiddiqui.in/resume

More for agents:
- [/llms.txt](/llms.txt): short summary of this site
- [/llms-full.txt](/llms-full.txt): the complete text of every page in one fetch
- [/agents.md](/agents.md): when to use this site, and how to query it
- [/sitemap-index.xml](/sitemap-index.xml): every URL, in seven languages
