# The Continuous-World Deck Technique — Deep Research Dossier

> **PORTABILITY NOTE (added 2026-07-27).** This process doc is published from Imperium's
> internal operating system. The METHOD is fully portable: prompts, model choices and
> parameters, CSS/SVG recipes, QA gates, and cost discipline all work anywhere. Paths that
> start with `~/.claude/`, `<repo-root>/`, `presentations/`, `scripts/`, or `memory/` are
> internal conveniences on the author's machine; substitute your own equivalents (any
> headless-browser screenshot loop, any image toolchain). Same for internal distribution
> steps (Google Drive folder IDs, Telegram bots) and `<your-vercel-team>` deploy scopes:
> use your own storage, messaging, and hosting. Working style anchors ship WITH this kit
> in `../cards/`, `../console/`, and `../folder/` — start from those, not from zero.

> Extracted 2026-07-06 from the shipped v2 build (`presentations/aios-walkthrough-v2/`, live at https://aios-v2.imperiumos.ai, built overnight 2026-06-12→13 from one mission prompt). This file is the WHY and HOW of every layer. The fill-in mission that reproduces it is `MISSION-TEMPLATE.md` (same folder). The reference implementation IS the v2 source — copy it, don't rewrite it.

## 1. The idea in one paragraph

Every slide is a camera position inside ONE generated world (a single place, one light grade). Every forward slide change plays a short generated film whose FIRST frame is pixel-matched to slide N's background still and whose LAST frame is pixel-matched to slide N+1's still. The browser swaps still → video → next still at frame boundaries, so the audience never sees a cut. Text is always HTML overlaid on top (never baked into images), so type stays sharp, editable, and animatable. The world itself carries a narrative arc (v2: estate dark at dusk on the cover → fully lit on the close = the product metaphor).

## 2. The five layers (bottom to top)

| z | Layer | What it is | Source |
|---|---|---|---|
| 0 | World still | webp background, `object-fit: cover`, crossfades on index change | GPT Image 2, 4k/high, chained refs |
| 0+ | Idle loop (optional) | ambient mp4 over the still while idle (cover only in v2) | Seedance, same still as start AND end frame |
| 1 | Transition film | full-screen mp4, plays once at 1.3× rate on forward nav | Seedance start/end-frame pair |
| 2 | Legibility scrim | always-on gradient, `pointer-events: none` | CSS: `linear-gradient(180deg, rgba(bg,0.30) 0%, rgba(bg,0.06) 30%, rgba(bg,0.10) 62%, rgba(bg,0.55) 100%)` |
| 3 | Text layer | per-slide React component in a transparent SlideShell, framer-motion staggers | HTML/CSS, brand tokens |
| 10 | Chrome | progress dots, slide counter, 5s nav hint | HTML |

## 3. The engine (Presentation.tsx) — what makes it feel seamless

Reference: `presentations/aios-walkthrough-v2/web/components/Presentation.tsx` (350 lines, self-contained).

- **Phase machine:** `idle → leaving (text exits, 220ms) → transition (film plays) → idle(n+1)`. Index only advances when the film ends, so the incoming still appears exactly under the film's last frame — the swap is invisible.
- **`TRANSITION_RATE = 1.3`**: films are generated ~5s but play at 1.3× so they feel 3-4s, Apple-brisk.
- **HEAD-probe availability (`useAvailable`)**: on mount, HEAD-fetch every film path once; any missing/unfinished film automatically falls back to a 700ms crossfade. This is what let v2 ship with 8/9 films done — the deck degrades gracefully per-transition.
- **Skip protection**: any key/click during a transition jumps `video.currentTime = video.duration` (fires `ended` → clean finish). Presenters are never trapped.
- **Backward nav = crossfade** (never reverse-play a film), **reduced-motion = crossfade**, **video error = crossfade**, **12s hard safety timeout** so a stalled video can never hang the deck.
- **Preloading**: while idle, a hidden `<img>` of the next still + `<video preload="auto">` of the current slide's outbound film.
- **Nav surface**: ←/→/space/PageUp/PageDown, F fullscreen, Home/End, click-thirds (left third = back), `data-no-nav-click` opt-out for interactive regions (the live iframe).

## 4. Type + glass system (the "fonts and everything on top")

- **Fonts (Imperium default):** headlines Emilio Light (`@font-face` from the kit's `fonts/`), 72-110px (slide h1s ~84px), letter-spacing -1 to -2.5px. Eyebrows/labels Switzer Medium, 12-13px, letter-spacing 4-5px, uppercase. Numbers Switzer + `tnum`. (Zero-webfont fallback for non-Imperium worlds only: `ui-serif, Georgia, serif` headlines + `ui-monospace` labels — never ship an Imperium deck on the fallback stack.)
- **Ink tokens over photography (Imperium default):** `--ink #F4F1EA` (bone), `--ink-sub rgba(244,241,234,0.82)`, `--ink-faint rgba(216,235,221,0.72)` (mint-tinted). Headline text-shadow `0 2px 28px rgba(8,34,26,0.65), 0 1px 4px rgba(8,34,26,0.4)`. (The old champagne ink set #EBDCC8/rgba(196,173,150) is retired with the warm palette.)
- **Glass card (text on photography, never opaque cards):** `background rgba(244,241,234,0.08)` + `backdrop-filter: blur(24px) saturate(1.4)` + `border 1px rgba(244,241,234,0.18)` + `box-shadow 0 8px 40px rgba(8,34,26,0.45)`, **radius 40** (the BRAND-BOOK R40 lock; the shipped v2 used 14, built before the lock — new Imperium decks use 40, as `deck-cinematic.html` does). Lives in `_premiumCard.ts`, spread into every card.
- **Entrance choreography (per slide):** eyebrow 0.35s → headline 0.5s → sub 0.95s → cards 1.2s + i×0.18s stagger, all `ease: [0.22, 1, 0.36, 1]`. Timing IS design; never everything at once.
- **SlideShell**: transparent wrapper that only adds the top-left logo wordmark; the world lives behind it.

## 5. Asset generation pipeline (the discipline that made it hold together)

1. **Balance preflight** (`balance` tool) — set a spend ceiling before generating anything.
2. **Stills: `gpt_image_2`, 4k, high, 16:9.** VERIFY `response.model === "gpt_image_2"` (+ resolution + quality) on EVERY call; mismatch = discard unjudged, re-route. Drafts never ship (the nano_banana silent-downgrade incident is why).
3. **Structured prompt, every time:** SUBJECT / COMPOSITION (name the reserved text zone!) / LIGHTING / LENS / GRADE / SECTION CONTEXT / STYLE FILTER (one locked paragraph appended verbatim to every prompt — this single paragraph is what enforces one-world consistency).
4. **Consistency by inheritance:** generate still 01 first (2-3 candidates, pick best), then generate each later still WITH a previous still attached as reference image. v2's chain: 01→(02,03,09); 03→04; 04→(05,06); 06→(07,08); 09→10. One place, one grade, all 10 held.
5. **Pilot-then-batch:** still 01 → approve grade → batch the rest. Transition T1 → approve motion feel → batch the rest.
6. **Transitions: `seedance_2_0`, 1080p (state it EXPLICITLY — the default slips to 720p), std, ~5s**, `start_image` = still N, `end_image` = still N+1. Prompt = one sentence naming the camera move + "smooth continuous camera move, no cuts, no new subjects, cinematic, slow ease-in-out, no sound". Let the frames do the work.
7. **Ambient loop:** same still as BOTH start and end frame + "ambient environmental motion only … perfect seamless loop". Seedance holds the seam; Kling shows one.
8. **Filter false-positives (WILL happen):** Seedance throws `nsfw`/`ip_detected` on harmless prompts containing words like "ignition", "comes alive", "sculpture". Fix = simpler literal phrasing. Two rejections on the same transition → switch that one to Kling 3.0 (v2's T9 payoff shipped on Kling). Rejected attempts STILL BILL.
9. **MCP preset interception:** pass `declined_preset_id` to proceed past recommended presets.
10. **Previz tier for big runs (back-ported from scroll-world, 2026-07-12).** When the deck has 6+ transitions, render the FULL transition set on `seedance_2_0_mini` (720p draft tier, frame-locks start/end images like the full model) before spending full-model credits. Wire the drafts into the actual deck app, click through, and judge camera grammar + pacing against the real slides and copy. Only after that review re-render finals on `seedance_2_0` 1080p with the SAME start/end frames and prompts — the draft validates everything that matters at a fraction of the spend. Verify mini's real per-job cost the same way as the full model (estimator under-reports ~5x: 45cr quoted vs ~216cr actual on 2026-07-11); pilot-then-batch (item 5) stays the floor for small runs.
11. **Idempotent generation (back-ported from scroll-world, 2026-07-12).** The output file IS the run state: before ANY generation, check `raw/` (and `web/public/world/`) for the asset. Present → skip; absent → generate. Save every job's JSON next to its asset. A crash, NSFW re-roll, or session restart then never repays for finished work — overnight runs resume mechanically instead of regenerating (and re-billing) what already exists.

## 6. Encode + weight budget

- **Transition films PLAY, they don't scrub** → normal GOP: `ffmpeg -y -i in.mp4 -an -c:v libx264 -preset slow -crf 22 -pix_fmt yuv420p -movflags +faststart out.mp4` (~1-2MB each, whole deck ~15MB). All-keyframe (`-g 1 -crf 18`) is ONLY for scroll-scrubbed video — never needed here.
- **Stills:** webp 2560×1440 q88 (200-430KB each) into `web/public/world/`. Raw 4k PNGs stay in `raw/` (globally gitignored — fine, Vercel uploads the working dir).
- Weak-venue-wifi rule: load the deck once on the venue network before presenting, or run `npm run dev` locally.

## 7. The app skeleton

- Next.js 15 + React 19 + **framer-motion ^11.18.2** (11.11 drops intrinsic attrs on `motion.*` under React 19 — a build-breaking type error v2 hit and v3 pinned away).
- Structure: `web/app/{layout,page,globals.css}` + `web/components/{Presentation,SlideShell}.tsx` + `web/components/slides/SlideNN*.tsx` (one component per slide, copy lives in-file as consts) + `_premiumCard.ts`.
- **Live product = live iframe, never generated fake UI.** v2 slide 05 absolutely positions an iframe into the wall-screen rect of the still (rect measured per-pixel from a screenshot, e.g. 23.44%/20.28%/54.37%/43.33%), `data-no-nav-click`, LIVE DEMO badge. ⚠ Whatever URL is embedded ships to the audience — check it's neutralised (audit yours before shipping).
- Start by COPYING a working deck app, never from zero. **Kit users: the shipped starter is `../documents/deck-cinematic.html`** — a self-contained vanilla-JS port of this exact engine (phase machine, availability probe, crossfade fallback, skip protection, safety timeout) with a working demo world; swap the world, keep the engine. The Next.js reference (`presentations/aios-walkthrough-v2/web/`, author-machine) is the original — reach for it only when you need React/framer-motion specifics beyond the shipped file.

## 8. QA protocol (Playwright against `npm run dev`)

0. **SSIM seam gate FIRST — machine-check every film against its stills before any browser opens** (back-ported from scroll-world, 2026-07-12). The deck swaps still N → film → still N+1 at frame boundaries, so BOTH boundaries of every transition film must near-match the stills the audience actually sees. Check them all from the encoded files:

   ```bash
   # score one boundary: frame vs still, SSIM on stdout
   seam_ssim() { # videoFrameArgs... still.png
     ffmpeg -v error -y "${@:1:$#-1}" -frames:v 1 /tmp/_f.png
     ffmpeg -v info -i /tmp/_f.png -i "${@: -1}" -lavfi "scale2ref[a][b];[a][b]ssim" -f null - 2>&1 \
       | grep -o 'All:[0-9.]*' | cut -d: -f2
   }
   # film first frame vs still N:    seam_ssim -ss 0 -i t3.mp4 still-03.png
   # film last frame vs still N+1:   seam_ssim -sseof -0.05 -i t3.mp4 still-04.png
   # ambient loop seam (first vs last frame of the loop itself)
   ```

   Thresholds: **≥0.90 pass · 0.75–0.90 warn** (the still-swap crossfade usually covers it — eyeball that transition) · **<0.75 fail** = the film drifted off its end-image or the wrong still is wired; regenerate or demote that transition to a crossfade, don't rationalize. Re-run after every re-roll — replacing one film touches both of its boundaries. A red asset can't be QA'd into a green page.

   Calibration against the shipped v2 deck (measured 2026-07-12, webp stills vs 1080p frames): t-04-05 scored 0.93/0.95 (clean), t-01-02 scored 0.85/0.78 (warn band — shipped and reads fine at 1.3× playback), the ambient loop seam 0.92, and a deliberately mismatched still/film pair 0.64. So warn is common and usually acceptable on this pipeline; fail is unambiguous.
1. Screenshot all slides at 1920×1080 with text settled → `qa/screenshots/`.
2. Capture mid-transition frames for 2-3 key films; verify film first/mid/last frames match the stills (v2 saved these as `raw/v-t1-first.png` etc.) — this eyeball pass now confirms *motion feel*; frame identity is already proven by the SSIM gate.
3. Console: zero app errors. `npm run build` clean.
4. Score each slide: (a) brand bar vs approved masters, (b) Apple-keynote register (one idea, huge type, vast space), (c) **legibility — every word readable at a glance over the photography**. Legibility is the criterion v2 was ultimately judged weakest on (Alex's v3 verdict: text fought photoreal backgrounds) — score it hardest, fix with scrims/glass/negative-space regen, not smaller type.
5. Max 3 fix passes per slide, then a full click-through, then repeat the click-through on the LIVE deployed URL.

## 9. Deploy + delivery

- `cd web && npx vercel deploy --prod --scope <your-vercel-team>` to a NEW project named after the deck. **Team `<your-vercel-team>` = public URLs. The `imperium-vercel` team puts SSO walls on previews — never deploy decks there.**
- Verify the live URL loads and click through 3 slides via Playwright against production.
- Write `BUILD-LOG.md` (every generation: prompt, model-field verification, credits, keep/reject + every QA pass) and `MORNING-REPORT.md` (URL, what shipped, honest deviations, credits, next actions).
- Telegram notify: `node scripts/notify-alex.js "<deck> LIVE: <url>" --summary "<3 lines>"`.
- Commit at phase boundaries; push (rule 06/08).

## 10. Cost model (REAL numbers from the v2 run — budget from these, not from guesses)

| Item | Real cost |
|---|---|
| GPT Image 2 still, 4k/high | ~12 cr (11 stills ≈ 132 cr) |
| Seedance 2.0 transition, 1080p ~5s | **~70 cr/job** |
| Filter-rejected Seedance attempt | **still bills** (~70 cr each; v2 ate 3) |
| v2 total (10 stills + loop + 9 films + retries) | **~969 cr** vs ~550 estimated |

Rules derived: (1) double any playbook video estimate; (2) balance preflight + explicit ceiling in the mission; (3) contingency ladder when the ceiling nears: 4s films → 720p → only the highest-impact transitions (cover-in + payoff first) with crossfades elsewhere; (4) drafts/exploration at low res, 4k/high only for keepers; (5) never generate video before its still is approved.

## 11. Pitfall registry (every one was hit for real)

| Pitfall | Fix |
|---|---|
| framer-motion 11.11 + React 19 type/build break | pin `framer-motion@^11.18.2` |
| Seedance defaults to 720p | state 1080p explicitly on every call, including the loop |
| Seedance false-positive nsfw/ip filters (billed) | literal phrasing; no "ignition/comes alive/sculpture"; Kling 3.0 fallback after 2 strikes |
| MCP silent model downgrade (nano_banana incident) | verify `response.model` on every call; mismatch = discard unjudged |
| `params.model: "videotape-alpha"` nested anomaly in gpt_image_2 responses | known MCP display quirk; trust top-level fields |
| Long-running video jobs at close-out | HEAD-probe fallback means you can ship with films pending; log the pickup command in BUILD-LOG |
| Text fights photoreal background (the v2 verdict) | reserve negative space in COMPOSITION, always-on scrim, glass cards, text-shadow; score legibility hardest in QA |
| Deck hangs on stalled video | 12s safety timeout + error → crossfade (already in the engine) |
| Presenter trapped mid-film | skip protection: any input jumps film to end (already in the engine) |
| SSO-walled deploy | `--scope <your-vercel-team>`, never imperium-vercel |
| Client data leaking via live iframe | audit the embedded URL before shipping |

## 12. Source map (read order for a rebuild session)

1. `continuous-world-mission-template.md` (same folder in the kit) — fill and run
2. `../documents/deck-cinematic.html` — the shipped engine to copy (kit users start HERE); `presentations/aios-walkthrough-v2/web/` is the author-machine Next.js original
3. `presentations/aios-walkthrough-v2/00-REBUILD-PROMPT.md` — the original one-prompt (v2-specific)
4. `presentations/aios-walkthrough-v2/BUILD-LOG.md` + `MORNING-REPORT.md` — how the run actually went
5. `memory/business/aireos/website/v3-research/higgsfield-asset-playbook.md` — full generation doctrine (prompt template, loop/scrub, glass recipe, credit discipline)
6. `research/2026-06-10-ai-premium-websites/SOP-presentations.md` — Track A/D doctrine (code renders text, diffusion renders atmosphere)
7. `assets/imperium/brand-v5/` — BRAND-BOOK.md + tokens (if the new deck is Imperium-branded; brand-colors.md is superseded)

⚠ Model IDs, credit prices, and MCP behavior move. Every claim above was verified on the 2026-06-12/13 run; a new mission must re-verify live (`balance`, `models_explore`, model-field checks) rather than trusting this file's snapshot.
