CINEMATIC-SCROLL-STORY

Cinematic Scroll Story

Multi-layer parallax, scroll-choreographed narrative, rich photography, transparent-edge PNG layers like theater flats.

Difficulty:★★★★★
Mood:Narrative · Immersive · Multi-Layer · Scroll-Driven · Epic
Cinematic & Motion-Heavy

Visual DNA

A sticky viewport containing 7+ parallax layers that shift, scale, blur, and reveal as you scroll 3700px. Scene photographs with transparent edges stack like theater flats. A sight card slider flies in from 420vw on X. Story panels fade in at precise scroll positions. The page is a film you control with your scroll wheel.

Design Principles

SCROLL IS THE DIRECTOR

Every pixel of scroll progress maps to a precise layer transform. The user's scroll wheel is the camera dolly, the crane, the zoom lens. Nothing animates on a timer — everything is choreographed to scroll position. A 3700px scroll container is the film reel; the sticky viewport is the screen. Lerp smoothing at 0.14 ensures the motion feels cinematic, not mechanical.

What it prevents: Auto-playing animations that ignore user agency, and jittery raw-scroll transforms that feel like a broken video player.

---

LAYERS AS THEATER FLATS

Transparent-edge PNGs stack at different Z-depths like theatrical flat sets — sky, distant architecture, midground bazaar, foreground bridge, frame elements. Each layer has an independent transform driven by scroll progress. The sky drifts slowly, the foreground races. This parallax separation creates real depth, not the fake depth of a CSS drop shadow.

What it prevents: Single-layer photography that feels flat, and card-based layouts that have no spatial relationship between elements.

---

SEGMENT-BASED CHOREOGRAPHY

The scroll range is divided into scenes. Each scene has its own enter/exit logic via a `segmentInOut` helper: a function that returns 0→1→0 across a defined scroll range. Scene 1 (0-650px): title rises and fades. Scene 2 (560-1620px): bridge splits and launches. Scene 3 (1760-2700px): bazaar saturates and story panel emerges. Scene 4 (2760-3560px): sight cards fly in from 420vw. Each scene is independently choreographed.

What it prevents: Uniform fade-in-everything approaches where all elements animate the same way regardless of narrative position.

---

BLUR AS SCENE TRANSITION

Between scenes, photographic layers ramp blur from 0px to 14px and shift brightness/saturation. This mimics a camera focus pull — the previous scene dissolves out of focus as the next scene sharpens. The blur is not a hover effect; it's a narrative device. It signals "this scene is ending" before the next scene's elements appear.

What it prevents: Hard cuts between sections that feel like page loads rather than cinematic transitions.

---

INFINITE SLIDER WITH CLONE-NORMALIZE

The sight card slider uses a 3-set clone technique: the card array is triplicated, and when the scroll offset reaches the end of the first set, the position instantly resets to the equivalent position in the second set. The user never sees the reset — it happens in a single frame. This creates true infinite scrolling without the visible "jump back" of naive carousel implementations.

What it prevents: Carousels that visibly rewind, and finite sliders that run out of content mid-narrative.

Typography System

# Typography System

## Display Typeface

| Property | Specification |
|----------|--------------|
| **Register** | Editorial serif — monumental, narrative, cinematic |
| **Examples** | Ogg Medium, Playfair Display, Cormorant Garamond, GT Sectra |
| **Weight** | 400-500 (medium, not bold — the scale provides the impact) |
| **Tracking** | -0.02em to 0.01em (tight, monumental) |
| **Size** | `clamp(4rem, 14vw, 14rem)` — truly monumental, fills the stage |
| **Transform** | Sentence case or title case, never uppercase for display |

**Why monumental serif:** The scroll story is a film. The title is the opening credit. A serif at 14vw carries the weight of a cinema marquee. Sans-serif feels like a web app; serif feels like a story.

---

## Body Typeface

| Property | Specification |
|----------|--------------|
| **Register** | Clean, readable sans-serif — the narrator's voice |
| **Examples** | Inter, Söhne, Aperçu, Aktiv Grotesk |
| **Weight** | 400-500 |
| **Tracking** | 0.01em-0.02em (slightly open for readability) |
| **Size** | `clamp(0.875rem, 1vw, 1.125rem)` — 14-18px |
| **Line Height** | 1.5-1.6 (comfortable for story panels) |

**Why sans body:** The serif display is the title; the sans body is the narration. The contrast between the two typefaces mirrors the contrast between the cinematic image and the explanatory text.

---

## Story Panel Type

| Property | Specification |
|----------|--------------|
| **Register** | Same sans as body, lighter weight |
| **Examples** | Inter Light (300), Söhne Light |
| **Weight** | 300-400 |
| **Tracking** | 0.02em-0.04em (open, breathable) |
| **Size** | `clamp(1rem, 1.5vw, 1.5rem)` — larger than body, smaller than display |
| **Line Height** | 1.5 |
| **Max Width** | 480px (narrow, like a story card) |

**Usage:** Story panels that fade in at precise scroll positions. These carry 2-4 sentences of narrative text — the "scene description" between cinematic moments.

---

## Navigation / UI Type

| Property | Specification |
|----------|--------------|
| **Register** | Same sans family, small and functional |
| **Weight** | 400-500 |
| **Tracking** | 0.08em-0.12em (wide, like film chapter markers) |
| **Size** | 12-14px |
| **Transform** | UPPERCASE for nav buttons and labels |

**Usage:** Navigation buttons that fade in at scroll position 3360-3660px. These are functional UI — they should feel like chapter markers in a film, not web navigation.

---

## Type Scale

```
Display:      clamp(4rem, 14vw, 14rem)    — monumental, fills the stage
Story panel:  clamp(1rem, 1.5vw, 1.5rem)  — narrative interludes
Body:         clamp(0.875rem, 1vw, 1.125rem) — standard readable text
Nav/UI:       12-14px                       — functional chapter markers
```

The scale spans from 12px to 224px — a 18:1 ratio. The display is the film title; the nav is the DVD menu.

---

## Font Pairing Logic

**Serif display + sans body:** The classic editorial pairing. Ogg Medium + Inter. Playfair Display + Söhne. The serif carries emotion; the sans carries information.

