hero-lineup

Community@machma1992/hero-lineup
Community skill — not reviewed. It was shared by Mach Ma and has not been checked by Neta. Read SKILL.md below before letting an agent use it.
Use it in Cohub

Paste this into your agent's chat — it fetches the skill and follows it.

Use the skill: hero-lineup. Run `npx -y @neta-art/neta-search-cli@latest skills use @machma1992/hero-lineup --via site` and follow what it prints. Read its complete output, redirecting it to a temporary file first if necessary, and resolve relative paths from the skill directory it prints.
Install from a terminal
npx -y @neta-art/neta-search-cli@latest skills use @machma1992/hero-lineup
Summary

Turn five of your characters into a match-loading lineup poster, then bring it to life as a short video.

  • Five characters pose side by side on glowing round pedestals, each ringed by a low fence of blue flames, on a polished fantasy stage.
  • You get the still first with a countdown dial on top, and nothing is animated until you approve it or ask for changes.
  • After you say yes, it becomes an 8-second video: heroes breathe and sway, then the center hero raises a weapon.
  • Optional countdown version: 5, 4, 3, 2, 1 with drum hits, then everyone moves into battle poses under a battle-start title.
  • Works best with five characters that have distinct looks and builds, plus a world with a clear mood to pick the stage.
SKILL.md
# hero-lineup · Five-Hero Lineup

Five characters in → **Step 1** one still of the five posing on portal-ring pedestals → **user confirms** → **Step 2** that still becomes a video.

Step 1 ends the turn. Never generate the video in the same turn as the still.

**Language.** Reply in the user's language. Text the user sees in the deliverable (caption, title) follows the user's language too. Generation prompts stay in English.

## Scope

- In scope: one five-hero lineup still, its countdown overlay, and the Step 2 video, made from characters the user names or that already exist in the world.
- Out of scope: do not change world settings (world config, visual style, existing materials). Do not delete existing files, Works or Board items — only add new ones. Do not edit the user's characters to make them fit; describe them in the spec instead.

## How this skill stays stable

Every run walks the same road. Do not improvise around it:

1. **One spec file drives every prompt.** All wording, coordinates and the reference order come from `assets/lineup.py`; never hand-write a generation prompt.
2. **The model never draws text.** The countdown ring, numeral and caption are always added afterwards by `assets/overlay.sh`; the model is told to leave the top of the frame empty.
3. **Every still passes the position check** (`assets/qa_sheet.py`) before the user sees it.
4. **A fixed retry ladder** (1.5). When the model keeps misplacing heroes, switch to the layered composite (route B), which places feet by coordinates and cannot miss.

`$A` below = this skill's `assets/` folder (the `assets` folder next to this SKILL.md; resolve it to an absolute path once). Work files for a run live in `/tmp/hero-lineup/<slug>/`. Commands that write into the world (`uploads/…`, `neta …`) run from the world root.

**Public URLs are cached for hours.** `cohub public upload` + `cohub public url` give the address to embed or pass to a model, but the CDN keeps whatever it first served at that address — including a 404. So never request a public URL before its upload finishes, and never reuse a public path for different content: every upload gets a new name (`lineup.jpg`, then `lineup-r2.jpg`, `lineup-r3.jpg` on revisions).

## Originality boundary

Inspired by MOBA match-loading screens; **only two elements are borrowed**:

1. **Portal-ring pedestal**: a round pedestal whose rim is a bright cyan-white energy ring with a thin segmented runic ring inside.
2. **Blue spirit-flame ring**: low blue flames burning all around the outer edge of the rim like a fence, ankle-to-knee high. **The disc center is empty**; the hero stands inside the ring of fire.

Everything else is original. Never use a purple nebula with big planet-rim arcs, a hexagon floor grid, the words "MATCH STARTS IN...", ability icons, or any existing game's heroes, logos or UI. Never write a game or company name in a prompt.

## Stages

Plates and their pedestal geometry live in `assets/stages.json` (2752×1536, 16:9). The plate image files ship in `$A`. The plate is always reference image #1; `lineup.py` uploads it to the current world's public folder (`hero-lineup/<file>`) the first time it is needed and reuses it afterwards — `python3 $A/lineup.py plate <stage id>` prints that URL.

