Everything below was rendered on 2026-07-27 and verified by driving a real browser or by measuring the file, not by inspection. Scope is website animations, the orb pipeline and the social clip factory. App Store assets and the narrated onboarding video are out of scope.
The site link is the real 11-section page with the animation layer in it. Scroll into "It sees how it all connects" and "One sentence becomes part of a long arc" and scrub up and down. The percentage badge in the corner is the live scroll progress.
The data-flow story, built for the siri section whose headline is already
"One sentence becomes part of a long arc". Four beats, 430 frames, scrubbed by scroll. One spoken
sentence in the gym becomes a habit card, and that card takes its place in the week. Nothing cuts:
the card physically travels into the grid and the cell it fills stays marked.
| Beat | What it does | Built from |
|---|---|---|
| 1 Said | Her line as a live waveform | real speech envelope, 398 frames from a Cartesia clip |
| 2 Heard | Waveform resolves into words | word reveal timed off the envelope, not hand-tuned |
| 3 Card | Habit card forms, green check draws | new HabitLogCard component |
| 4 Weekly | Card shrinks, a line carries it into the grid, Wednesday lands green | week grid + connector with a travelling head |
Copy is lifted verbatim from the live page's own beatstrip in that section, so this is an animated version of already-approved content rather than new copy. On the page the animation replaces those four static cards, but only once the player is actually live, so with JavaScript off or reduced motion the original cards stay and the section still reads.
One thing to decide: the card shows "4 x 8 @ 185 lb" because the live page already says that. The-source models a habit as name, days, time and status, with no quantity field anywhere, so the page is already ahead of the data model on sets and reps.
Three sections are pinned scroll-scrub driven by a Remotion Player through
seekTo(). Six animate once on entry. One is deliberately left still. The whole
integration is one stylesheet and one script added to the page, nothing else changed.
| Section | Headline | Treatment |
|---|---|---|
hero | A coach in your pocket | live orb loop replaces the static PNG |
vision | Goals that keep getting pushed aside | animate once, slow push |
problem | We looked at 95 apps | animate once |
newway | A privacy-first conversational AI coach | animate once |
how-it-works | Talk it through, or just tap | pinned scrub, habit card forming |
car | Hands free, on the drive home | animate once, slow push |
morning | It sees how it all connects | pinned scrub, insight panel |
siri | One sentence becomes part of a long arc | pinned scrub, week grid |
privacy | What you say stays yours | left alone on purpose, motion undercuts trust copy |
coach | Built by a real coach | animate once |
cta | Opening soon | animate once, form works without JS |
Three pinned sections is the ceiling before the page feels like it is fighting the
reader. If it reads as too much, how-it-works is the one to drop first.

morning, "It sees how it all connects", at 0 / 25 / 50 / 75 / 100 percent scroll.

siri, "One sentence becomes part of a long arc", same five positions.

how-it-works, "Talk it through, or just tap", same five positions.
Also rendered as transparent WebM for overlay use:
media/web/*.webm. The section transition exists as transparent WebM and as
ProRes 4444 for Premiere (the ProRes is 34MB so it is kept local, not deployed).
| File | Raw | Gzip | Who pays it |
|---|---|---|---|
gg-anim.css | 4.9 kB | 1.9 kB | everyone |
gg-anim.js | 5.9 kB | 2.1 kB | everyone |
gg-player.js | 479 kB | 154 kB | wide screens only |
The Player is 97 percent of the weight, so it is loaded conditionally: skipped
under 900px wide, skipped on prefers-reduced-motion, skipped on Save-Data. Phones get
the 4 kB path and the sections still scrub through CSS. The orb loop is also skipped on Save-Data.
Run with node scripts/verify-site.mjs against a real headless browser.
| Check | Result |
|---|---|
| Scrub tracks scrolling down | [0, 0.2496, 0.4996, 0.7496, 0.9996] |
| Scrub tracks scrolling back up | [0.7496, 0.4996, 0.2496, 0] |
| Players attached and receiving seekTo | 3 of 3 |
| Console and page errors | 0 |
| Reduced motion: all sections in finished state | yes, pinning collapsed, orb video not injected |
| JavaScript disabled | 4 of 4 headlines present, signup surface intact |
A real port into React Three Fiber, not a wrapper: the lab is plain three.js with no package.json. Every value is a pure function of the frame, particle seeds come from a fixed PRNG, so frame N is identical on every render pass.
Eight variants, each at frames 0 / 6 / 12 / 18 / 24 across one breath cycle, because stills lie about motion. Rows top to bottom:

| Row | Variant | Row | Variant |
|---|---|---|---|
| 1 | Locked preset | 5 | Sealed, gap 0.02 |
| 2 | Dense, 60k particles | 6 | Coach talking, amp 0.8 |
| 3 | Sparse, 12k particles | 7 | User talking, amp 0.8 |
| 4 | Wide gap 0.3 | 8 | Both halves off (idle grey) |
The lab bug is that all 16 voice-routing amounts default to 0, so the envelope drives almost nothing. These are the values the orb should see, derived from real audio through the locked mapping, written as plain JSON so they can go back into the app.
| Field | Value |
|---|---|
| Frames | 398 at 30fps, 13.28 s |
| Speaking / silent | 391 / 7 |
| Peak amp | 1.0 |
| Mapping | em = 1.25 * (0.6 + amp*0.55), scale = 0.5 + amp*0.9, membrane = 0.7 + amp*0.8 |
| File | talking-curves.json |
Source is coach_greeting.mp3, the only onboarding clip that exists as
mp3. The other 27 declared clips are wav-only. Nothing was written back to the-source.
One parameterised composition, one config array, three aspect ratios, rendered in a single pass. 6 clips x 3 ratios = 18 files. Format is the locked one: mundane footage, no talking, one headline, message lives in the caption.
placeholder copy Every headline is marked
[PLACEHOLDER] on purpose. No approved marketing, no claims, no numbers, no results.
Safe margins measured rather than eyeballed: on 9:16 the headline occupies
y 0.129 to 0.258 of frame height, inside the 0.12 to 0.82 box. The bottom 18 percent is left clear
because the platform UI covers it. All 18 files are in media/social/.
| Item | Detail |
|---|---|
| Orb look is not final | The contact sheet exists so it can be picked by eye. The gap currently reads as a hard seam, and the particle field is on the speckled side. |
| Section colours flagged | newway uses orb-gold.png against the coach-blue / user-orange rule. The preset names that value userPrimary #dc9612, so it looks like naming rather than a wrong asset. Nothing was recoloured. |
| Live site has no reduced-motion handling | grep -c prefers-reduced-motion returns 0 on the deployed page. The integration layer adds it. The gap on the live site is separate from this work. |
| Nothing merged back | gg-website-mock was never written to. Merging is two tags plus the asset files, see site-integrated/DROP-IN.md. |