**Never:** Two sans-serifs (no contrast), two serifs (too uniform, loses the narrator/title distinction), or monospace for anything (too technical for a narrative).

Color Architecture

# Color Architecture

## Ground

The ground varies by scene — this is a narrative style, not a single-palette style. Each scene has its own color world:

```css
/* Scene 1 — Sky / Title */
--ground-sky: #B8D4E3;        /* Soft atmospheric blue */
--ground-sky-deep: #5B8BA6;   /* Deeper sky for gradient */

/* Scene 2 — Bridge / Architecture */
--ground-bridge: #8B7355;     /* Warm stone, aged architecture */
--ground-bridge-dark: #3D3328; /* Deep shadow under bridge */

/* Scene 3 — Bazaar / Warmth */
--ground-bazaar: #D4956A;     /* Warm amber, market lanterns */
--ground-bazaar-glow: #F5C99B; /* Lantern glow */

/* Scene 4 — Sights / Cards */
--ground-sights: #1A1A2E;     /* Deep night, card showcase */
--ground-sights-card: #FDF1E1; /* Cream card background */
```

**Why scene-based color:** A film doesn't use one color grade. Each scene has its own palette that transitions via blur and brightness shifts. The color IS the narrative.

---

## Text

```css
--text-on-light: #111411;     /* Ink on cream/light scenes */
--text-on-dark: #FDF1E1;      /* Cream on dark/night scenes */
--text-muted: rgba(253, 241, 225, 0.6); /* Muted cream for subtext */
--text-story: #1A1A1A;        /* Story panel text on cream cards */
```

