# Motion
Read this before adding any animation. The static page must already be finished and passing
`inspect.mjs`; motion is the last pass, never a way to rescue a weak layout.
## Contents
1. The budget
2. What strong pages do with motion
3. Four questions before any animation
4. Tokens
5. Scroll behaviours, ranked
6. Recipes (copy, then adapt)
7. Signature moves
8. Reduced motion and performance
9. What gives generated motion away
10. Rules for motion
## 1. The budget
A page that does not move reads as a mock-up; the inspector counts what moves while the page loads and
scrolls, and fails it under eight elements. The motion layer has five parts:
- **Two or three scroll scenes:** the big, scroll-driven moments award sites are remembered for (a
camera flying through a field of pictures, a picture that grows from a window to the whole screen,
type that crosses a held stage, cards that deal out and stack). The direction sheet deals them from
`library/scenes.json` when the library has scenes for the brief; `references/scenes.md` says how to
fill and fit them. They are where the page is bold; everything else stays quiet so they land.
- **One signature moment:** the thing a visitor would describe an hour later: the headline that rises
out of a mask, the image that opens like a shutter, the wordmark that scales with the scroll. The
direction sheet from `draw.mjs` names one. Build that one well; it is the only move allowed to be
loud.
- **Section entrances, choreographed per section:** the parts of each section arrive in reading order
(heading, text, picture, details), staggered 60–90ms, rising 16–32px with opacity, once. Vary the
manner with the content instead of stamping one preset everywhere: a list arrives row by row, a
picture opens from a mask or settles from a slight scale, a figure counts up once, a statement rises
line by line.
- **Feedback under the hand:** buttons that give on press, links that answer the pointer, cards and
pictures that respond on hover, a menu that opens from where it was clicked.
- **One moving picture:** a found clip as a muted loop with a poster frame, a slow drift or scale on
a large photograph, or a scroll-linked move on the hero picture.
What is still banned: the same fade-and-slide-up with the same timing on every block, which is the most
cited motion tell of generated pages; motion that hides content until a script runs, with no fallback;
bounce and elastic easing; anything that animates layout properties. Uniform motion says nobody decided
anything; choreographed motion with one protected moment says somebody did.
## 2. What strong pages do with motion
Measured on the latest 99 Awwwards Sites of the Day (July to October 2026), at 1440 and 390, with
the transitions, tweens and scroll triggers recorded while each page loaded and was scrolled. Where
the gallery does something the house rules ban, the house version follows.
- **The stack.** Two in three use a smooth-scroll library and three in five ship Three.js; GSAP is
named on 56. The quiet layer below (reveals, line rises, the ink fill) needs none of it: CSS scroll
timelines and view transitions do that work, though only 3 and 6 of the sites use them. The big
scroll scenes are built on GSAP ScrollTrigger, as on the award sites, through the shared stage in
`assets/scenes/` (`references/scenes.md`).
- **Curves.** The curves that really ran are strong ease-outs: expo out (`.16,1,.3,1` and
`.19,1,.22,1`) ran on 14 sites, cubic out on 13, quint out on 7, and by number of transitions the
quint and expo outs lead every chosen curve. In-out curves are kept for wipes and things that
travel (cubic and quart in-out, about 15 sites each). Ease-in appears only for exits, short, before
a slow arrival. Colour changes use plain `ease` (46 sites). Overshoot sits in 19 stylesheets and
ran on 2.
- **Durations.** Hover colour 200-300 ms; small interface moves 300-450 ms; reveals and line rises
600-1,000 ms; picture openings and wipes 1,100-1,600 ms. The median of everything that ran on a
site is 420 ms.
- **Staggers.** 20-40 ms between characters, 75-120 ms between lines, 50-100 ms between list items.
- **Scroll-linked motion is locked to the scrollbar.** Of the sites that scrub, 14 of 18 lock it with
no smoothing; the other four smooth it by 0.1-3 s. Reveals fire when the element's top is 80-95% of the way
down the viewport, once.
- **How much moves.** A median of 130 elements are moved by script on a page, but they come from a
few gestures: one split heading is 20-60 elements. The inspector's floor (8, warning under 16) is a
floor, not a target.
- **Waiting is the most common cost.** 60 of 99 open on a loading screen, 26 of them with a counter.
Half the usable pages were still unreadable 2.5 s after the browser said the page had loaded, a
third at 4.5 s. The preloaders were not covering slow pages (their documents loaded faster than the
rest) and the readers' design marks were the same whatever the wait; only usability fell. House
version: no preloader, the message readable on the first frame, the load sequence over by 1.2 s.
- **Pins cost length.** Two in three scrolling pages hold something sticky or pinned. Those pages
are twice as long (a median 16.6 screens against 7.5) for fewer words per screen (27 against 46).
House version: at most two held stages, about one screen per step, three screens per stage.
- **Reduced motion is mostly missing.** 35 of 99 have any `prefers-reduced-motion` rule, 16 have
three or more; 5 of 26 scroll-scrubbed scenes have one. Every recipe here keeps the finished state
as the reduced state.
## 3. Four questions before any animation
1. **How often will someone see it?** Once per visit (hero load, a section reveal): it can take its
time. Many times per visit (hover, menu, accordion): fast and small. Hundreds of times (keyboard
actions, typing): no animation.
2. **What is it for?** Valid answers: it shows where something came from, it confirms an action, it
keeps a change from being jarring, it explains how the product works, it is the signature moment.
"It looks nice" is only valid for the signature moment.
3. **Which easing?** Entering or leaving: ease-out, so movement starts the instant the eye arrives.
Moving between two places on screen: ease-in-out. Colour and opacity on hover: `ease`. Constant
travel (marquee, progress): `linear`. `ease-in` makes interfaces feel slow because the first frames
barely move; do not use it. The CSS keyword curves are weak, so use the tokens below.
4. **How long?** Press feedback 100–160ms. Hover 150–200ms. Menus and popovers 150–250ms. Dialogs and
drawers 250–450ms. Section and hero reveals 500–900ms. Leaving is quicker than arriving (about 70%).
## 4. Tokens
```css
:root {
--ease-out: cubic-bezier(0.22, 1, 0.36, 1); /* entrances, moving hovers, UI under 600ms */
--ease-expo: cubic-bezier(0.16, 1, 0.3, 1); /* new: reveals of 800ms and longer, line rises, openings */
--ease-in-out: cubic-bezier(0.76, 0, 0.24, 1); /* wipes, panels, things that travel while visible */
--ease-soft: cubic-bezier(0.33, 1, 0.68, 1); /* slow scale and drift on photographs */
--ease-exit: cubic-bezier(0.55, 0.055, 0.675, 0.19); /* new: something leaving for good, short */
--t-press: 140ms;
--t-hover: 180ms;
--t-ui: 240ms;
--t-reveal: 800ms;
--t-open: 1200ms; /* new: picture openings and clip wipes */
--stagger-line: 90ms; /* new */
--stagger-item: 70ms; /* new */
}
```
How the study bears on each:
- `--ease-out` (quint out) is in 28 stylesheets with its near twin and is the single most frequent
chosen curve among transitions that ran. Keep it.
- `--ease-expo` is new. Expo out (`.16,1,.3,1` and `.19,1,.22,1`) is in 28 stylesheets and ran on 14
sites, more than any other chosen family, and it is the curve the long reveals use: line rises,
picture openings, card entrances of 0.9-1.6 s. Use it for anything at `--t-reveal` or longer; it
replaces `--ease-out` in the masked headline and the reveal-on-scroll recipes.
- `--ease-in-out` (quart in-out) matches its family (14 stylesheets, 7 ran). Keep it for wipes,
shutters, menus and page transitions.
- `--ease-soft` (cubic out) matches the cubic-out family (21 stylesheets, 13 ran), but the study uses it
for interface moves of 0.3-0.8 s, where `--ease-out` already serves. Narrow it to slow scale and
drift on photographs.
- `--ease-exit` is new. Ease-in is right for exactly one job: something that leaves for good (an
outgoing caption, a replaced slide, a closing panel), short, at about 60-70% of the matching
entrance. Never for anything the visitor is waiting to see.
- Not adopted: `.4,0,.2,1` (38 stylesheets) is a framework default, and overshoot curves (19) stay
banned on interface elements.
- `--t-hover` stays at 180ms. The study's colour hovers run 200-300ms; a little quicker reads as
more exact. A hover that moves something (a label roll, an arrow) takes `--t-ui` to 400ms.
## 5. Scroll behaviours, ranked
| Behaviour | Pages of 99 | What it serves | House version |
| --- | --- | --- | --- |
| Smooth-scroll library | 67 | A feeling of weight; makes scrubbed scenes easier to build | Optional, through `Stage.smooth()`: Lenis at lerp 0.1-0.14, off for touch and reduced motion |
| Held (sticky or pinned) stage | 44 | One picture or object carries several claims; chapters | Yes: the scroll scenes (`references/scenes.md`), two or three per page, one to four screens each; other sticky stages three screens at most |
| Split text | 40 | Lines and words arriving as they are read; hover rolls | Lines for headings and statements; words only for the ink fill; characters only for a heading of one to three words |
| Colour by chapter | 34 | Tells chapters apart without padding bands | Yes, as hard cuts at section edges; the header ink follows in 300ms |
| Scroll-scrubbed scene (camera, object, image sequence) | 26 | Explains a product by moving it | Yes, from the scene library, built on the shared stage with a finished composition for reduced motion |
| Parallax depth | 24 | Depth in collages; pictures drifting inside oversized frames | Travel under 8% of the element, pictures held at 106-110% in their frames |
| Marquee | 21 | Life on a static band | Existing "Edge-to-edge marquee": one per page, pausable |
| Masked line or word rise | 18 | Headings that arrive like set type | Yes: "Masked line reveal" on load, "Lines that rise on scroll" below the fold |
| Wheel drives a scene, no page scroll | 16 | Browsing one object | Not used (see 2.6) |
| Blur-in | 15 | Soft arrival | At most 8px of blur, 600ms, gone once settled; never on body text |
| Picture from inset frame to full-bleed | 14 | The hand-over from a type screen to the big picture | Yes: "Opening window", about one screen of scroll |
| Grey-to-ink text fill | 14 | Paces one statement | Yes, once per page, starting at 45% ink: "Ink fill" |
| Sheet slides over a held layer | 11 | Page-turn rhythm between chapters | Yes: new move "Sheet over a held picture" |
| Scramble or decode | 11 | A "code" voice | Not used (see 2.6) |
| Header ink by chapter | 10 | The fixed header stays legible | Yes, with colour by chapter |
| Horizontal track pinned to vertical scroll | 8 | Walking through rooms or a collection | Native drag row first; pinned only when the row is under three screens long, with keyboard support: "Sideways gallery" |
| Curtain footer | 8 | The page ends as a scene | Existing move |
| Rolling digits | 6 | Figures that arrive | Yes, for pages whose case is a handful of numbers: "Rolling figures" |
| Index with hover preview | 5 | Navigating work by name | Existing move, upgraded recipe |
Text, split by unit: characters on about 20 pages, lines on about 14, words on about 11. Character
splits serve short headings and button rolls; on long statements they leave fragments mid-scroll.
## 6. Recipes
All recipes keep the content visible when JavaScript is off, put every hidden state inside
`prefers-reduced-motion: no-preference`, and use the tokens above. Two notes that apply to several:
- **Scroll timelines need a scroller to watch.** `animation-timeline: view()` takes the nearest scroll
container, and `overflow: hidden` makes one. A picture inside an `overflow: hidden` frame therefore
never animates. Use `overflow: clip` on frames, or name the timeline on the section
(`view-timeline-name: --hero`) and point the children at that name. This also applies to the
existing "Scroll-linked progress" recipe.
- **Scroll-linked means linear.** The scroll is the easing. 14 of the 18 scrubbing sites in the study
lock motion to the scrollbar, which is what CSS scroll timelines do.
All recipes keep content visible when JavaScript is off or slow: the hidden state only applies under
`html.js`, which a one-line inline script sets.
```html
```
### Load sequence in 1.2 seconds
Everything is in the HTML and painted on the first frame; the sequence only adds arrival. Nothing
waits for the load event, and nothing in the navigation animates.
```html
…
Stools for
small kitchens

…
…
…
…
```
Each name may appear once per page. Browsers without cross-document view transitions simply navigate.
Study: 16 pages load a page-transition library and run full-screen curtains of 0.7-2.4 s; 6 use view
transitions. The new page should be readable within half a second.
### Ink fill
One statement that fills from part-ink to full ink as it rises through the screen, line by line. It is
readable before it fills; the fill only sets the pace.
```html
Nine people, one workshop, no catalogue: every stool is made for the kitchen it goes to.
``` ```css [data-fill] { --fill: var(--ink); color: var(--fill); } @supports (animation-timeline: view()) { @media (prefers-reduced-motion: no-preference) { .is-filling > span { display: inline-block; color: color-mix(in oklab, var(--fill) 45%, transparent); animation: ink-fill linear both; animation-timeline: view(); animation-range: cover 20% cover 45%; } } } @keyframes ink-fill { to { color: var(--fill); } } ``` ```js (() => { if (!CSS.supports('animation-timeline: view()') || matchMedia('(prefers-reduced-motion: reduce)').matches) return; document.querySelectorAll('[data-fill]').forEach((el) => { const words = el.textContent.trim().split(/\s+/); el.textContent = ''; words.forEach((word, i) => { const s = document.createElement('span'); s.textContent = word; el.append(s, i < words.length - 1 ? ' ' : ''); }); el.classList.add('is-filling'); }); })(); ``` Each word watches its own position, so the words of a line fill together as the line moves from three quarters of the way down the screen to just past half way. Set `--fill` on a dark chapter to its ink. Study: 14 pages scrub a statement from grey to ink; unread words started at about 25-30% and settled frames still caught statements half pale, which reads as disabled text. Here: one statement per page, three to five lines, 32px or larger, starting at 45% (about 3:1 against the ground, enough for large text). Full ink without scroll timelines or under reduced motion. ### Rolling figures For pages whose case is a handful of numbers: each digit rolls up to its value once, the columns a beat apart. The real number stays in the text for screen readers and search. ```html1987 the year the first stool was sold
``` ```css .roll__digits { display: inline-flex; font-variant-numeric: tabular-nums; line-height: 1; } .roll__col { display: inline-block; height: 1em; overflow: clip; } .roll__strip { display: flex; flex-direction: column; transform: translateY(calc(var(--d) * -1em)); } .roll__strip > span { height: 1em; } @media (prefers-reduced-motion: no-preference) { .roll:not(.is-in) .roll__strip { transform: none; } .roll.is-in .roll__strip { transition: transform 1100ms var(--ease-expo) calc(var(--i) * var(--stagger-item)); } } ``` ```js (() => { const els = [...document.querySelectorAll('[data-roll]')]; if (!els.length) return; els.forEach((el) => { const value = el.textContent.trim(); const said = Object.assign(document.createElement('span'), { className: 'sr-only', textContent: value }); const shown = Object.assign(document.createElement('span'), { className: 'roll__digits' }); shown.setAttribute('aria-hidden', 'true'); let i = 0; for (const ch of value) { if (!/\d/.test(ch)) { shown.append(ch); continue; } const col = Object.assign(document.createElement('span'), { className: 'roll__col' }); const strip = Object.assign(document.createElement('span'), { className: 'roll__strip' }); strip.style.setProperty('--d', ch); strip.style.setProperty('--i', i++); for (let n = 0; n < 10; n++) strip.append(Object.assign(document.createElement('span'), { textContent: n })); col.append(strip); shown.append(col); } el.classList.add('roll'); el.replaceChildren(said, shown); }); if (!('IntersectionObserver' in window)) { els.forEach((el) => el.classList.add('is-in')); return; } const io = new IntersectionObserver((entries) => { for (const e of entries) if (e.isIntersecting) { e.target.classList.add('is-in'); io.unobserve(e.target); } }, { rootMargin: '0px 0px -15% 0px' }); els.forEach((el) => io.observe(el)); })(); ``` Study: 6 pages roll digit columns (stats, a year, coordinates), about 0.9-1.1 s per column on an expo-out, staggered by column. Use it for three or four figures at display size, never in running text and never for a value that changes after load. Reduced motion: the figure is set at its value. ### Accordion ```css .acc__panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 320ms var(--ease-out); } .acc__panel > div { overflow: hidden; } .acc[open] .acc__panel, .acc__item.is-open .acc__panel { grid-template-rows: 1fr; } ``` Animating `grid-template-rows` from `0fr` to `1fr` opens to the content's real height without measuring it in JavaScript. ### Menu or dialog ```css .menu { transform-origin: top right; transition: opacity var(--t-ui) var(--ease-out), transform var(--t-ui) var(--ease-out); } .menu[hidden], .menu.is-closed { opacity: 0; transform: scale(0.97) translateY(-6px); pointer-events: none; } ``` Things open from where they were triggered (`transform-origin` at the trigger). Nothing enters from `scale(0)`; start at 0.95–0.98. A centred dialog is the one case that scales from its centre. ## 7. Signature moves The direction sheet names one. Each of these was seen repeatedly on the studied gallery sites. | Move | What happens | Build with | | --- | --- | --- | | Masked headline | Lines rise from behind their baselines on load | Masked line reveal recipe | | Shutter image | Hero or showcase image opens from an edge, settling from 1.12 scale | Image shutter recipe | | Scaling wordmark | A giant wordmark shrinks or grows a few percent as the hero scrolls out | Scroll-linked recipe on `scale` | | Pinned story | A heading holds while its items scroll past | Sticky recipe | | Index with preview | Text rows; the hovered row shows its image | Index with preview, anchored to the row | | Curtain footer | The last section is revealed from under the page as you reach the end | `position: sticky; bottom: 0` footer under a main with a background and `z-index` | | Slow crossfade | Hero image crossfades through 3–4 photographs, 5–6s each | Stacked images, opacity keyframes, pause when the tab is hidden | | Edge-to-edge marquee | One line of very large type travels sideways, slowly | Duplicate the content, `translateX(-50%)` linear loop, pause on hover and under reduced motion | | Drag row | A horizontally scrollable row of large images with scroll snapping | `overflow-x: auto; scroll-snap-type: x mandatory`, no library | | Colour by chapter | Each chapter is its own full-bleed colour field, so the ground changes at section edges; a fixed header takes the ink of the chapter beneath | Section backgrounds in CSS; `base.js` sets `--header-ink` from `data-ink`. Never recolour the whole page from an observer | | Opening window | A type screen hands over to the big picture: a window in the middle of a held stage opens to full-bleed over about one screen | Opening window recipe | | Ink-fill statement | One statement fills from 45% to full ink as it rises through the screen | Ink fill recipe | | Sheet over a held picture | A full-bleed photograph holds for one screen; the next chapter slides up over it as an opaque sheet | A 100svh `position: sticky; top: 0` figure followed by a section with its own background and a higher `z-index`; no script | | Sideways gallery | A short row of pictures moves sideways while the vertical scroll holds the stage | Sideways gallery recipe | | Pushed line | One line of very large type is pushed sideways by the scroll, reading whole at the middle of its run | `animation-timeline: view()` on the line, `translateX` from 12% to -12%, linear; static where unsupported | | Rolling figures | The few figures that make the case roll up to their values once | Rolling figures recipe | | Carried picture | The picture clicked in a list travels into place as the next page's hero | Page to page recipe | Pick by subject. A portfolio wants the index with preview; a restaurant wants the slow crossfade; a product wants the shutter on its hero object. If the move says nothing about the subject, it is decoration and the page is better without it. Pick by subject, as before: the opening window for a product or a place with one great photograph; the sheet for chapters that each own a picture; the sideways gallery for a short set of rooms, kitchens or pieces; the pushed line for a brand whose claim is a word; rolling figures for a report, a fund or a practice that leads with numbers; the carried picture for a portfolio whose index and case pages are both built. ## 8. Reduced motion and performance - `prefers-reduced-motion: reduce` means fewer and gentler, not none: keep opacity and colour changes, drop travel, scale, parallax and autoplay. All recipes above are written inside `no-preference` blocks so the reduced state is simply the finished state. - Animate `transform`, `opacity` and `clip-path`. Animating `width`, `height`, `top`, `left`, `margin` or `padding` forces layout on every frame. `grid-template-rows` for accordions is the accepted exception because it is short and rare. - Do not drive many children from one changing CSS variable on a parent; set the transform on the element that moves. - `will-change` only on an element that is about to animate, removed afterwards. - Pause loops when off screen or when the tab is hidden. - No scroll hijacking and no custom cursor. They cost usability, which is 30% of an Awwwards score and the line most winners score lowest on. Smoothing only through `Stage.smooth()`, which stays off for touch and reduced motion. ## 9. What gives generated motion away - The same fade-up on every block. - A dozen staggered entrances in the hero. - Hover lift or zoom on every card and image. - Bounce or elastic easing on interface elements. - Infinite loops that mean nothing: pulsing dots, floating shapes, shimmering text, rotating badges. - A reveal that never fires and leaves a blank band in the page. Run `inspect.mjs` after the motion pass and look at every screenshot for missing content. ### Seen in the latest Sites of the Day Seen in the study, with how often, and what the house does instead. - **Preloaders and counters (60 of 99; 26 with a counter).** Half the usable pages were unreadable 2.5 s after load, a third at 4.5 s; the preloaders sat mostly on pages that had already loaded, and 25 of the 29 that loaded in under 3 s still showed nothing readable at 4.5 s. Counters stalled, outlasted the load or were tweens with nothing behind them. House: no preloader. If a scene must load, the HTML first screen (headline, picture, action) is there at once and the scene fades in behind it when ready. - **Gates (10 of 99): enter, sound, age or gesture before any content.** House: none. Sound is a control on the page, off by default. - **Scroll hijacking (16 wheel-driven scenes, 9 of them endless, plus 14 single screens where the wheel does nothing).** The visitor cannot tell where they are, how much is left or how to get out, and the keyboard and screen readers get nothing. Usability 6.01 against 6.50 for pages that scroll. House: native scroll; any scene is driven by scroll position on a sticky stage that ends, with a count. - **Smooth-scroll libraries (67 of 99).** They make the scrubbed scenes feel weighted, and they also put a delay between the hand and the page; with a long ease the page keeps sliding after the wheel stops. - **Custom cursors (28 of 99).** Followers, image trails, a lens that smears the headline it sits on, labels that only exist on hover; 10 of the 28 have any reduced-motion rule. House: the system cursor; labels are on the page. - **Scramble and typewriter text (11 and 5).** The headline unreadable for four seconds, body copy garbled mid-scroll, actions held back until the typing ends. House: not used. Real words arrive whole. - **Long held runways.** Pages with pins average twice the length for fewer words per screen; 17 pages are 20 screens or longer, some with whole screens that hold only a slow rotation. House: about one screen per step, three per stage, every held screen carries a line of text. - **Content held at zero opacity.** Reveals that keep text invisible until the scroll settles, inactive items dimmed to 10-15%, statements left half pale. House: reveals only below the fold, once, never longer than 900 ms in view; dims no lower than 40%; fills start at 45%. - **Motion over words.** Marquees and drifting objects that cross statements, a sliding line that crops its own words, blur left on text mid-move. House: nothing moves across text the visitor is reading; a moving line must read whole at some point of its run. - **Overshoot and bounce (19 stylesheets).** House: still banned on interface elements. - **Endless loops with no off switch.** Grain, pulsing dots, equalisers, spinning badges, running with no reduced-motion rule on most pages. House: loops pause off screen and stop under reduced motion. Where the study's work does something the house bans, the house version: hairline progress bars and tick rails on held stages become a plain count in the body face ("2 of 4"); zero-padded counters ("01/08") become a plain count or nothing; crossfading gradient grounds become flat chapter colours with hard cuts; monospace HUD labels become small text in the text face. ## 10. Rules for motion 1. **First frame.** The headline, the sentence and the picture are in the HTML and painted on the first frame. The load sequence starts without waiting for the load event and is settled by 1.2 s, with four elements at most. 2. **No preloader, no counter, no gate, no custom cursor, no wheel capture.** Smoothing only through `Stage.smooth()` (Lenis at lerp 0.1-0.14, off for touch and reduced motion): weight, never a page that keeps sliding after the hand stops. 3. **Curves.** `--ease-out` for anything under 600ms; `--ease-expo` for reveals of 800ms or more; `--ease-in-out` for wipes and things that travel; `--ease-exit` only for things leaving for good; `linear` for scroll-linked motion and loops; plain `ease` for colour. No overshoot. 4. **Durations.** Press 140ms; hover colour 180-200ms; hover that moves 300-400ms; menus 240ms; reveals 700-900ms; picture openings 1,100-1,300ms; exits 60-70% of the matching entrance. 5. **Staggers.** Lines 80-110ms; list items 50-90ms; characters 20-40ms and only in a heading of one to three words; four steps at most in any one sequence. 6. **Reveals.** Fire once, when the element's top is 85-90% of the way down the viewport; rise 12-32px or from a line mask; nothing in view stays hidden for more than 900ms; nothing above the fold waits for scroll. 7. **Scroll scenes are where the page is bold.** The big moves come from the scene library (`references/scenes.md`): the direction sheet deals two or three per page, each built on `assets/scenes/stage.js`, each held for one to four screens, each with a phone version and a finished composition. Inside a scene the scene's own numbers apply (a picture may grow from a window to the whole screen, type may cross the stage, a camera may fly through a field of pictures); rules 8 and 10 still hold for the text a visitor is reading. 8. **Outside the scenes, scroll-linked travel stays quiet.** Linear and locked to the scrollbar; parallax under 8% of the element; scale between 0.94 and 1.15; built with scroll timelines inside `@supports`, static without them. Other held stages: sticky, about one screen per step, three screens at most, a line of text on every held screen. 9. **One of each.** One signature move (a dealt scene can be it); one moving picture; one marquee at most. A quiet section between two scenes, so each one lands. 10. **Text stays readable.** Fills start at 45% ink; dims no lower than 40%; blur at most 8px and gone once settled; nothing moves across text being read. 11. **Colour by chapter** as hard cuts at section edges; the fixed header's ink follows in 300ms. 12. **Horizontal motion.** The native drag row first; the pinned sideways gallery only for rows under three screens long, with keyboard support. 13. **Page to page.** View transitions, 480ms at most, off under reduced motion. 14. **Reduced motion.** Every hidden state lives inside `prefers-reduced-motion: no-preference`; the reduced page is the finished page; loops stop. Two in three award pages skip this, which is a reason to do it, not a licence to skip it.