| id | Name | Status | Look | Suits |
|---|---|---|---|---|
| `observatory` (default) | Celestial Observatory | ready | Obsidian terrace above a cloud sea, gold constellation floor, brass armillary rings, aurora | Fantasy, wuxia, ensemble casts, heroic factions |
| `mirror-lake` | Mirror Lake Sanctum | **needs rebuild** | Black mirror lake, ruined arches, full moon top-center, mist, fireflies | Period drama, tragedy, fate, night factions |
| `rift-hall` | Rift Throne Hall | **needs rebuild** | Floating obsidian shards, white-blue rift with god rays, amber braziers | Villains, apocalypse, sci-fi, final battles |

Pick the stage the user names; otherwise `observatory`, or a ready stage whose "Suits" clearly fits better — say why in one line. `lineup.py` refuses a stage that is not `ready`.

### Rebuilding a "needs rebuild" plate (one time, then it is ready)

Those plates still have flame columns rising from the disc centers. Only when a user picks one:

```bash
NETA_DEFAULT_IMAGE_MODEL=gemini-3.1-flash-image-preview node /mods/neta/neta generate image \
  --prompt "Keep this scene exactly: composition, camera, every object, light and color. Change only the five pedestals: remove the flame columns from the pedestal centers; each pedestal disc becomes clear, flat and empty, its rim a bright cyan-white portal ring with a thin segmented runic inner circle, and a continuous low ring of short cyan-blue spirit flames burns along the outer edge of every rim like a fence, ankle-to-knee high, slightly taller at the back. No characters, no text." \
  --work-title "Stage plate · <name> v2" --param aspect_ratio=16:9 --param image_size=2K \
  --image "$(python3 $A/lineup.py plate <id>)"
```

(`lineup.py plate` works for any stage, ready or not; only a spec refuses a stage that is not ready.) Download the result to `$A/stage-<id>-v2.jpg` (scale to 2752×1536 if it differs), run `python3 $A/qa_sheet.py $A/stage-<id>-v2.jpg <id> /tmp/plate-qa.jpg` and look: every cross must sit at a disc center, every disc empty with a flame ring. If a cross misses, correct that pedestal's `x`/`y` in `stages.json` (read the new position off the `qa_sheet` crops). Then set `file` to `stage-<id>-v2.jpg` and `status` to `ready`; the next `lineup.py` call uploads the new plate on its own. Redo once on failure; if it still fails, use `observatory` and say so.

### Custom stage (only when the user asks for one from their world)

Generate with the same pedestal language, then measure and register it — a plate without measured coordinates is not usable:

```bash
NETA_DEFAULT_IMAGE_MODEL=gemini-3.1-flash-image-preview node /mods/neta/neta generate image --prompt "An original hero-lineup stage called <name>: <floor, background, sky/ceiling, light sources, mood, palette>.
FIVE EMPTY PEDESTALS in a shallow V across the lower half: the largest front-center (disc center ~50% from left, ~84% from top), two medium ones slightly behind at ~24% and ~76% from left (~75% from top), two smaller ones further back at ~12% and ~88% from left (~66% from top). Each pedestal is a circular platform whose rim is a bright cyan-white portal ring with a thin segmented inner circle of faint runic glyphs, and a continuous low RING OF BLUE SPIRIT FLAMES burns along the outer edge of the rim all the way around, like a flaming fence: short dense cyan-blue flame tongues, ankle-to-knee height of a standing person, slightly taller at the back. NO flame in the middle of any pedestal: every disc center is clear, flat and empty. Nothing stands on the pedestals. The top quarter of the frame stays calm and empty.
Polished high-end 3D game cinematic render, volumetric light, rich depth, 16:9. No characters, no text, no logos, no UI, no watermark." \
  --work-title "Stage plate · <name>" --param aspect_ratio=16:9 --param image_size=2K
```

Download it as `$A/stage-<id>.jpg` (scale to 2752×1536 if it differs), copy the `observatory` entry in `stages.json` as `<id>`, fill `name/file/look/suits/light/ambient`, set `status: "ready"`, run `qa_sheet.py` and correct each pedestal's `x/y/r/h` until every cross sits at a disc center. No upload step: `lineup.py` hosts the plate from `$A` when it is first used.

## Step 1 · The still

### 1.1 Gather the five

Count rules:

- Exactly 5 → go.
- Fewer than 5 → ask once: fill from the world (name the candidates) or leave pedestals empty? Stop.
- More than 5 → ask once: which 5, and who takes the center? Stop.

One reference image per hero, highest priority first:

1. The character's card in the world: `node /mods/neta/neta find images "<name>"` — prefer a single full-body front view; otherwise the first image in the card's `media`.
2. An image the user uploaded this turn.
3. None → `ref: null`; the `look` line carries the whole design.