**On dark scenes:** Text is cream (#FDF1E1), never pure white. Pure white on dark photography creates halation and feels digital.

**On light scenes:** Text is ink (#111411), never pure black. Pure black on warm backgrounds feels harsh.

---

## Accent

```css
--accent-warm: #C4956A;       /* Dusty amber — lantern light */
--accent-cool: #5B8BA6;       /* Muted blue — sky reflection */
--accent-gold: #C4A265;       /* Gold — for sight card highlights */
```

**Rule:** The accent shifts with the scene. The warm amber belongs to the bazaar; the cool blue belongs to the sky. Don't mix scene accents — each scene has ONE accent that matches its palette.

---

## Shade Gradients (Scene Transitions)

```css
/* Transition gradient between scenes */
--transition-shade: linear-gradient(
  180deg,
  rgba(0, 0, 0, 0) 0%,
  rgba(0, 0, 0, 0.3) 50%,
  rgba(0, 0, 0, 0) 100%
);
```

**Purpose:** Between scenes, a shade gradient overlay creates a brief darkening — like a film's scene transition. This is applied via opacity that ramps with scroll progress at scene boundaries.

---

## Color Treatment Per Scene

| Scene | Dominant Color | Mood | Treatment |
|-------|---------------|------|-----------|
| 1 — Sky/Title | Soft blue #B8D4E3 | Open, expansive | Bright, slight desaturation |
| 2 — Bridge | Warm stone #8B7355 | Architectural, grounded | Contrast ramp, blur to 14px at exit |
| 3 — Bazaar | Warm amber #D4956A | Alive, warm | Saturation ramp from 60% to 100% |
| 4 — Sights | Deep night #1A1A2E | Showcase, focused | Dark, cards glow with cream |

---

## Color Don'ts

| Don't | Do Instead |
|----------|--------------|
| One static palette for all scenes | Scene-specific palettes that transition |
| Pure #FFFFFF text on dark scenes | Cream #FDF1E1 |
| Pure #000000 text on light scenes | Ink #111411 |
| Saturated neon accents | Dusty, warm, scene-appropriate tones |
| Sudden color jumps between scenes | Blur + brightness + shade gradient transitions |
| Color overlays that flatten photography | Subtle opacity ramps (0.3 max) |

Motion Principles

# Motion Principles

## Philosophy

Motion in Cinematic Scroll Story is **scroll-choreographed, not timer-driven**. Every transform is a function of scroll progress. The user is the director — they control the pace. Lerp smoothing at 0.14 ensures the motion trails the scroll slightly, creating a cinematic "weight" rather than a 1:1 mechanical link.

---

## Scroll-Driven Parallax

| Layer | Transform | Scroll Range | Easing |
|-------|-----------|-------------|--------|
| Sky | translateY(0 → -80px), scale(1.0 → 1.08) | 0-3700px (full) | lerp 0.14 |
| Back architecture | translateY(0 → -160px), scale(1.0 → 1.12) | 0-3700px | lerp 0.14 |
| Bazaar | translateY(0 → -240px), saturation(60% → 100%) | 0-2700px | lerp 0.14 |
| Bridge | translateY(0 → -320px), blur(0 → 14px) | 0-1620px | lerp 0.14 |
| Frame | translateY(0 → -120px) | 0-3700px | lerp 0.14 |
| Title | translateY(0 → -210px), opacity(1 → 0) | 0-650px | segmentInOut |
| Story panel | opacity(0 → 1), translateY(40px → 0) | 1760-2700px | segmentInOut |

**Lerp smoothing formula:**
```javascript
current += (target - current) * 0.14;
```
This creates a slight lag — the layers "follow" the scroll rather than snapping to it. The effect is cinematic weight.

---

## Segment-Based Choreography (segmentInOut)

Each scene has a scroll range with enter and exit phases:

```javascript
function segmentInOut(scroll, start, end) {
  const t = (scroll - start) / (end - start);
  if (t < 0 || t > 1) return 0;
  // Enter: 0 → 1 in first 30% of range
  // Hold: 1 for middle 40%
  // Exit: 1 → 0 in last 30% of range
  if (t < 0.3) return t / 0.3;
  if (t > 0.7) return (1 - t) / 0.3;
  return 1;
}
```

| Scene | Scroll Range | Enter | Hold | Exit |
|-------|-------------|-------|------|------|
| Title | 0-650px | 0-195px (rise) | 195-455px | 455-650px (fade) |
| Bridge split | 560-1620px | 560-980px (widen) | 980-1200px | 1200-1620px (launch + blur) |
| Bazaar | 1760-2700px | 1760-2100px (saturate) | 2100-2400px | 2400-2700px (desaturate) |
| Sights slider | 2760-3560px | 2760-3100px (fly-in) | 3100-3400px | 3400-3560px (hold) |
| Nav buttons | 3360-3660px | 3360-3500px (fade) | 3500-3660px | — |

---

## Sight Card Slider Fly-In

```css
/* Cards start at 420vw (off-screen right) and fly to center */
transform: translateX(clamp(420vw, calc(420vw - scroll-progress * 420vw), 0));
/* Easing: power(1.55) — fast start, slow finish */
```

The slider uses a custom power easing: `Math.pow(t, 1.55)`. This creates a fast initial fly-in that decelerates as the cards approach center — like a camera whip-panning and settling.

---

## Infinite Slider (Clone + Normalize)

```javascript
// 3-set clone: [cards][cards][cards]
// When scroll offset passes end of set 1, instant-jump to equivalent position in set 2
const setWidth = cards.length * cardWidth;
if (offset > setWidth) {
  offset -= setWidth; // Instant normalize — no visible jump
}
```

**Why 3 sets:** The user never sees the edges. At any scroll position, there are always cards to the left and right. The reset happens in a single frame — invisible.

---

## Mouse Parallax (Optional)

```javascript
// Subtle mouse-driven parallax on top of scroll parallax
const mouseX = (e.clientX / window.innerWidth - 0.5) * 2;
const mouseY = (e.clientY / window.innerHeight - 0.5) * 2;
// Apply: layer.translateX += mouseX * depthFactor * 0.12;
// Depth factor varies per layer (0.5 for sky, 1.0 for foreground)
```

**Factor 0.12:** Very subtle. The mouse parallax adds life without competing with the scroll choreography.

---

## Blur/Brightness/Saturation Shifts

| Transition | Property | Range | Trigger |
|-----------|----------|-------|---------|
| Scene 1 → 2 | blur | 0px → 14px on bridge | scroll 1200-1620px |
| Scene 2 → 3 | brightness | 1.0 → 0.85 on bridge | scroll 1200-1620px |
| Scene 3 enter | saturation | 60% → 100% on bazaar | scroll 1760-2100px |
| Scene 3 exit | saturation | 100% → 70% on bazaar | scroll 2400-2700px |

---

## Reduced Motion

When `prefers-reduced-motion: reduce`:
- All scroll-driven transforms snap to final values (no lerp)
- Blur transitions disabled (instant swap)
- Parallax disabled (layers static)
- Slider auto-centers (no fly-in)
- Story panels appear at full opacity

```css
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
  }
}
```

---

## What NOT to Animate

| Never | Why |
|-------|-----|
| Timer-based auto-scroll | The user controls the pace — always |
| Spring or bounce easing | Wrong register — this is a film, not a toy |
| Rotation on photographic layers | Breaks the theatrical flat metaphor |
| Scale on text elements | Text should translate and fade, not grow |
| Staggered card entrances | Cards fly in as a group, not individually |

Layout Grammar

# Layout Grammar

## Grid System

**No visible grid.** The layout is defined by absolute-positioned layers within a sticky stage. However, internally:

- Sticky stage: 100vw × 100vh, `overflow: hidden`, `position: sticky`, `top: 0`
- Scroll container: 100vw × `calc(100vh + 3700px)`, `position: relative`
- Layers: `position: absolute`, `inset: 0`, with CSS variable transforms
- Story panels: centered at 45% vertical, max-width 480px

---

## The Sticky Stage Architecture

```css
.cinema-scroll {
  height: calc(100vh + 3700px); /* Total scroll distance */
  position: relative;
}

.cinema-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  /* All parallax layers are children of this */
}

.cinema-layer {
  position: absolute;
  inset: 0;
  /* Transform driven by JS scroll progress */
  transform: translate3d(var(--tx), var(--ty), 0) scale(var(--scale));
}
```

**Why sticky + scroll container:** The sticky stage stays fixed in the viewport while the user scrolls through 3700px of "virtual" scroll space. The JS reads `scrollTop` and maps it to layer transforms. This is the core architecture of scroll-driven cinema.

---

## Layer Stack (Z-Order)

| Z-Index | Layer | Content | Parallax Depth |
|---------|-------|---------|----------------|
| 1 | Sky | Transparent-edge PNG of sky/atmosphere | Slowest (0.3) |
| 2 | Back architecture | Distant buildings, bridges | Slow (0.5) |
| 3 | Bazaar | Midground market scene | Medium (0.7) |
| 4 | Splitframe L/R | Foreground frame elements | Fast (0.85) |
| 5 | Bridge | Hero architectural element | Fast (0.9) |
| 6 | Frame two | Closest foreground frame | Fastest (1.0) |
| 7 | Story panel | Narrative text overlay | Independent |
| 8 | Sights slider | Card carousel | Independent |
| 9 | Nav buttons | UI navigation | Independent |

**Depth factor:** Higher = more parallax movement per scroll pixel. The sky barely moves; the foreground races. This creates the illusion of real depth.

---

## Story Panel Layout

```
┌─────────────────────────────────────────┐
│                                         │
│  [Photographic layers behind]           │
│                                         │
│                                         │
│         ┌─────────────────┐             │
│         │  Story Panel    │             │
│         │  (max-w-480px)  │             │
│         │                 │             │
│         │  2-4 sentences  │             │
│         │  of narrative   │             │
│         │                 │             │
│         └─────────────────┘             │
│         (centered at 45% vertical)      │
│                                         │
└─────────────────────────────────────────┘
```

Story panels fade in at precise scroll positions. They sit at 45% vertical — slightly above center — so the photographic layers remain visible above and below.

---

## Sights Slider Layout

```
┌─────────────────────────────────────────┐
│                                         │
│  [Cards fly in from 420vw on X]         │
│                                         │
│   ┌──────┐  ┌──────┐  ┌──────┐         │
│   │ Card │  │ Card │  │ Card │  ←→     │
│   │      │  │      │  │      │ infinite│
│   └──────┘  └──────┘  └──────┘         │
│                                         │
│  [Nav buttons fade in below]            │
│      [PREV]        [NEXT]              │
└─────────────────────────────────────────┘
```

The slider occupies the lower third of the stage. Cards are 280-320px wide, cream background (#FDF1E1), with photographic content. The infinite scroll means cards extend beyond both edges.

---

## Responsive Behavior

| Breakpoint | Layout Change |
|------------|--------------|
| **Desktop (1200px+)** | Full 7-layer parallax, 3700px scroll, mouse parallax |
| **Tablet (768px)** | Reduce to 5 layers, 2800px scroll, disable mouse parallax |
| **Mobile (375px)** | Reduce to 3 layers (sky, midground, foreground), 2000px scroll, disable mouse parallax, simplify slider to swipe |

**On mobile:** The parallax depth is maintained but with fewer layers. The scroll choreography still works — the scenes still transition. Don't disable the scroll story on mobile; simplify it.

---

## Layout Don'ts

| Don't | Do Instead |
|----------|--------------|
| Static sections stacked vertically | Sticky stage with scroll-driven layers |
| Single-layer photography | 7+ transparent-edge PNG layers at different depths |
| Card grids for content | Story panels and sight slider within the stage |
| Standard navigation bar | Nav buttons that fade in at scroll position 3360px |
| Fixed pixel scroll distances | Use `calc(100vh + Npx)` for viewport-relative depth |
| CSS-only parallax (background-attachment) | JS lerp-smoothed transforms for cinematic weight |

Texture & Surface

# Texture & Surface

## Primary Texture: Transparent-Edge PNG Layers

The texture of this style is photographic — but not flat photography. Each layer is a transparent-edge PNG that stacks like a theatrical flat:

| Layer | Content | Edge Treatment |
|-------|---------|---------------|
| Sky | Atmospheric gradient, clouds | Fully transparent bottom edge |
| Back architecture | Distant buildings, bridges | Transparent left/right/bottom, feathered top |
| Bazaar | Market scene, lanterns, people | Transparent edges, feathered into background |
| Splitframe L/R | Foreground frame elements | Transparent inner edges, hard outer frame |
| Bridge | Hero architectural element | Transparent edges, feathered for depth blending |
| Frame two | Closest foreground | Transparent inner, hard outer frame |

**Why transparent edges:** The layers blend into each other at their edges. There are no hard rectangular boundaries — the layers merge like theater flats viewed from the audience. This creates the illusion of a continuous 3D scene from 2D layers.

---

## Film Grain

```css
.film-grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9999;
  opacity: 0.04;
  background-image: url("data:image/svg+xml,...");
  mix-blend-mode: overlay;
}
```

**Parameters:**
- Opacity: 0.03-0.05 (barely visible — this is a film, not a horror game)
- Position: fixed (covers viewport, doesn't scroll with layers)
- Blend mode: overlay

---

## Shade Gradients (Scene Transitions)

```css
.scene-shade {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0) 0%,
    rgba(0, 0, 0, 0.25) 50%,
    rgba(0, 0, 0, 0) 100%
  );
  opacity: var(--shade-opacity); /* Ramps with scroll at scene boundaries */
}
```

**Purpose:** Between scenes, a shade gradient creates a brief darkening — like a film's scene transition. The opacity ramps from 0 to 0.25 and back to 0 at each scene boundary.

---

## Blur as Texture

Blur isn't just a transition — it's a textural quality. The bridge layer ramps blur from 0px to 14px as it exits. This creates a depth-of-field effect: the bridge goes out of focus as the bazaar scene sharpens. The blur is a textural signal that says "this layer is receding."

| Layer | Blur Range | Trigger |
|-------|-----------|---------|
| Bridge | 0px → 14px | scroll 1200-1620px (exit phase) |
| Bazaar | 14px → 0px | scroll 1760-2100px (enter phase) |
| Sky | 0px (constant) | Never blurs — it's the backdrop |

---

## Image Processing Rules

All photographic layers must be processed:

1. **Remove backgrounds** — transparent edges (PNG with alpha channel)
2. **Color-grade per scene** — each layer matches its scene palette
3. **Desaturate slightly** — 80-90% saturation (not full, not raw)
4. **Add film grain** — overlay at 0.04 opacity
5. **Feather edges** — soft alpha transitions, no hard rectangular boundaries

**The goal:** Each layer should look like a film set piece — a theatrical flat with a photographic surface, not a rectangular image slapped on a page.

---

## Surface Rules

| Surface | Treatment |
|---------|-----------|
| Stage background | Sky layer (transparent-edge PNG) |
| Layer surfaces | Photographic, color-graded per scene |
| Story panels | Semi-transparent dark overlay (rgba(0,0,0,0.6)) with cream text |
| Sight cards | Cream (#FDF1E1) solid background, photographic content |
| Nav buttons | Transparent, thin border (1px, 40% opacity), cream text |
| Dividers | None — scenes transition via blur and shade gradients |

---

## Texture Don'ts

| Don't | Do Instead |
|----------|--------------|
| Rectangular images with hard edges | Transparent-edge PNGs with feathered alpha |
| Raw, unprocessed photography | Color-graded, slightly desaturated, film grain |
| CSS box shadows for depth | Z-depth via parallax layer separation |
| Gradient overlays on layers | Shade gradients only at scene boundaries |
| Glassmorphic story panels | Semi-transparent dark overlay with cream text |
| Patterned backgrounds | The photographic layers ARE the background |

Vocabulary Bank

# Vocabulary Bank

## Primary Terms

| Term | Why It Matters |
|------|---------------|
| `sticky viewport` | The stage stays fixed while the user scrolls through virtual space |
| `parallax layers` | 7+ transparent-edge PNGs at different Z-depths with independent transforms |
| `transparent-edge PNG` | Images with alpha-channel feathered edges — stack like theater flats |
| `scroll-driven choreography` | Every transform is a function of scroll progress, not a timer |
| `segmentInOut` | Helper function for enter/hold/exit phases per scroll range |
| `lerp smoothing` | Linear interpolation at 0.14 factor — creates cinematic weight |
| `theater flats` | Theatrical set pieces — the metaphor for how layers stack |

---

## Secondary Terms

| Term | Effect |
|------|--------|
| `blur ramp` | 0px → 14px blur as a layer exits — depth-of-field transition |
| `shade gradient` | Brief darkening at scene boundaries — film transition |
| `infinite slider` | 3-set clone with instant-jump normalization — true infinite scroll |
| `fly-in from 420vw` | Sight cards enter from far off-screen right with power(1.55) easing |
| `mouse parallax` | Subtle 0.12 factor mouse-driven offset on top of scroll parallax |
| `saturation ramp` | Bazaar layer desaturates to 60% and ramps to 100% on enter |
| `scene palette` | Each scene has its own color world that transitions |
| `depth factor` | Per-layer multiplier for parallax intensity (0.3 sky → 1.0 foreground) |
| `focus pull` | Blur transition between layers — mimics a camera focus shift |
| `narrative scroll` | The scroll container is the film reel; the sticky stage is the screen |

---

## Context Terms

| Term | Mood |
|------|------|
| `theater flat` | Staged, deliberate, artificial-but-believable |
| `film scroll` | The page is a film you control with your scroll wheel |
| `scene transition` | Blur + brightness + shade — like a cut in cinema |
| `camera dolly` | The scroll wheel is the camera movement |
| `opening credits` | The monumental serif title at scene 1 |
| `chapter markers` | Nav buttons that fade in at the end — like DVD chapters |
| `Mostar` | The reference implementation — a city scroll story |
| `bazaar` | The warm midground scene — market, lanterns, life |

---

## Banned Terms

| Term | Why It Breaks the Style |
|------|------------------------|
| `static sections` | The page is a continuous scroll story, not stacked sections |
| `simple fade transitions` | Fades are insufficient — use blur, brightness, and shade |
| `single-layer photography` | One image can't create depth — need 7+ parallax layers |
| `card-based layout` | Cards belong in the sight slider, not as the primary layout |
| `auto-playing animation` | The user controls the pace — never timer-driven |
| `spring easing` | Wrong register — this is a film, not a toy |
| `CSS-only parallax` | `background-attachment: fixed` can't do multi-layer lerp-smoothed transforms |
| `rectangular images` | Hard edges break the theater flat metaphor — use transparent PNGs |
| `timer-based carousel` | The slider is scroll-driven, not auto-advancing |

---

## Prompt-Ready Phrases

```
"sticky viewport with 3700px scroll container"
"7+ transparent-edge PNG parallax layers"
"scroll-driven choreography with lerp smoothing at 0.14"
"segmentInOut transitions per scene range"
"blur ramp 0px to 14px between scenes"
"infinite slider with 3-set clone and instant-jump normalize"
"sight card fly-in from 420vw with power(1.55) easing"
"theater flat layering with feathered alpha edges"
"shade gradient at scene boundaries (opacity 0.25 max)"
"monumental serif display at clamp(4rem, 14vw, 14rem)"
"reduced motion: snap values, disable transitions"
```

Anti-Slop Checklist

☐Real media carries the world — every scene has photographic footage or stills; procedural gradients never stand in for scenery
☐Every media layer is graded (CSS filter chain) and veiled (atmospheric gradient) — no raw ungraded stock ships
☐One invariant element survives every scene transition (thread line + instrument HUD stay pixel-continuous across cuts)
☐Scenes cut hard — media, veil, palette, and text swap within one frame; no cross-fades or blend frames
☐Luminance descends across scenes — earliest scene brightest, final scene darkest, strictly descending (v2 measured 101 → 71 → 27 → 19; the descent is the contract, not those numbers)
☐Instrument HUD holds one constant tone across all scenes — scene identity lives in media + veil only, never in re-colored UI
☐Text and HUD zones sit over locally darkened veil or scrim (AA contrast for light instrument text, including bright scenes)
☐Sight cards arrive from thread markers, not from a detached rail — markers persist once passed, cards cut with their scene
☐Sight cards are solid instrument cards — no glass, no `backdrop-filter`, no frosted surfaces anywhere
☐Char reveals rationed to the opening wordmark + scene place-names only — body copy and HUD never split
☐Only the active scene's media plays; everything pauses off-screen, on tab blur, and under reduced motion
☐Entrance is a hard cut from the loader field to scene 01 — no fade-in of the page itself
☐No bounce or spring easing (strong decel curves only, e.g. `cubic-bezier(0.22, 1, 0.36, 1)`)
☐No timer-based auto-scroll (the reader controls the pace)
☐No card grids or bento mosaics as primary layout (one field of evidence at a time)
☐No carousel rewind — no looping sight rail, no back-to-start button; the descent goes one way
☐No uniform fades as scene transitions
☐No decoration without instrument function — no blobs, orbs, mesh gradients, neon glow beyond the thread signal, animated grain, replacement cursors, or autoplay audio
☐`prefers-reduced-motion` collapses fully — stacked graded stills in document order, thread fully drawn and static, no loader, all copy readable

When to Use

# When to Use

## Perfect For

| Context | Why This Style Works |
|---------|---------------------|
| **Travel/tourism** | The scroll story mimics a journey — each scene is a destination |
| **Brand storytelling** | The cinematic narrative structure communicates brand heritage and values |
| **Documentary sites** | The scroll-as-film metaphor is literally how documentaries work |
| **Cultural institutions** | Museums, heritage sites — the narrative pacing matches the content |
| **Long-form narratives** | The segment-based choreography supports complex, multi-act stories |
| **City/destination guides** | The reference implementation (Mostar) is exactly this — a city scroll story |
| **Architecture portfolios** | The parallax layering showcases architectural photography with depth |

## Brand Personality Match

This style reinforces brands that are:
- **Narrative-driven** — the story IS the product
- **Cinematic** — visual-first, atmosphere-heavy
- **Immersive** — the user doesn't browse, they experience
- **Cultural** — rooted in place, heritage, or craft
- **Premium** — the production value signals quality

## Avoid When

| Context | Why It Fails |
|---------|-------------|
| **SaaS dashboards** | Users need information density, not a film |
| **E-commerce product pages** | The scroll story is too slow for transactional browsing |
| **Developer tools** | Too atmospheric, not technical enough |
| **News/content sites** | The narrative pacing can't support rapid content scanning |
| **Mobile-first with limited scroll** | The 3700px scroll depth requires committed engagement |
| **Accessibility-critical sites** | The heavy scroll-driven motion can be challenging, even with reduced-motion fallbacks |
| **Sites needing SEO content** | The minimal text and JS-driven content are not search-friendly |

## How to Decide

Ask: **"Does my content have a narrative arc that benefits from cinematic pacing?"**

If yes → Cinematic Scroll Story.
If no → Consider Vast Quiet Cinematic (simpler, less narrative), Dark Cinematic Studio (warmer, more motion), or Editorial Serif Narrative (more text-driven).

## Technical Considerations

- **Performance:** 7+ parallax layers with lerp smoothing requires careful optimization. Use `transform: translate3d()` for GPU acceleration, `will-change: transform` on layers, and debounce scroll handlers.
- **Mobile:** Reduce to 3-5 layers and 2000-2800px scroll depth. Disable mouse parallax. The story still works — just simplified.
- **Accessibility:** The reduced-motion fallback must snap all values to their final state. The narrative should still be readable without motion — story panels should appear at full opacity.
- **Content:** All images must be transparent-edge PNGs. This requires image editing — you can't use stock photos directly.

Common Mistakes

Mistake 1: Using Raw Scroll Without Lerp Smoothing

**What people do:** Map `scrollTop` directly to layer transforms — `layer.style.transform = translateY(scrollTop * depth)`.

**Why it fails:** Raw scroll linking feels mechanical and jittery. Every scroll wheel notch produces a visible jump. The motion has no weight — it feels like a broken video player, not a film.

**Fix:** Use lerp smoothing: `current += (target - current) * 0.14`. The layers "follow" the scroll with a slight lag, creating cinematic weight. The 0.14 factor is the sweet spot — lower feels sluggish, higher feels mechanical.

---

Mistake 2: Single-Layer Photography

**What people do:** Use one full-bleed image as the background and animate it with simple parallax.

**Why it fails:** One layer can't create depth. The parallax effect requires multiple layers at different Z-depths moving at different rates. A single image with parallax looks like a zooming photo, not a 3D scene.

**Fix:** Stack 7+ transparent-edge PNG layers. Each layer has its own depth factor (0.3 for sky, 1.0 for foreground). The differential movement between layers creates the illusion of depth.

---

Mistake 3: Rectangular Images With Hard Edges

**What people do:** Use standard rectangular images (JPGs or PNGs without alpha channels) as parallax layers.

**Why it fails:** Hard rectangular edges break the theater flat metaphor. The layers look like stacked cards, not a continuous 3D scene. The boundaries between layers are visible and distracting.

**Fix:** Use transparent-edge PNGs with feathered alpha channels. The edges of each layer should blend into the layers behind them. This requires image editing — remove backgrounds, feather edges, export as PNG with alpha.

---

Mistake 4: Timer-Based Auto-Scroll

**What people do:** Add an auto-scroll feature that advances the story on a timer, "so users don't have to scroll."

**Why it fails:** The entire premise of this style is that the user is the director. Auto-scroll removes user agency and turns the experience into a video, not an interactive story. Users who want to linger on a scene can't.

**Fix:** Never auto-scroll. The user controls the pace. If you want to guide users, use visual cues (a scroll indicator, subtle arrow) — but never take control of the scroll.

---

Mistake 5: Uniform Fade Transitions Between Scenes

**What people do:** Fade all layers in and out with the same opacity transition between scenes.

**Why it fails:** Uniform fades feel like a slideshow, not a film. Cinema uses focus pulls, brightness shifts, and color transitions — not uniform fades.

**Fix:** Use blur ramps (0px → 14px on exiting layers), brightness shifts (1.0 → 0.85), and saturation changes (60% → 100%). Each scene transition should have its own unique combination of effects.

---

Mistake 6: Visible Carousel Rewind

**What people do:** Implement the sight card slider as a standard carousel that visibly rewinds when it reaches the end.

**Why it fails:** The visible "jump back" breaks the cinematic illusion. The user is reminded they're looking at a web component, not a film.

**Fix:** Use the 3-set clone technique: triplicate the card array, and when the scroll offset passes the end of the first set, instant-jump to the equivalent position in the second set. The reset happens in a single frame — invisible to the user.

---

Mistake 7: Too Many Text Elements

**What people do:** Add headlines, subheadlines, body text, captions, and labels to every scene.

**Why it fails:** This is a visual story, not a text article. The photographic layers carry the narrative. Text should be sparse — a title, a story panel, and nav buttons. More text competes with the imagery and breaks the cinematic atmosphere.

**Fix:** Limit text to: one monumental title (scene 1), one story panel per scene (2-4 sentences), and nav buttons (end). Everything else is visual. If you need more text, the story isn't visual enough — reconsider the content.

Reference Gallery

# Reference Gallery

## Live Sites

### Mostar City Scroll Story
- **What works:** 7+ transparent-edge PNG parallax layers, segment-based scroll choreography, lerp smoothing at 0.14, blur ramps between scenes, infinite sight card slider with 3-set clone
- **URL:** Reference implementation — the canonical example of this style

### Raft (raft.com)
- **What works:** Full-bleed video backgrounds, scroll-triggered scene changes, narrative structure, cinematic color grading
- **URL:** raft.com

### 21 Hrs On The Moon (21hrs.space) — Studio 28K
- **What works:** Vast photography, minimal text, slow reveals, atmospheric pacing, scroll-driven scene transitions
- **URL:** 21hrs.space

### Apple AirPods Pro Page
- **What works:** Scroll-driven product reveals, parallax depth, smooth camera movements, segment-based choreography
- **URL:** apple.com/airpods-pro

## Key Visual References

### Layer Architecture
- **Transparent-edge PNGs** — sky, architecture, midground, foreground with feathered alpha
- **Theater flat stacking** — layers at different Z-depths with independent parallax
- **Depth gradient** — sky (slowest) to foreground (fastest) parallax movement

### Typography References
- **Ogg Medium** at `clamp(4rem, 14vw, 14rem)` — monumental editorial serif
- **Inter** at 16-18px — clean sans body text
- **Story panels** — Inter Light 300, max-width 480px, 2-4 sentences

### Motion References
- **Lerp smoothing at 0.14** — the cinematic weight factor
- **segmentInOut** — enter/hold/exit phases per scroll range
- **Blur ramp 0px → 14px** — depth-of-field scene transitions
- **Power(1.55) easing** — sight card fly-in from 420vw

### Layout References
- **Sticky stage + scroll container** — `calc(100vh + 3700px)` scroll depth
- **Absolute-positioned layers** — CSS variable transforms driven by JS
- **Story panels at 45% vertical** — centered, max-width 480px
- **Infinite slider** — 3-set clone with instant-jump normalization

## Screenshot Notes

> When capturing references for this style, look for:
> - The number of parallax layers (should be 7+)
> - The scroll depth (should be 3000-4000px)
> - The smoothness of motion (should have lerp weight, not raw scroll)
> - The scene transitions (should use blur/brightness, not just fades)
> - The transparent edges on images (should be feathered, not rectangular)
> - The text density (should be minimal — title, story panels, nav only)
> - The slider behavior (should be truly infinite, no visible rewind)

AI Prompts

EXACT REPLICA10365 characters
> Copy this entire prompt and paste it into your AI coding assistant (Cursor, Claude Code, v0.dev, etc.) to generate a Cinematic Scroll Story page.

---

```
Build a cinematic scroll story for "KOVRA" — a field-document river descent from glacial source to night delta. Stack: React + Vite + TypeScript + Tailwind CSS v4. Page title: `Kovra — A River Descent`.

Vibe: A recovered field instrument playing back four hard-cut territories of real footage. One unbroken thread line draws itself through all four scenes while a fixed HUD counts kilometers. The scroll wheel is the playback head.

---

## CONCEPT REGISTER

- Four scenes, one descent: 01 GLACIER (high aerial, pale ice) → 02 PINE (dark forest corridor) → 03 MILL TOWN (warm dusk, human scale) → 04 DELTA (near-black tidal flats).
- One continuous thread line (mint signal stroke) enters from the top edge and draws downward with scroll progress. It never re-routes, never blurs, and never breaks at scene cuts.
- Copy voice is field-document: terse place-names in expanded caps, one poetic field note per scene in light sentence case, mono micro-labels (`FIELD NOTE 118`, `KM 147 / 412`). No marketing headlines, no serif nostalgia.
- Scene windows on scroll progress p (0→1): 01 at 0–0.25, 02 at 0.25–0.5, 03 at 0.5–0.75, 04 at 0.75–1.0. Cut lines at 0.25 / 0.5 / 0.75. km counter maps linearly: km = round(p × 412).

---

## REAL-MEDIA MANIFEST (hard policy)

- Every scene's base layer is photographic: free-license footage or stills (Pexels, Pixabay, Coverr — no attribution-required sources unless licensed). Minimum: 1 video layer + stills across the page; stills-only scenes are allowed if motion comes from thread draw + parallax + arriving cards.
- Every video ships a poster still (representative frame) — posters are the mobile media and the preload face, rendered under the same grade stack.
- No baked-in grading: grade with CSS only — a filter chain on the media, a scene-tint layer (`mix-blend-mode: color`), and an atmospheric veil gradient. Raw library color never ships; every asset sits under its veil.
- Optional: one static monochrome noise layer at opacity ≤0.035 over media only (never over HUD or text) to bind heterogeneous stock into one developed stock. If it reads as visible speckle, delete it — never animate it.
- Reject any clip that cannot hit its scene's luminance step after grading; substitute with fallback search terms.

---

## PER-SCENE COMPOSITION

Layer stack (bottom → top): base media → far still (optional horizon band) → grade filter → scene tint → atmospheric veil → text scrim → thread → POI markers + sight cards → text block → HUD (fixed, topmost).

- 01 GLACIER (km 000–103): glacial river aerial video + ice-peak far still. Ground #0B2436, veil pale-top #D7F0F2 → deep #07161F, tint #8FCBD8. Brightest scene. Hero wordmark over footage at p=0, drifting out by p≈0.06.
- 02 PINE (km 103–206): forest-river drone video + misty-valley far still. Ground #0A1F1A, veil #4E7A6E → #04110D, tint #2E6B5C. First hard luminance drop — walking under the tree line.
- 03 MILL TOWN (km 206–309): old-bridge-at-dusk still (stills-only is fine). Ground #1A0F08, veil #8A5A2E → #0E0804, tint #B07A3A. Warmest scene; the thread dims across the bridge band (scene-scoped mask) to read as passing behind it.
- 04 DELTA (km 309–412): night-water video + estuary far still. Ground #060913, veil #1B2350 → #03050C, tint #2A3A8C. Darkest scene; the thread glow rises so the line is the brightest element in frame. Ends on km 412/412 — no footer, no rewind.

Text placement: place-name lower-left with char-by-char reveal on scene entry; field note below it (max-width ~16–20ch); mono micro-label above. Every text block sits over the veil's dark zone plus a local scrim so light text holds AA.

---

## FONTS (open-license only)

Load via Google Fonts or fontsource:
- Display + UI: Archivo variable (expanded width axis, weights 300–800). Hero wordmark expanded 800 at ~16vw caps; place-names expanded 700 at ~8.5vw caps; field notes Archivo Light ~2.2vw (clamp 18–34px) sentence case.
- Instrument: IBM Plex Mono 400/500 for all HUD, labels, counters (12–14px, uppercase, tracking 0.1–0.18em, tabular-nums on the km readout).
- Two families total. Casing: display and mono always caps; field notes always sentence case, never tracked.

---

## MOTION + SCROLL MODEL

- Sticky 100vh stage inside a tall track (`calc(100vh + 3600px)` desktop; `calc(100dvh + 2000px)` below 768px). One rAF loop with lerp 0.14 driving progress p; optional Lenis underneath for wheel smoothing (Lenis 0.12 + the 0.14 scrub layered, one shared loop).
- Thread draw: single SVG path, `stroke-dashoffset` tied to p — the same value triggers sight cards (marker at km K fires when p ≥ K/412). Thread drifts globally with p so it stays pixel-continuous across cuts.
- Hard cuts at 0.25/0.5/0.75: per-scene stacks toggle `visibility` in the same frame as the scene-attribute swap — no transition, no blend frame. Thread + HUD are outside the swapped stacks and never animate opacity at a boundary (one glow pulse per stitch excepted: ≤+40% opacity, ≤300ms).
- Char reveals (40ms stagger, strong decel, no overshoot) apply to the opening wordmark + four place-names only — nothing else splits.
- Sight cards: fly in from their marker (~450ms, strong decel, no overshoot), dismiss on reverse scroll (~250ms), cut instantly with their scene. Hover does nothing but a 2px nudge. Markers latch lit once passed.
- Loader: ink field, corner brackets draw (≤600ms), three mono status lines in sequence, then a hard cut to scene 01 (cut by ~1.8s or a 6s timeout — never hold the viewer). No spinner, no ring.
- Only the active scene's video plays; pause the rest. Pause everything off-screen and on tab blur. Animate transform/opacity/thread-draw/HUD-text only — never tween filters, shadows, or backgrounds.

---

## HUD SYSTEM

Fixed, topmost, never unmounts, constant bone tone #F2EDE4 in every scene with one mint signal accent #BCF2DC (thread + progress fill only):
- Four L-corner brackets (1px bone/70%, ~28px arms, ~20px inset).
- Top-left: wordmark + `FIELD RECORD / RIVER DESCENT` micro. Top-right: `SCENE 01/04 — GLACIER` index (hard-swaps at cuts) + live `KM n / 412` counter (tabular, change-only writes, forced 412/412 at terminal).
- Bottom-left: flavor telemetry (mock coordinates — mark as mock in code). Bottom-center: three-state line — `SCROLL TO DESCEND` cue (p<0.03) / field-note mirror / `THE RIVER LEAVES EVIDENCE.` at terminal.
- Bottom edge: 1px progress track with mint fill = p and tick marks at 25/50/75%.

---

## REDUCED MOTION + RESPONSIVE

- Reduced motion: full collapse — no loader, no sticky stage; four stacked full-bleed graded stills (video posters) in document order; thread fully drawn and static; all sight cards visible inline; HUD static with km 412/412 and no scene index; no cursor dot. All copy readable, focus order = document order.
- 768px and below: videos swap to poster stills (no video element), parallax factors halve, flavor telemetry drops.
- 360px: far stills drop; text block docks lower-third full-width; sight cards become bottom-anchored full-width strips sliding up; stage uses `100dvh`, never `h-screen`; zero horizontal overflow.

---

## COPY (field-document voice — use verbatim)

- 01 GLACIER: place-name `GLACIAL SOURCE` · field note *"Nobody owns the first kilometer. The ice keeps it."* · micro `KOVRA / FIELD NOTE 001` · detail `Blue hour under permanent weather`.
- 02 PINE: place-name `THE NARROWS` · field note *"The river narrows until the mountain has to listen."* · micro `KOVRA / FIELD NOTE 118` · detail `Split pine, black water, and a hard white current`.
- 03 MILL TOWN: place-name `MILL TOWN` · field note *"Every wheel in town remembers the shape of the water."* · micro `KOVRA / FIELD NOTE 224` · detail `A working river, measured in timber and light`.
- 04 DELTA: place-name `DELTA AT NIGHT` · field note *"At the edge, KOVRA forgets its name and becomes everything."* · micro `KOVRA / FIELD NOTE 412` · detail `Tidal flats, lanterns, and the last clear reflection`.
- Sights: `SIGHT 01 / KM 118 Gorge Gate` · `SIGHT 02 / KM 224 Bell Bridge` · `SIGHT 03 / KM 271 Mill Wheel` · `SIGHT 04 / KM 412 Lantern Quay` — one-line notes each, kept terse.
- HUD microcopy: `FIELD RECORD / RIVER DESCENT` · `SCENE 01/04 — GLACIER` · `KM 000 / 412` · terminal line `THE RIVER LEAVES EVIDENCE.` · loader lines `KOVRA / FIELD RECORD`, `CALIBRATING THREAD`, `LOADING FOOTAGE 07/07` (count = final asset total).

---

## ANTI-PATTERNS (automatic fail)

- NO procedural terrain (SVG ridges, gradient mountains, drawn bridges) — footage or nothing
- NO ungraded or unveiled media
- NO cross-fades, uniform fades, or blend frames between scenes
- NO glass / `backdrop-filter` on cards or HUD — sight cards are solid ink (~92%) with a 1px bone/15% border
- NO card grids or bento mosaics as primary layout
- NO carousel rewind, looping sight rail, or back-to-start button
- NO bounce or spring easing
- NO timer-based auto-scroll
- NO serif display type; NO third font family
- NO decoration without instrument function (blobs, orbs, mesh gradients, extra glows, animated grain, replacement cursors, autoplay audio)

---

## ACCEPTANCE CRITERIA

- Luminance arc strictly descending with the final scene darkest; v2 reference measured 101 → 71 → 27 → 19 (cite as example, not mandate — the descent plus darkest-final are the contract).
- HUD persistent: bone brackets sample-correct in all four scenes; scene index matches the visible scene; km monotonic, exactly 412/412 at the end; progress fill matches p.
- Cut integrity: frame-stepping each boundary shows media/veil/text swapping within ~2 frames, zero blend frames, thread + HUD identical before/after.
- Thread continuous across all scenes in signal mint; brightest linear element in the final scene.
- Typography: two families only, wordmark ~16vw, place-names ~8.5vw, mono HUD 12–14px, no serif glyphs.
- Text zones: AA contrast behind every text block and corner bracket, including the brightest scene.
- Responsive: posters-only at ≤768px; lower-third text + bottom-strip cards at 360px; full reduced-motion collapse as specced.

---

Recreate this page faithfully with the exact register, media policy, scene composition, motion model, HUD system, and acceptance criteria above.
```

This prompt recreates a specific design pixel-faithfully. Study it to understand the style at its purest.