Files
soroush.asadi cb6512fee3 feat(remotion): asset-library catalog + Phase 0 (license firewall, @remotion/lottie, 30 CC0 characters)
- docs/ASSET_LIBRARY.md: curated catalog from the asset sweep (91 sources -> 62
  usable) + completeness-critic reality check; clean CC0/MIT tier, license/geo
  traps, and the 2.5D layered-scene plan (sky->room->furniture->device->character
  ->grain) to fix the "naked scene".
- deps: add @remotion/lottie@4.0.290 (runtime) + DiceBear (build-time devDep).
- scripts/gen-dicebear.mjs: generate 30 CC0 Open-Peeps characters OFFLINE (no
  runtime CDN) into public/illustrations/dicebear/ + a per-file assets.json ledger.
- scripts/check-assets.mjs: license-firewall CI guard — fails on any un-ledgered
  vendored asset.
- AssetSheet dev composition: proves vendored SVG -> staticFile() -> Remotion render
  (30 real characters render cleanly).
- NOTE: GitHub (Open Peeps/IRA/Notion git clones) + Gumroad (Lukasz) are geo-blocked
  headless here; those + Humaaans (Figma export) need a manual/mirror fetch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 18:59:03 +03:30

272 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FlatRender — Curated Asset Library Catalog
> Generated by the `flatrender-asset-sweep` workflow (91 sources surveyed → 62 usable
> after adversarial license/Iran-access verification) + a completeness-critic pass.
> **Read §12 (Reality Check) before treating any count as "done".** Nothing here is
> vendored yet — this is an *acquisition + build plan*, not a shipped library.
**Reference look:** Google "Alegria" / Pablo-Stanley flat editorial illustration — bold flat colour-blocking (23 shades per shape), confident hand-drawn line, expressive hands & foreshortening, characters set in **rich scenes** (rooms, furniture, devices, sky), subtle grain.
**Render stack (verified in repo):** Remotion `4.0.290` + `@remotion/three` `4.0.290` (installed). **`@remotion/lottie` is NOT yet a dependency** in `services/remotion/package.json` — it must be added (MIT, Nexus-mirrorable) before any Lottie path works.
**Stack rule (standing):** every template animation is built on **Remotion + Three.js together** (`@remotion/three`). Flat-illustration art is composited as **2.5D** — 2D layers placed on planes inside a Three.js scene for parallax camera, depth, lighting and 3D particles. Animate only off `useCurrentFrame()`.
**Vendoring target (verified):** `services/remotion/public/` exists and currently holds only `fonts/` (Vazirmatn 400900 woff2). The project already serves vendored files via `staticFile()` (see `src/lib/fonts.ts` and `AppShowcase3D.tsx`). All asset categories below land under `public/illustrations/...` and `public/lottie/...`.
**The "naked scene" problem (verified):** `src/compositions/CharacterStory.tsx` draws a single hand-coded SVG figure standing on an abstract blurred blob inside a gradient — no room, no furniture, no real sky, no props. The catalog and integration plan below exist specifically to fix this with a layered, asset-driven scene system.
---
## 0. Totals (claimed — see §12 for the honest correction)
| Bucket | Claimed usable count |
|---|---|
| **Characters — riggable (separable parts)** | ~110 base components / poses (Humaaans ~100 + Open Peeps part-set) |
| Characters — Lottie/animated (self-authored + curated CC0) | ~5080 loops (mostly self-authored) |
| Scenes & interiors (recolorable, vendor curated subset) | ~300+ curated (from unDraw/ManyPixels/Lukasz pools of 4,000+) |
| Devices / props | ~150 |
| Sky / nature / abstract | ~80 |
| Grain / texture | ~10 |
| Persian / seasonal | ~40 (self-authored) |
> ⚠️ The critic's verdict: this "≥100 characters" is **permutation-inflated**. The defensible
> count of distinct, named, ledgered figures from the firewall-clean CC0/MIT tier is **~5060**,
> and **0 are riggable today** (all need manual `<g>`-splitting). §12 gives a concrete plan to a
> real, enumerated 100-figure roster.
---
## 1. Characters — Riggable (separable SVG parts) ⭐ core tier
Individual `<g>` body parts can be driven by `useCurrentFrame()` / `interpolate()` — true per-limb rigging, not just whole-figure parallax.
### 1.1 Humaaans (Pablo Stanley) — FLAGSHIP
- **License:** CC0 1.0 Public Domain (commercial OK, no attribution, redistribution/vendoring OK).
- **Iran-access:** acquisition **yes** (humaaans.com / Figma Community / archive.org). **Do NOT use Gumroad.** Render-time is CDN-free once vendored.
- **Animation-fit:** riggable-parts — **realistically 78/10**. Parts are separable as *components in the Figma/Sketch source*; a flat one-shot SVG export flattens groups, so you must export each body part as its own `<g>` first.
- **Count:** ~100 base components (≈12 base poses + dozens of heads/hair/tops/bottoms/shoes + objects).
- **Style-match:** **9/10** — THE flagship Alegria reference.
- **Download:** Figma Community file `1200350623263197299` → export per-part SVGs; or humaaans.com; `archive.org/details/humaaans` fallback.
- **Use-cases:** full-body hero rigging; per-limb wave/present/think/celebrate poses (replaces the hand-coded `Pose` system in `CharacterStory.tsx`).
### 1.2 Open Peeps (Pablo Stanley) — secondary
- **License:** CC0 1.0 (no attribution, redistribution OK).
- **Iran-access:** acquisition yes. **Animation-fit:** riggable-parts — `github.com/opeepsfun/open-peeps` exposes separated body/head/face/beard/accessory SVGs (npm wrapper is MIT; **vendor the raw CC0 SVGs, not the wrapper**).
- **Count:** ~200 building-block parts. **Style-match 7/10** (confident hand-drawn line; lighter colour-blocking). Character-only — pair with §3 scenes.
- ⚠️ **Do NOT confuse with Avataaars** (same artist, NOT CC0).
### 1.3 DiceBear (programmatic, offline) — build-time avatars
- **License:** core MIT; per-style — `open-peeps`=CC0, `avataaars`=free-commercial custom, `big-smile`/`adventurer`/`croodles`=**CC BY 4.0 (attribution REQUIRED)**.
- **Iran-access:** **yes**`@dicebear/core` + `@dicebear/collection` run **fully offline via npm** (Nexus mirror). `createAvatar(style,{seed}).toString()` → raw SVG, no runtime CDN. Do NOT call the dicebear HTTP API at render.
- **Animation-fit:** component-swap + transforms (blink/expression micro-rigs), not full skeletal rig. Bust→half-body scale.
- **Count:** ~45 styles. **Style-match 6/10.** Use `idRandomization` to avoid SVG id collisions.
### 1.4 IRA Design (Creative Tim) — selective gradient style
- **License:** **MIT** (`ira-design/ira-illustrations`). **Retain LICENSE.md** alongside vendored SVGs (no on-screen credit needed).
- **Iran-access:** yes (`git clone`). **Animation-fit:** moderate — monolithic per-illustration files, manual `<g>` split needed; gradient fills complicate flat parallax.
- **Count:** ~100 vendorable SVGs (36 characters + 52 objects + backgrounds). **Style-match 6/10** (gradient sketch, not strict flat-block).
### 1.5 Avataaars (Pablo Stanley) — ⚠️ caution
- **License:** code ports MIT, but **artwork is custom "free for personal & commercial use", NOT MIT/CC0** — no redistribution "in a pack", no "use to create a competing service" (a live risk for an avatar SaaS).
- **Verdict:** talking-head micro-animation **only** (via DiceBear `avataaars` offline npm); document as "permissive-with-restrictions"; do NOT build a user-facing avatar generator on it.
---
## 2. Characters — Lottie / Animated
> **Strategic note:** every third-party Lottie library carries a redistribution and/or
> competing-service clause that collides with a paid template SaaS vendoring assets into
> `public/`. **Primary path = SELF-AUTHOR brand Lottie loops** from the CC0 SVGs in §1/§3
> and drive them with `@remotion/lottie`.
- **2.1 LottieFiles (free/community)** — ⚠️ Simple License (commercial OK) but **no "compile files to develop a competing service"**, no standalone JSON redistribution; per-file Free/Premium badge. Iran-access unknown (login-gated). Use only inside a final render, Free badge re-verified.
- **2.2 Lottielab** — ⚠️ free tier **watermarks all exports**; value is as an **authoring tool** on cleanly-licensed SVGs (Pro ~$12/mo for watermark-free).
- **2.3 LottieFiles creators/marketplace** — ⚠️ per-item; only **Free**-badged vendorable; Premium forbids redistribution even after purchase. Style-match up to 8/10 for the right creator.
---
## 3. Scenes & Interiors (recolorable flat backdrops — fixes the "naked scene")
> **The category that directly cures `CharacterStory.tsx`** — flat scene SVGs (rooms, desks, cities, devices, sky) used as BACKGROUND/MID-GROUND layers behind the riggable §1 characters; animation is whole-scene parallax/recolor.
- **3.1 unDraw** — ⚠️ **NOT MIT** (stale label) → custom proprietary (free commercial, no attribution) that **forbids replicating a competing service, distributing "in packs", scraping, AND AI/ML training**. ~1,300 scenes. **Style-match 23/10.** Hand-pick only (no bulk scrape), bake into renders, log `license="unDraw custom (proprietary)"`.
- **3.2 ManyPixels** — ⚠️ custom permissive but forbids "distribute in packs", being the product's center, AND AI-training. 2,500+. Iran-access yes (Iconduck/`quentincaffeino/manypixels-illustrations` mirror). **Style-match 4/10.** Curated subset baked in; never an in-product picker.
- **3.3 Lukasz Adam** — ⚠️ **CC0/MIT** (cleanest scene tier) but **acquisition NO via official Gumroad** (sanctioned) → Eagle/aggregator mirror or non-Iran box. **229 confirmed** (characters, remote-work, cities, devices). The free-static scene anchor.
- **3.4 Storyset (Freepik)** — ⚠️ **reference only** — resale/database clause + visible Freepik credit; Iran-access **NO** (OFAC checkout). Only attractive property: exports Lottie JSON.
- **3.5 DrawKit** — ⚠️ freemium proprietary, forbids standalone redistribution + AI-training, revocable terms. Use only if repo private + users get rendered output only.
- **3.6 Black Illustrations** — ⚠️ proprietary; forbids serving raw SVG / end-user extraction → **bake into MP4 only**. ~120 free. Valuable inclusive-character supplement.
- **3.7 Ouch! by Icons8** — ⚠️ **reference only** — forbids distributing as "stand-alone files" (conflicts with `public/`+`staticFile()`); Iran-access NO.
---
## 4. Devices / Props
- **Humaaans objects** (CC0) — laptops/phones/plants/chairs/signs, separable mid-ground props.
- **IRA Design objects** (MIT) — 52 objects; split manually.
- **Lukasz Adam "Devices"** (CC0) — phones/laptops/monitors; pairs with the `AppShowcase3D.tsx` `screenUrl` + `staticFile()` pattern.
- **Self-authored SVG props** — drawn inline in the same flat style (the repo already proves this works).
## 5. Sky / Nature / Abstract
- **Self-authored gradients & shapes** (CSS/SVG, already used in `CharacterStory.tsx`) — cleanest license-free sky/abstract layer; drift off `useCurrentFrame()`.
- **unDraw/ManyPixels nature** (curated, recolored).
- **Three.js abstract** via `@remotion/three` (installed) for parallax sky/particles depth.
## 6. Grain / Texture
- **Inline `feTurbulence` SVG noise** — ALREADY in `CharacterStory.tsx` (`mixBlendMode: overlay`, opacity 0.05). Keep as the standard grain.
- **CC0 paper/noise tiles** — vendor 510 (ambientCG / cc0textures, CC0).
## 7. Persian / Seasonal
- **Self-authored Nowruz (haft-sin, sabzeh, goldfish, sonbol), Yalda (pomegranate, watermelon, Hafez), Ramadan (crescent, fanoos)** flat SVG motifs — the repo already ships `Nowruz3D.tsx` / `NowruzGreeting.tsx`.
- **Recolored CC0 scenes** (Humaaans + Lukasz) dressed with seasonal props. Vazirmatn RTL already vendored.
---
## 8. DOWNLOAD MANIFEST
All assets land under `services/remotion/public/`. npm via the **Nexus mirror** (`mirror.soroushasadi.com`). For Gumroad/Freepik/Icons8/Cloudflare-gated sources, acquire from a **non-Iran box / VPN once**, then commit — render-time is CDN-free.
```bash
# 0. Folders
mkdir -p services/remotion/public/illustrations/{humaaans,openpeeps,undraw,manypixels,lukaszadam,ira,notion}
mkdir -p services/remotion/public/lottie
# 1. Humaaans (CC0) — FLAGSHIP. Figma community 1200350623263197299 → export each
# body-part as its own <g> SVG → public/illustrations/humaaans/ (NOT Gumroad)
# 2. Open Peeps (CC0) — pre-separated parts:
git clone https://github.com/opeepsfun/open-peeps # vendor raw CC0 SVGs, not the wrapper
# 3. DiceBear (offline, no runtime CDN) — generate at BUILD time:
npm i @dicebear/core @dicebear/collection # via Nexus mirror
# createAvatar(openPeeps,{seed}).toString() → write SVG to public/
# 4. IRA Design (MIT) — keep LICENSE.md alongside:
git clone https://github.com/ira-design/ira-illustrations
# 5. Lukasz Adam (CC0, 229) — Eagle/aggregator mirror or non-Iran box (Gumroad sanctioned)
# 6. unDraw / ManyPixels — HAND-PICK only (no bulk scrape); curated subset
# 7. Notion Avatars (CC0) — talking-head busts:
git clone https://github.com/notion-avatar/notion-avatar
# 8. Lottie pipeline (must add the package first):
npm i @remotion/lottie # via Nexus — currently MISSING
```
**Iran-mirror flags:** Gumroad (Lukasz/Open Peeps/Black Illustrations) = sanctioned. Freepik/Storyset + Icons8 = OFAC-blocked. Cloudflare (DrawKit/ManyPixels) = intermittent. All npm = Nexus mirror.
---
## 9. INTEGRATION PLAN
### 9.1 License firewall — `public/illustrations/assets.json`
Every vendored file gets one ledger row. **No row → asset may not ship.**
```jsonc
{
"humaaans/torso-a.svg": { "source": "Humaaans", "license": "CC0-1.0",
"license_class": "CC0", "commercial_ok": true, "attribution_required": false,
"ai_training_allowed": true, "url": "humaaans.com", "vendored": "2026-06-22" },
"ira/scene-desk.svg": { "source": "IRA Design", "license": "MIT",
"attribution_required": true, "notice_file": "ira/LICENSE.md", "ai_training_allowed": true },
"undraw/working.svg": { "source": "unDraw", "license": "unDraw custom (proprietary)",
"commercial_ok": true, "attribution_required": false,
"ai_training_allowed": false, "redistribute_as_pack": false }
}
```
Hard rules: **(1)** only CC0/MIT/PD vendored as raw `public/` SVG; **(2)** custom-restrictive (unDraw/ManyPixels/DrawKit/Black Illustrations) only as a **curated subset baked into renders**, `ai_training_allowed:false` (excluded from FlatRender's future AI-video pipeline); **(3)** Blush/Storyset/Icons8/Avataaars-pack = **never vendored**, reference only.
### 9.2 Rigging an SVG kit in Remotion (`useCurrentFrame()`-driven `<g>` parts)
The repo already proves the pattern in `CharacterStory.tsx` (`arm(ang,sign)` / `leg(sign,ang)` rotate `<g>` groups by frame-derived angles). Replace the hand-drawn paths with vendored Humaaans part SVGs, keep the rig math:
```tsx
const f = useCurrentFrame();
const wave = pose === "wave" ? 130 + Math.sin(f / 4) * 14 : 12;
<g transform={`translate(${shoulderX} ${shoulderY}) rotate(${wave})`}>
<UpperArm /> {/* vendored SVG <g>, pivot at shoulder */}
<g transform={`translate(0 ${forearmY}) rotate(${elbow})`}><Forearm /></g>
</g>
```
Keep conventions: `staticFile()` for raster, stable `rand(seed)` (not `Math.random`), per-instance `id()` prefixes (`cs${idn}_`) to avoid `<defs>` id collisions, `L.vmin()/L.pick()` for aspect re-flow.
### 9.3 `@remotion/lottie` (after `npm i @remotion/lottie`)
Vendor JSON under `public/lottie/`; never a CDN/HTTP API at render. Use for self-authored idle/confetti/sky loops; firewall third-party Premium JSON out.
### 9.4 LAYER scenes for depth — the cure for "naked scene" (do this in the Three.js 2.5D scene)
Stack layers back-to-front, each on its own plane / parallax speed off `useCurrentFrame()`:
```
Layer 0 SKY gradient / Three.js abstract (slow drift, far plane)
Layer 1 ROOM vendored interior SVG (wall, window) (slow)
Layer 2 FURNITURE desk / shelf / plant props (medium)
Layer 3 DEVICE laptop/phone (staticFile, AppShowcase pattern)
Layer 4 CHARACTER riggable Humaaans <g> rig (foreground, full motion)
Layer 5 GRAIN feTurbulence overlay (already in repo, opacity 0.05) + vignette
```
Parallax = depth factor (`0.1 … 1.0`) on each layer's translate / z-position. Direct upgrade of `CharacterStory.tsx`: "blob + particles + character" → "sky → room → furniture → device → character → grain".
### 9.5 Recolor to brand palette
Map `colorSchema` (`accentColor`/`secondaryColor`/`backgroundColor`/`textColor`) onto each scene SVG's fills. unDraw exposes one accent var; multi-shade SVGs (Lukasz/ManyPixels/Humaaans) need a small fill-remap at vendor time (`mixHex()` from `src/lib/anim.ts`).
---
## 10. PHASED BUILD PLAN (to Alegria quality)
- **Phase 0 — Plumbing (½ day).** `public/illustrations/*` + `public/lottie/`; `assets.json` ledger + a CI check that fails on un-ledgered files; `npm i @remotion/lottie`.
- **Phase 1 — Flagship character rig (23 days).** Vendor Humaaans parts; build `<RiggedCharacter>` (separable `<g>` arms/legs/head, pose presets) reusing the `CharacterStory.tsx` pose math.
- **Phase 2 — Layered scene system (23 days).** `<SceneStage>` composing sky→room→furniture→device→grain with parallax inside the Three.js scene. Vendor a curated ~30-scene set (Lukasz CC0 first, then hand-picked unDraw/ManyPixels). Rebuild `CharacterStory.tsx`'s 13 beats with real environments.
- **Phase 3 — Recolor + brand binding (12 days).** Wire `colorSchema` → scene fills; verify studio colour controls recolor the whole stack.
- **Phase 4 — Lottie & motion polish (2 days).** Self-author 810 idle/wave/confetti/sky loops; integrate via `@remotion/lottie`; per-limb secondary motion + foreshortening on the hero (expressive hands).
- **Phase 5 — Persian/seasonal pack (12 days).** Nowruz/Yalda/Ramadan prop sets on the layered scenes (reuse `Nowruz3D`).
- **Phase 6 — QA & firewall audit (1 day).** Render 3 aspects; confirm determinism, no runtime CDN, full `assets.json` coverage, AI-prohibited assets flagged out.
---
## 11. Verdict summary
| Source | Verdict | Vendor raw? | License | Iran (acquire) | Anim-fit | Style |
|---|---|---|---|---|---|---|
| Humaaans | **USE** ⭐ | ✅ | CC0 | yes | rig 78 | 9 |
| Open Peeps | **USE** | ✅ | CC0 | yes | rig | 7 |
| DiceBear (CC0/free) | **USE** | ✅ gen→commit | MIT+CC0 | yes (offline npm) | swap/transform | 6 |
| IRA Design | **USE** (selective) | ✅ +LICENSE.md | MIT | yes | moderate | 6 |
| Lukasz Adam | **USE** (mirror) | ✅ | CC0 | acquire NO (Gumroad) | single-svg | 4 |
| Notion Avatars | **USE** (add) | ✅ | CC0 | yes | bust micro-rig | 6 |
| unDraw | caution | curated/baked | custom-restrictive | unknown | single-svg | 23 |
| ManyPixels | caution | curated/baked | custom-permissive | yes | single-svg | 4 |
| Black Illustrations | caution | bake-into-MP4 | proprietary | unknown | single-svg | 4 |
| Avataaars (art) | caution | gen→commit, no pack | custom-permissive | yes | bust micro-rig | 6 |
| LottieFiles (free) | caution | inside-render only | Simple License | unknown | lottie | 56 |
| DrawKit | caution | private repo only | freemium-proprietary | unknown | lottie | 4 |
| Storyset | **reference only** | ❌ | resale-ban | NO | lottie | 5 |
| Ouch!/Icons8 | **reference only** | ❌ | no stand-alone files | NO | lottie | 5 |
| Blush | **reference only** | ❌ | proprietary | NO (Stripe) | rig (paid) | 9 |
**Bottom line:** Build the production library on the clean, vendorable CC0/MIT tier — **Humaaans, Open Peeps, DiceBear-CC0, IRA, Notion Avatars** + **Lukasz Adam** scenes; supplement scenery with **curated, baked-in** unDraw/ManyPixels; **self-author** Lottie loops; keep the encumbered/Iran-blocked sources (Blush/Storyset/Icons8/DrawKit) as **design-time reference only**.
---
## 12. ⚠️ Reality Check (completeness critic) — READ THIS
The "≥100 characters" in §0 does **not** survive scrutiny. Honest status and the fix:
**Gaps:**
1. **Count is permutation-inflated.** "Humaaans ~100 components" = interchangeable heads/hair/tops/bottoms, not 100 figures. Defensible distinct, named, firewall-clean figures ≈ **12 Humaaans poses + 36 IRA + a few DiceBear seeds ≈ 5060** — and those need manual `<g>`-splitting before riggable.
2. **Nothing is vendored yet.** `public/` contains only `fonts/`. `public/illustrations/` + `public/lottie/` don't exist. The catalog is an **acquisition plan**, not a library. **Riggable-today count = 0.**
3. **Riggability overstated.** A one-shot Humaaans SVG export flattens groups → until per-part `<g>` export + pivot authoring is done, it's single-figure parallax (same tier as unDraw).
4. **Diversity thin & encumbered.** The only dedicated inclusive source (Black Illustrations) is bake-into-MP4-only. No source covers older adults, disability, hijab/chador, or Persian dress beyond self-authored props.
5. **`@remotion/lottie` is a hard blocker** (absent from `package.json`) — every Lottie path (§2) is non-functional until added; don't count those ~5080 loops yet.
6. **Self-authored assets counted as if they exist** (Lottie ~5080, seasonal ~40, sky ~80, props ~150 are future work).
**Additions / the concrete fix — enumerate a real 100-figure roster (each an actual committed file + `assets.json` row):**
- **~30** Humaaans base poses × authored top/bottom/skin variants you actually create & ledger.
- **36** IRA Design character SVGs (MIT, `github.com/ira-design/ira-illustrations`).
- **30** DiceBear `open-peeps` (CC0) fixed seeds, generated + committed.
- **~10** Open Peeps part assemblies. → **~106 real, named, ledgered figures.**
- **+ Notion Avatars** (CC0, `github.com/notion-avatar/notion-avatar`) — ~1015 talking-head busts, firewall-clean, Iran-accessible. Fills the talking-head gap without Avataaars' restrictions.
- **+ Croodles / Adventurer** (DiceBear, **CC BY 4.0 — attribution ledger row**) — ~20 doodle-line seeds.
- **+ OpenMoji** people/gestures (CC BY-SA 4.0, `github.com/hfg-gmuend/openmoji`) — expressive hands/heads for secondary characters; curated subset + share-alike tracking.
- **Diversity pass:** author hijab/scarf head `<g>` overlays, older-adult variants, varied skin tones via `mixHex()` recolor on the **CC0 Humaaans base** (firewall-clean derivative; directly serves the Persian-first product).
- **Grain:** vendor 35 CC0 paper/noise tiles from **ambientCG** (CC0, GitHub/CDN-mirrorable).
- **`@remotion/lottie`** added in Phase 0 *before* any Lottie asset counts as usable.
- **Sky:** add Lukasz Adam CC0 nature SVGs + 23 self-authored gradient-sky presets bound to `colorSchema`.
**So the true near-term deliverable** is not "100 characters downloaded" but: Phase 0 plumbing + a vendored, ledgered **~3040 character starter set** (Humaaans + IRA + DiceBear + Notion) split into riggable `<g>` parts, plus a `<SceneStage>` layered-scene system — enough to rebuild `CharacterStory` to the Alegria bar — then grow the roster to 100+ on that proven pipeline.