Reference rules (each one removes a known failure):

- **One character, one view per image.** A multi-view sheet makes the model draw the hero two or three times. Crop the front view out instead: `ffmpeg -i <sheet> -vf "crop=iw/3:ih:0:0" /tmp/hero-lineup/<slug>/ref<n>.png` (adjust to the panel), then `cohub public upload` it and use the URL `cohub public url` prints. Only if cropping is impossible keep the sheet and set `"sheet": true`.
- **Never use `@Name`** in these prompts — it attaches covers outside the fixed reference order. Always pass images explicitly through the spec.
- **Famous IP or real-looking people:** `look` describes signature traits (build, hair, outfit colors, props) and `name` may stay; if a generation is refused (1.5), replace each `name` with a short descriptive epithet ("the twin-braided swordswoman") and retry.

### 1.2 Write the spec

`/tmp/hero-lineup/<slug>/spec.json` — field reference at the top of `$A/lineup.py`. Heroes in pedestal order 1→5, left to right:

| # | Position | Best for |
|---|---|---|
| 1 | Far left, back, small | Agile, ranged, small |
| 2 | Left-center, slightly back, medium | Armored, warriors |
| **3** | **Center front, largest** | **Lead / captain / biggest body** (the user's pick, else the most iconic) |
| 4 | Right-center, slightly back, medium | Marksmen, mages |
| 5 | Far right, back, small | Assassins, support |

Per hero fill all of: `name`, `look` (8–20 words: build, hair, palette, outfit), `pose`, `item`, `ref`, `idle` (one small motion for Step 2), `battle` (only if the countdown variant is likely). Poses read as **ready and waiting**, never mid-attack, and no two repeat:

- Warrior: weapon planted or on the shoulder, cape hanging, chin up.
- Mage: one hand holding a glowing spell, the other relaxed.
- Marksman: gun or bow held diagonally across the chest, side-on.
- Assassin: half-crouch, reverse-grip blade, body low.
- Giant creature: wings half-folded, looking down ahead (keep wings within its own pedestal's width).

`style`: `3d` (default: one consistent stylized 3D look) · `keep` (each reference's own art style — when the user asks, or the world's `visualStyle` should rule) · `photoreal` (when the user asks for realism). `caption`: the user's exact text for under the countdown, else empty.

Validate: `python3 $A/lineup.py check <spec>` — fix every error it lists. (The first `check` in a world uploads the stage plate; that is expected.)

### 1.3 Route A · one-shot still (default)

```bash
D=/tmp/hero-lineup/<slug>; python3 $A/lineup.py still $D/spec.json > $D/still.txt
NETA_DEFAULT_IMAGE_MODEL=gemini-3.1-flash-image-preview node /mods/neta/neta generate image \
  --prompt "$(cat $D/still.txt)" --work-title "<title> · Lineup (clean)" \
  --param aspect_ratio=16:9 --param image_size=2K \
  $(python3 $A/lineup.py images $D/spec.json)
```

`photoreal` → `image_size=4K`. Synchronous: this turn needs the image.

### 1.4 Position check (every still, every time)

```bash
python3 $A/qa_sheet.py "<mediaUrl>" <stage id> $D/qa.jpg
```

Open `$D/qa.jpg`. **Pass only if all hold**:

1. Exactly five people; nobody duplicated (watch for a second copy of a hero from a multi-view reference).
2. Each hero's feet sit on their magenta cross, inside its box — not on the floor between pedestals, not on a neighbour's disc.
3. Flames are a low ring around the shins; no flame column in a disc center, no flames over a torso or face.
4. Each hero matches their reference/look: hair color, outfit palette, signature item.
5. No text, numbers, logos or UI anywhere; nothing in the top ~25% of the frame except sky/background (the countdown goes there).
6. The five share one style (for `3d` / `photoreal`).

### 1.5 Retry ladder (fixed)

| Result | Next action |
|---|---|
| Command error naming a fixable cause | Fix it, resubmit the same command once |
| `Generation returned no image URL`, or a refusal | Same prompt and images with `NETA_DEFAULT_IMAGE_MODEL=gpt-image-2.5-sunburst … --param size=2048x1152 --param quality=high` (no `aspect_ratio`/`image_size`; `photoreal` → `3840x2160`). Refused again → names to epithets (1.1) and one more `gpt-image-2.5-sunburst` try; then stop and explain |
| Check fails, first time | Re-run route A unchanged, once (it is a fresh roll) |
| Check fails again — or any time a hero lands off their pedestal twice | **Route B** |
| Route B fails its check | Stop: show the best result, name what is wrong, ask how to proceed |

Never patch single heroes with free-form edit prompts; that is what route B is for.

### 1.6 Route B · layered composite (guaranteed positions)

1. **Five cutouts**, one per hero (run them in parallel; skip none):

   ```bash
   python3 $A/lineup.py solo $D/spec.json <n> > $D/solo<n>.txt
   NETA_DEFAULT_IMAGE_MODEL=gpt-image-2.5-sunburst node /mods/neta/neta generate image \
     --prompt "$(cat $D/solo<n>.txt)" --work-title "<title> · Cutout <n>" \
     --param size=1024x1536 --param quality=high --param background=transparent --param output_format=png \
     [--image "<hero n ref>"]
   ```

   Check each: one character, whole body including feet and weapon, background really transparent. Reroll a bad one once. If transparency failed, remove the background with the BiRefNet model (it takes an image URL and writes a transparent image; it may crop to the figure, which `composite.py` trims anyway):

   ```bash
   cohub generate "remove background" -m birefnet-general --image "<cutout mediaUrl>" -o $D/cut<n>.webp
   ```

   Then pass `$D/cut<n>.webp` to the composite instead of the URL.
2. **Composite** (feet land exactly on the pedestal coordinates, nearer heroes overlap farther ones, the front flame tongues are laid back over the shins):

   ```bash
   python3 $A/composite.py <stage id> $D/composite.png <cutout1> <cutout2> <cutout3> <cutout4> <cutout5>
   # each cutout = URL or local path; "-" leaves a pedestal empty
   # a giant creature or a child too big/small: HEIGHTS="1,1,1.2,1,0.85" python3 $A/composite.py ...
   ```

3. **Blend pass** — unify light and contact without moving anything:

   ```bash
   cohub public upload $D/composite.png hero-lineup/runs/<slug>/composite.png
   CU=$(cohub public url hero-lineup/runs/<slug>/composite.png)
   python3 $A/lineup.py blend $D/spec.json > $D/blend.txt
   NETA_DEFAULT_IMAGE_MODEL=gemini-3.1-flash-image-preview node /mods/neta/neta generate image \
     --prompt "$(cat $D/blend.txt)" --work-title "<title> · Lineup (clean)" \
     --param aspect_ratio=16:9 --param image_size=2K --image "$CU"
   ```

4. Run the 1.4 check on the blend. If the blend moved a hero or changed a face, use the raw composite as the clean still instead: `mkdir -p uploads/hero-lineup && cp $D/composite.png uploads/hero-lineup/<slug>-composite.png`, then `node /mods/neta/neta work import image --file uploads/hero-lineup/<slug>-composite.png --prompt "<title> lineup" --work-title "<title> · Lineup (clean)"`.

### 1.7 Countdown overlay

The **clean still** (no ring, no text) is kept as the Step 2 first frame. The delivered still gets the countdown added by code (run from the world root):

```bash
curl -sL "<clean still url>" -o $D/clean.png
mkdir -p uploads/hero-lineup
$A/overlay.sh $D/clean.png uploads/hero-lineup/<slug>-lineup.jpg 3 "<caption or empty>"
node /mods/neta/neta work import image --file uploads/hero-lineup/<slug>-lineup.jpg \
  --prompt "<title> hero lineup with countdown" --work-title "<title> · Lineup"
cohub public upload uploads/hero-lineup/<slug>-lineup.jpg hero-lineup/runs/<slug>/lineup.jpg
cohub public url hero-lineup/runs/<slug>/lineup.jpg   # URL to embed
rm uploads/hero-lineup/<slug>-lineup.jpg   # only the temporary copy this run made; the Work keeps its own
```

Default: ring at top center, numeral "3", no caption. Heads reaching high → `RING_Y=0.09 RING_H=0.26`. On `mirror-lake` the moon is the dial: keep the default position, the ring sits on the moon.

### 1.8 Board

Place it on the Board the user is viewing, as the first column of a flow (the countdown version on top, the clean one under it):

```bash
S=/mods/neta/.agents/skills/board-ops/scripts
node $S/flow.mjs --board <board path> --flow "<title> · Lineup" --step "Lineup" --work <clean work path>
node $S/flow.mjs --board <board path> --flow "<title> · Lineup" --step "Lineup" --work <countdown work path>
```

`--work` takes only work paths printed by the commands. Cutouts and composites (route B) go into a step "Cutouts" before "Lineup". Revisions go into the same step; the newest sits on top. If that script is not present in this environment, skip the Board placement and say so in one line; the Works still exist.

### 1.9 Deliver and stop

The reply contains:

1. The countdown still, embedded from its public URL.
2. A hand-over card for each Work made this step: an interaction-components link whose target is the Work (`{"type":"work","workId":"<id>"}`, the id from the `generate` / `work import` output), on its own line.
3. A small table: pedestal → hero → pose; one line on the stage and why.
4. The Step 2 **motion plan** (2.1 table filled from the spec's `idle` lines, or the 2.4 timeline if they asked for the countdown).
5. One line asking for confirmation; any change to stage, positions, poses, text or style — just say.

**The turn ends here. No video.**

On a change request → edit only that field in the spec (one variable at a time), rerun from 1.3 (or only 1.7 for a caption/numeral change — no new generation needed), stop again.

## Step 2 · The video (only after confirmation)

### 2.1 Motion plan (default: 8 s idle loop)

| Time | Action & camera | Sound |
|---|---|---|
| 0–2s | A light pulse runs round all five rings, each flame ring flares and settles; stage ambience moves (`ambient` in `stages.json`); very slow push-in begins | Deep energy hum |
| 2–6s | Each hero does their `idle` motion (breathing, a flick of the weapon, a spinning orb, slow wing beats, cape and hair sway) | Orchestral swell, roaring cold flames |
| 6–8s | Center hero lifts their head and raises their weapon; all five rings flare (no white flash); everyone holds | One heavy impact hit |

The countdown ring and numeral are overlaid on the finished video and stay fixed. No dialogue, no new characters, nobody leaves their pedestal, no cuts.

### 2.2 Generate

Pick the model once, by the cast:

- **Realistic human faces, or recognizable famous-IP characters** → `kling-image-to-video` directly (Seedance blocks these as copyright/face content).
- **Everything else** → `seedance-2-0-fast`.

```bash
python3 $A/lineup.py idle $D/spec.json > $D/idle.txt
# Seedance
NETA_DEFAULT_VIDEO_MODEL=seedance-2-0-fast node /mods/neta/neta generate video --async --prompt "$(cat $D/idle.txt)" --work-title "<title> · Lineup Motion" \
  --image "first_frame=<CLEAN still url>" --param duration=8 --param ratio=adaptive --param resolution=720p
# Kling
NETA_DEFAULT_VIDEO_MODEL=kling-image-to-video node /mods/neta/neta generate video --async --prompt "$(cat $D/idle.txt)" \
  --work-title "<title> · Lineup Motion" --image "<CLEAN still url>" \
  --param duration=10 --param aspect_ratio=16:9 --param mode=pro \
  --param "negative_prompt=text, numbers, extra people, morphing faces, camera cut, white flash, flames covering people"
```

The first frame is always the **clean** still — never the one with the countdown.

Wait with `node /mods/neta/neta generate reconcile <taskRunId> --timeout-ms 600000 --interval-ms 30000` (downloads the result into the work when done). `GENERATION_PENDING` after that → say "still rendering"; the card finishes on its own; never resubmit.

When ready, add it to the flow, then overlay the countdown and deliver the overlaid cut:

```bash
node $S/flow.mjs --board <board path> --flow "<title> · Lineup" --step "Motion" --via "image to video" --work <video work path>
$A/overlay.sh "<video mediaPath>" $D/motion-final.mp4 3 "<caption or empty>"
cohub public upload $D/motion-final.mp4 hero-lineup/runs/<slug>/motion.mp4
cohub public url hero-lineup/runs/<slug>/motion.mp4   # URL to embed
```

Videos cannot be imported as works: place the final cut as a native video item to the right of the flow (`cohub boards inspect <board> --json` for the flow frame's bounds; then `jq -n --arg s "<URL>" '{items:{"<slug>-motion":{type:"video",position:{x:<right of frame + 80>,y:<frame top>},size:{width:640,height:360},props:{src:$s}}}}' | cohub boards apply <board> -i -`). Embed the URL with `<video>`, and hand over the raw motion Work with an interaction-components link card whose target is that Work.

### 2.3 Failures

| Situation | Fix |
|---|---|
| Submit error | Fix what it names, resubmit once |
| Seedance card `failed` on moderation (`OutputVideoSensitiveContentDetected` etc.) | Rerun on Kling (2.2), descriptive epithets instead of names |
| Kling `job stalled` | Resubmit once with `--target-work` |
| Messy motion, deformed bodies | One variable at a time: only center + two heroes move (others "breathe only"), then Seedance `--param seed=<n>` / Kling `--param cfg_scale=0.7` |
| Wants higher quality | `NETA_DEFAULT_VIDEO_MODEL=seedance-2-0`; mention it costs more |

### 2.4 Variant: 8 s continuous shot — 5→1 countdown, "battle begins", natural switch to battle poses (verified)

Use when the user wants a 5-4-3-2-1 countdown, one number per second on drum hits, a battle-start title and the heroes moving into battle poses. **One continuous shot: no white flash, no hard cut.** Countdown, title and drums are placed in post; the video model only animates the people.

**Timeline**: 0–5 s waiting + countdown → at 5.0 s the heroes start moving, the title pops, the impact hits → 5–8 s battle poses.

1. **First frame** = the confirmed clean still (its top is already empty).
2. **Last frame (battle)**: `python3 $A/lineup.py battle $D/spec.json > $D/battle.txt`, generate with gemini (`aspect_ratio=16:9`, same `image_size` as the still) and `--image "<clean still url>"`. Run the 1.4 check — wide lunges step out of the ring; reroll once if they do.
3. **Two Kling clips** (`kling-image-to-video`, `duration=5`, `aspect_ratio=16:9`, `mode=pro`, prompt includes `static locked-off camera`):
   - **Clip A**: clean still → 5 s of small waiting motions only (the `idle` lines).
   - Last frame of A: `ffmpeg -sseof -0.05 -i A.mp4 -frames:v 1 $D/last.png`, then `cohub public upload` it and take the URL from `cohub public url`.
   - **Clip B**: `--image <last.png url>` then `--image <battle frame url>` (start, end); prompt: "still waiting for the first second, then in one smooth motion of about 1.5 seconds everyone moves into their battle pose, then holds it with tense idle motion". B starts on A's last frame, so the join is invisible.
4. **Find the motion start `s`** in B: `ffmpeg -i B.mp4 -vf "fps=4,scale=480:-2,tile=5x4" -frames:v 1 $D/b-sheet.jpg` (frame k = k/4 s); usually 0.5–2.5 s, and it must be under 4.5 s.
5. **Compose**:

   ```bash
   $A/compose_countdown.sh A.mp4 B.mp4 <s> $D/final.mp4
   # heads reach high: RING_Y=0.068 RING_H=0.21 TITLE_W=0.30 $A/compose_countdown.sh ...
   # Chinese title: TITLE_IMG=$A/title-battle-start-zh.png $A/compose_countdown.sh ...
   # any other language: TITLE_IMG=<transparent PNG> $A/compose_countdown.sh ...
   ```

   Output = A[s:5] + B[0:s] + B[s:] (slowed if needed to fill 3 s) = 8 s, motion start exactly at 5.0 s. The title follows the user's language: `assets/title-battle-start-en.png` ("BATTLE BEGINS", the default) or `assets/title-battle-start-zh.png` ("战斗开始"). For another language make one in the same style: `cohub generate "Recreate this game title logo in exactly the same style, but the text reads exactly: <TITLE>. Isolated on a fully transparent background." -m gpt-image-2.5-sunburst --image $A/title-battle-start-en.png --param background=transparent --param output_format=png --param size=1536x512 --param quality=high -o $D/title.png`. Sound comes from `assets/countdown_sfx.py` (drums at 0–4 s, riser, impact at 5.0 s, brass + choir); clips without audio are handled.
   Verify: each number spans exactly 4 frames at fps=4; no jump around 5.0 s.
6. **Deliver** like 2.2: upload the final cut, native video item to the right of the flow; both frames and both clips go into flow steps; hand over the Works with interaction-components link cards.

## Rules

- Stop for confirmation between the steps; Step 2 executes the confirmed still and motion plan.
- Every prompt comes from `lineup.py` and a spec; every still passes the 1.4 check; failures follow the 1.5 ladder.
- The model never draws text or numbers; `overlay.sh` / `compose_countdown.sh` add them.
- The stage plate is always reference image #1; only `ready` stages are used.
- Flames are always a low ring along the rim with an empty center; heroes stand inside it.
- Only the portal-ring pedestal and the blue flame ring are borrowed; no game names in prompts.
- Generation prompts in English; reply in the user's language.
- Embed only CLI-returned or `cohub public url` URLs, never local paths; never reuse a public path for new content.
- Never change world settings or delete existing files, Works or Board items.