anima
Overview
One piece = a short on-brand motion artifact: an intro / teaser / announcement video, a single
animated illustration, an animated hero, a footage piece, or a multi-scene feature explainer -
the last of which may be built from screen recordings, from pure vector animation, or from both. HyperFrames makes the motion; anima makes it
look like PageFly. This is a design system, not a template library: the reusable thing is
not one index.html, it is three composition-independent layers - brand tokens
(references/brand.css, with shared identity in _pf-brand/), motion gu
(references/style-guide.md), and a kit of on-brand motion blocks (components/) - composed
freely per case on templates/canvas.html. The Page Checkup intro is the first worked example
(examples/before-after/). Same model as illustra (kit + freeform compose + style-guide +
governance), ported from static art to motion. anima and illustra stay separate skills -
anima CALLS illustra when a case needs a static PNG; neither absorbs the other.
Orchestrates HyperFrames - does not reinvent it
Video machinery (composition structure, timeline contract, scene transitions,
lint/inspect/validate, render): follow the hyperframes skill. This skill adds ONLY:
brand preset (references/brand.css), motion gu (references/style-guide.md), component
kit (components/).
No HyperFrames authoring rules are duplicated here (DRY - avoids drift on HF upgrades).
When to use
Producing a short on-brand PageFly motion piece:
- an intro / teaser / announcement ("New in PageFly: ..."),
- a single animated illustration (one concept animating),
- an animated hero (a hero section in motion),
- a footage piece: one on-brand title card cutting to one real recording, or
- a feature explainer:
intro -> [claim -> evidence] x N -> outro - animated beats that
each teach one thing, each followed by something that proves it. The evidence may be a
screen recording, an animated vector demo, or a mix - a fully-vector explainer with no
recording anywhere is the same type and the same grammar, it just skips the capture work.
This is the shape for a feature video on a landing page. Two worked examples, and they
differ in more than length: examples/loop-ground/ (14.8s, 5 beats, ONE continuous navy
ground, travelling clusters, loops) is the current reference - copy its joins and structure.
outputs/checkup-hero (13 scenes, 40.7s) is older, is local-only, alternates light and navy,
and closes with one uniform crossfade on all 12 joins; read it for the beat-pair idea, not
for its transitions.
The first three are 1600x900 seamless loops. The last two are usually linear - but a footage
piece or explainer can ALSO loop, by returning to frame 0's state instead of closing on a
veil. That is a separate structure with its own component and worked example
(components/loop-ground.html, examples/loop-ground/, 14.8s, seam measured at 48.6 dB).
Prefer it for anything that will autoplay on a landing page; a linear cut ending on a dead
frame is a choice, not a default.
A feature explainer is NOT a longer footage piece: its unit of composition is the beat pair
(a claim plus the evidence for it), so its structure, transition grammar and QA all differ.
Treating one as the other is what produced a context-free clip reel on the first attempt.
Motion grammar for any multi-scene piece - the handoff, the match cut, crossfade tiers -
lives in references/style-guide.md -> Transitions, and is independent of whether there is any
footage. Only if a real recording is involved, also read references/footage-pieces.md
first (the canvas then comes from the capture, not the kit) and cut with
scripts/solve-crop.py. Doing capture work by eye costs several full re-renders.
The case has clear beats: an intro hold, an optional before/after morph, a single-feature hold.
For an animated illustration / hero, the intro title-card (scene 1) is often dropped.
Not for: static marketing images (use illustra / feature-demo); a non-PF brand (swap
references/brand.css instead - the engine is brand-neutral, PF is just the default preset).
Prerequisites (two tiers)
- To render / re-skin a finished example: npm
hyperframes (via npx, auto-fetched on
first run) + Node >= 22 + FFmpeg. First action: run npx hyperframes doctor and report
any missing dependency before going further.
- To author a NEW piece: additionally the hyperframes skill family (authoring knowledge -
timeline contract, transitions, QA). Those skills are user-global at
~/.claude/skills/ and are
NOT bundled in this repo - cloning PageFly does not get you them. anima carries enough
PF convention to proceed without them, but it defers ALL machinery rules to them, so author
quality drops sharply if they are missing.
First-time setup on a new machine
Run once per machine. Safe to re-run - all steps are idempotent.
npx hyperframes doctor # 1. Node >= 22, FFmpeg, Chrome, memory
cd /tmp && npx hyperframes init hf-bootstrap \
--example blank --non-interactive # 2. installs the HF skill family
ls ~/.claude/skills/hyperframes # 3. verify: SKILL.md + references/
rm -rf /tmp/hf-bootstrap # 4. throwaway scaffold, delete it
Notes:
- There is no dedicated "install skills" command.
init scaffolds a project and installs the
skills - per its own --skip-skills help text, "init always checks AI skills against GitHub"
(verified on CLI v0.7.108; HYPERFRAMES_SKIP_SKILLS=1 opts out). Hence the throwaway dir.
Do NOT run init inside this repo - it would scatter a scaffold into the skills tree.
init takes a project NAME, not a path - cd to the parent dir first, as above.
- It installs ~15 skills in one batch:
hyperframes, hyperframes-cli, hyperframes-media,
hyperframes-registry, plus the animation-library adapters (gsap, animejs, css-animations,
lottie, three, typegpu, waapi, tailwind) and website-to-hyperframes.
- HyperFrames is not a Claude plugin - do not look for it in a marketplace.
- Rendering needs network. GSAP is loaded from
cdn.jsdelivr.net (pinned 3.14.2) in
templates/canvas.html and every example, matching the upstream hyperframes pattern. Offline
renders fail.
Workflow
Intake the intent (ask first, then plan). Before generating, ask the member a short
brief - what kind (teaser / animated illustration / animated hero), which scenes / beats,
layout, transitions - then show a one-paragraph plan to OK. If they say "just try it",
skip the brief and proceed. The intro title-card (scene 1) is optional - drop it for an
animated illustration / hero unless the case wants the "New in PageFly" framing.
Two questions belong in EVERY intake, because getting them wrong invalidates the whole piece
rather than one line (both learned the hard way on checkup-hero):
- Which claims are durable? Never build a piece around pricing ("free", "$X/mo") - it is
the most likely thing to change. Lead on what the product does.
- When a member cites a reference video, say WHY it works before copying its shape. A
sparse-UI product can cut straight into raw footage; a dense one cannot, and the member is
buying the outcome, not the structure.
Understand the case -> the message + which beats it needs (intro? before/after morph?
single feature?). State it in one sentence: what is new and why it matters.
Start from a stage. Freeform new case -> copy templates/canvas.html (blank on-brand
1600x900 stage, brand.css inlined, loop veil wired inert). A before/after -> start from
examples/before-after/ instead. Save the working file as index.html in the case's own
dir (the HF CLI runs a dir's index.html; keep the committed canvas.html pristine).
Pick blocks from components/ (index: references/component-catalog.md). Paste each
block's markup into a scene and its <!-- Timeline recipe --> into the single tl timeline,
rebasing offsets to the scene's enter time. Note any bespoke part you must build. Blocks that
show assets (logo-lockup, before-after-card) expect an assets/ dir beside your
index.html - create it and copy the PF logo from examples/before-after/assets/pf-logo.svg,
plus your own screenshot crops.
Apply the gu (references/style-guide.md): eases (vary >= 3 per scene), breathing glows,
the loop-seam veil, pacing / beat anchors. Labels, eyebrows, and badges follow the shared kit
(_pf-brand/label-rules.md + _pf-brand/badges.html) - Poppins, sentence case, soft badges via
var(--badge-*); never mono-caps eyebrows or metallic pills. Defer structural and timeline
rules to the hyperframes skill.
Get framing + copy approved BEFORE the first render. A render is ~2.5 minutes and every
framing or copy miss costs a whole one (checkup-hero burned five). Show the member (a) a
contact sheet of the first frame of every cut and (b) the copy list - headline + subhead per
scene. This is where essentially every rebuild originates.
QA: npx hyperframes lint && npx hyperframes validate && npx hyperframes inspect. Fix
overflow, contrast, and H.264 banding. Use npx hyperframes inspect --at 4.6,5.3 to pin
specific beats.
For a footage piece also run python3 scripts/solve-crop.py verify <cut>.mp4 on every cut
before composing - contact sheets missed a leaked browser tab strip and a partial border twice.
Render: npx hyperframes render. Then harvest reusable parts - run the governance
scan below.
Hard rules
Mirror references/style-guide.md; defer the full video-machinery list to the hyperframes skill.
- Brand vars only. Never hardcode a brand color / radius / shadow - always
var(--...);
derive alpha tints from solid tokens via color-mix(in srgb, var(--token) N%, transparent)
(glow tokens ship pre-baked), so swapping brand.css rebrands. Literal exceptions (per
style-guide): fonts "Poppins" / "JetBrains Mono" (the
renderer cannot resolve var(--font-*)); GSAP color tweens; SVG stroke; neutrals.
- No jump cuts - but "opaque scene" is a rule about CROSSFADES. Scene-to-scene is a blur
crossfade over an opaque
--bg (a hyperframes hard rule), and where you crossfade, each card
scene must own its ground: a transparent scene over a shared bed makes the crossfade stack two
scenes and read muddy. If nothing crossfades, that requirement lifts. A piece whose beats
TRAVEL (clusters translating over one continuous ground, footage sliding in with a hard edge)
may and should share a single ground - it is the only way glows can carry across a beat
boundary instead of restarting at it. Worked reference: components/loop-ground.html,
examples/loop-ground/. Check "does anything here crossfade?" before reusing it.
Ground choice: the kit ground is navy; the light Layer B exists for pieces that dissolve
into a light app UI (see references/footage-pieces.md -> 5, which is scoped to dissolves).
- Exits are banned only when they would break the seam - which is not the same as "loop".
In a veil-closed loop (teaser / illustration / hero) animate IN every element and keep exit
tweens on the loop tail, because the tail is what makes frame N == frame 0. In every other
piece - linear, AND a loop that closes by returning to frame 0's STATE rather than by a veil -
exits are required: the strongest join is a handoff, where the outgoing element travels off
toward the point the next one launches from, so the eye is never asked to re-find the subject
(
style-guide.md -> Transitions -> The handoff). A cluster must never travel as one rigid
block; stagger its children (style-guide.md -> Overlap / follow-through).
What stays banned everywhere is the decorative fade-out that exists only to clear the screen.
- No emoji. Iconography is hand-drawn - CSS dots, a drawn SVG check path, a green pill -
never an emoji glyph.
- Deterministic. No
Math.random() / Date.now(); finite repeats only (never
repeat: -1) - renders must be reproducible.
- A seamless loop has TWO recipes, and a footage piece can use the second one.
(a) Veil close - teaser / illustration / hero: glows start hidden and
#loopveil fades to
the ground colour near T-end, so the final frame == frame 0.
(b) Return to frame 0's state - no veil at all: open on a static card, take it away, bring
it back untouched, and land every travelling thing (glows especially) back on its opening
arrangement. This is what lets a footage piece loop, which the kit previously said was
impossible. outputs/checkup-15s does it at 48.6 dB PSNR between first and last frame.
Prove it with ffmpeg -lavfi psnr, never by eye - see footage-pieces.md -> 10. Expect >40 dB;
anything lower means something really moved.
A piece that is genuinely one-shot may still just end, but say so deliberately.
File map
| Path |
Role |
.claude/skills/_pf-brand/ |
Source of truth for brand identity tokens + badge palette + label rules (brand-identity.css, badges.html, label-rules.md). It is a SIBLING skill dir, not a subdir of anima - every bare _pf-brand/ in this skill resolves to this path. Layer A + badges are synced from here into references/brand.css. Consumed by anima today; illustra still keeps its own fork at illustra/references/brand.css and does NOT read this file. |
references/brand.css |
Brand preset: Layer A identity + badge palette (synced from _pf-brand/) + Layer B video surface palette. Swap to rebrand. |
references/style-guide.md |
Motion gu: pacing, eases, glow, loop-seam, transitions, labels / badges, do/don't. Training ledger - append every refine. |
references/footage-pieces.md |
Read before any piece using a real recording. Intake questions, explainer-beat structure, the no-partial-border framing rule, capture de-chroming, light palette, pre-render approval. |
scripts/solve-crop.py |
solve a border-free crop for a cut; verify a cut has no window backdrop / browser tab strip on any edge. |
references/component-catalog.md |
Kit index + promotion governance + built-parts log. |
components/*.html |
Self-contained motion blocks (pf- prefixed): leading comment + scoped <style> + markup + a <!-- Timeline recipe -->. Paste into the case timeline. |
templates/canvas.html |
Blank on-brand 1600x900 HF stage; brand.css inlined, loop veil ready. Start here for a freeform case. |
examples/before-after/ |
Page Checkup intro = worked example, parametrized via data-composition-variables. Start here for a before/after. |
examples/loop-ground/ |
The current reference for anything with footage in it. A 14.8s explainer that LOOPS (48.6 dB seam, no veil): one continuous ground, travelling glows, clusters that stagger, footage that slides. Its index.html header records five traps that each cost a render - two of which pass lint, check and render while being visibly broken. |
Rendering is npx hyperframes render directly - no bespoke render script (unlike illustra's
render.mjs for Playwright PNGs). scripts/solve-crop.py is a measuring tool, not a renderer.
Governance - the training model ("don't redo it each time")
Every refine compounds into the shared layers, never into a throwaway file:
- tweak a brand color / glow ->
references/brand.css (identity + badge palette live in _pf-brand/)
- change a label / badge taste ->
_pf-brand/label-rules.md + _pf-brand/badges.html
- lock a new ease / pacing / technique ("modern PF") ->
references/style-guide.md
- a block hand-built a second time -> promote it into
components/ via suggest + approve.
- a footage / capture lesson ->
references/footage-pieces.md
After each piece, scan what you hand-built against 3 criteria:
- Reusable - a generic motion concept, not specific to this case's content. ("Would I
plausibly build this again for a different feature?")
- Self-contained & parameterizable - own markup + scoped CSS + a clean timeline recipe;
accepts text / color / size.
- On-brand & clean - var-only, follows the style-guide.
All 3 -> PROPOSE promoting it: a new components/<name>.html + a component-catalog.md
built-parts row (yes/no to the member; never auto-add, never ask them to judge technicals).
Strong signal: a part built a 2nd time almost always promotes. Keep the kit lean (YAGNI) -
do not seed a part until a real case needs it. Each use thickens kit + gu, so the next piece is
less work. Default gate = suggest + approve.
1---2name: anima3description: anima4---56# anima78## Overview9One piece = a short on-brand motion artifact: an intro / teaser / announcement video, a single10animated illustration, an animated hero, a footage piece, or a multi-scene feature explainer -11the last of which may be built from screen recordings, from pure vector animation, or from both. **HyperFrames makes the motion; anima makes it12look like PageFly.** This is a **design system, not a template library**: the reusable thing is13not one `index.html`, it is three composition-independent layers - brand tokens14(`references/brand.css`, with shared identity in `_pf-brand/`), motion gu15(`references/style-guide.md`), and a kit of on-brand motion blocks (`components/`) - composed16freely per case on `templates/canvas.html`. The Page Checkup intro is the first worked example17(`examples/before-after/`). Same model as `illustra` (kit + freeform compose + style-guide +18governance), ported from static art to motion. anima and illustra stay separate skills -19anima CALLS illustra when a case needs a static PNG; neither absorbs the other.2021## Orchestrates HyperFrames - does not reinvent it22> Video machinery (composition structure, timeline contract, scene transitions,23> lint/inspect/validate, render): follow the **hyperframes** skill. This skill adds ONLY:24> brand preset (`references/brand.css`), motion gu (`references/style-guide.md`), component25> kit (`components/`).2627No HyperFrames authoring rules are duplicated here (DRY - avoids drift on HF upgrades).2829## When to use30- Producing a short on-brand PageFly motion piece:31 - an **intro / teaser / announcement** ("New in PageFly: ..."),32 - a single **animated illustration** (one concept animating),33 - an **animated hero** (a hero section in motion),34 - a **footage piece**: one on-brand title card cutting to one real recording, or35 - a **feature explainer**: `intro -> [claim -> evidence] x N -> outro` - animated beats that36 each *teach* one thing, each followed by something that *proves* it. **The evidence may be a37 screen recording, an animated vector demo, or a mix** - a fully-vector explainer with no38 recording anywhere is the same type and the same grammar, it just skips the capture work.39 This is the shape for a feature video on a landing page. Two worked examples, and they40 differ in more than length: `examples/loop-ground/` (14.8s, 5 beats, ONE continuous navy41 ground, travelling clusters, loops) is the current reference - copy its joins and structure.42 `outputs/checkup-hero` (13 scenes, 40.7s) is older, is local-only, alternates light and navy,43 and closes with one uniform crossfade on all 12 joins; read it for the beat-pair idea, not44 for its transitions.4546 The first three are 1600x900 seamless loops. The last two are usually linear - **but a footage47 piece or explainer can ALSO loop**, by returning to frame 0's state instead of closing on a48 veil. That is a separate structure with its own component and worked example49 (`components/loop-ground.html`, `examples/loop-ground/`, 14.8s, seam measured at 48.6 dB).50 Prefer it for anything that will autoplay on a landing page; a linear cut ending on a dead51 frame is a choice, not a default.5253 A feature explainer is NOT a longer footage piece: its unit of composition is the **beat pair**54 (a claim plus the evidence for it), so its structure, transition grammar and QA all differ.55 Treating one as the other is what produced a context-free clip reel on the first attempt.5657 **Motion grammar for any multi-scene piece** - the handoff, the match cut, crossfade tiers -58 lives in `references/style-guide.md` -> Transitions, and is independent of whether there is any59 footage. **Only if a real recording is involved**, also read `references/footage-pieces.md`60 first (the canvas then comes from the capture, not the kit) and cut with61 `scripts/solve-crop.py`. Doing capture work by eye costs several full re-renders.62- The case has clear beats: an intro hold, an optional before/after morph, a single-feature hold.63 For an animated illustration / hero, the intro title-card (scene 1) is often dropped.6465**Not for:** static marketing images (use `illustra` / `feature-demo`); a non-PF brand (swap66`references/brand.css` instead - the engine is brand-neutral, PF is just the default preset).6768## Prerequisites (two tiers)69- **To render / re-skin a finished example:** npm `hyperframes` (via `npx`, auto-fetched on70 first run) + Node >= 22 + FFmpeg. **First action:** run `npx hyperframes doctor` and report71 any missing dependency before going further.72- **To author a NEW piece:** additionally the **hyperframes skill family** (authoring knowledge -73 timeline contract, transitions, QA). Those skills are user-global at `~/.claude/skills/` and are74 **NOT bundled in this repo** - cloning PageFly does not get you them. anima carries enough75 PF convention to proceed without them, but it defers ALL machinery rules to them, so author76 quality drops sharply if they are missing.7778### First-time setup on a new machine79Run once per machine. Safe to re-run - all steps are idempotent.8081```bash82npx hyperframes doctor # 1. Node >= 22, FFmpeg, Chrome, memory83cd /tmp && npx hyperframes init hf-bootstrap \84 --example blank --non-interactive # 2. installs the HF skill family85ls ~/.claude/skills/hyperframes # 3. verify: SKILL.md + references/86rm -rf /tmp/hf-bootstrap # 4. throwaway scaffold, delete it87```8889Notes:90- There is no dedicated "install skills" command. `init` scaffolds a project **and** installs the91 skills - per its own `--skip-skills` help text, *"init always checks AI skills against GitHub"*92 (verified on CLI v0.7.108; `HYPERFRAMES_SKIP_SKILLS=1` opts out). Hence the throwaway dir.93 Do NOT run `init` inside this repo - it would scatter a scaffold into the skills tree.94- `init` takes a project NAME, not a path - `cd` to the parent dir first, as above.95- It installs ~15 skills in one batch: `hyperframes`, `hyperframes-cli`, `hyperframes-media`,96 `hyperframes-registry`, plus the animation-library adapters (`gsap`, `animejs`, `css-animations`,97 `lottie`, `three`, `typegpu`, `waapi`, `tailwind`) and `website-to-hyperframes`.98- HyperFrames is **not** a Claude plugin - do not look for it in a marketplace.99- **Rendering needs network.** GSAP is loaded from `cdn.jsdelivr.net` (pinned 3.14.2) in100 `templates/canvas.html` and every example, matching the upstream hyperframes pattern. Offline101 renders fail.102103## Workflow1041. **Intake the intent (ask first, then plan).** Before generating, ask the member a short105 brief - *what kind* (teaser / animated illustration / animated hero), *which scenes / beats*,106 *layout*, *transitions* - then show a one-paragraph plan to OK. If they say "just try it",107 skip the brief and proceed. The **intro title-card (scene 1) is optional** - drop it for an108 animated illustration / hero unless the case wants the "New in PageFly" framing.109110 Two questions belong in EVERY intake, because getting them wrong invalidates the whole piece111 rather than one line (both learned the hard way on `checkup-hero`):112 - **Which claims are durable?** Never build a piece around pricing ("free", "$X/mo") - it is113 the most likely thing to change. Lead on what the product does.114 - **When a member cites a reference video, say WHY it works before copying its shape.** A115 sparse-UI product can cut straight into raw footage; a dense one cannot, and the member is116 buying the outcome, not the structure.1172. **Understand the case** -> the message + which beats it needs (intro? before/after morph?118 single feature?). State it in one sentence: what is new and why it matters.1193. **Start from a stage.** Freeform new case -> copy `templates/canvas.html` (blank on-brand120 1600x900 stage, brand.css inlined, loop veil wired inert). A before/after -> start from121 `examples/before-after/` instead. Save the working file as **`index.html`** in the case's own122 dir (the HF CLI runs a dir's `index.html`; keep the committed `canvas.html` pristine).1234. **Pick blocks** from `components/` (index: `references/component-catalog.md`). Paste each124 block's markup into a scene and its `<!-- Timeline recipe -->` into the single `tl` timeline,125 rebasing offsets to the scene's enter time. Note any bespoke part you must build. Blocks that126 show assets (`logo-lockup`, `before-after-card`) expect an `assets/` dir beside your127 `index.html` - create it and copy the PF logo from `examples/before-after/assets/pf-logo.svg`,128 plus your own screenshot crops.1295. **Apply the gu** (`references/style-guide.md`): eases (vary >= 3 per scene), breathing glows,130 the loop-seam veil, pacing / beat anchors. Labels, eyebrows, and badges follow the shared kit131 (`_pf-brand/label-rules.md` + `_pf-brand/badges.html`) - Poppins, sentence case, soft badges via132 `var(--badge-*)`; never mono-caps eyebrows or metallic pills. Defer structural and timeline133 rules to the hyperframes skill.1346. **Get framing + copy approved BEFORE the first render.** A render is ~2.5 minutes and every135 framing or copy miss costs a whole one (`checkup-hero` burned five). Show the member (a) a136 contact sheet of the first frame of every cut and (b) the copy list - headline + subhead per137 scene. This is where essentially every rebuild originates.1387. **QA:** `npx hyperframes lint && npx hyperframes validate && npx hyperframes inspect`. Fix139 overflow, contrast, and H.264 banding. Use `npx hyperframes inspect --at 4.6,5.3` to pin140 specific beats.141 For a footage piece also run `python3 scripts/solve-crop.py verify <cut>.mp4` on **every cut**142 before composing - contact sheets missed a leaked browser tab strip and a partial border twice.1438. **Render:** `npx hyperframes render`. Then **harvest** reusable parts - run the governance144 scan below.145146## Hard rules147Mirror `references/style-guide.md`; defer the full video-machinery list to the hyperframes skill.1481. **Brand vars only.** Never hardcode a brand color / radius / shadow - always `var(--...)`;149 derive alpha tints from solid tokens via `color-mix(in srgb, var(--token) N%, transparent)`150 (glow tokens ship pre-baked), so swapping `brand.css` rebrands. Literal exceptions (per151 style-guide): fonts `"Poppins"` / `"JetBrains Mono"` (the152 renderer cannot resolve `var(--font-*)`); GSAP color tweens; SVG `stroke`; neutrals.1532. **No jump cuts - but "opaque scene" is a rule about CROSSFADES.** Scene-to-scene is a blur154 crossfade over an opaque `--bg` (a hyperframes hard rule), and where you crossfade, each card155 scene must own its ground: a transparent scene over a shared bed makes the crossfade stack two156 scenes and read muddy. **If nothing crossfades, that requirement lifts.** A piece whose beats157 TRAVEL (clusters translating over one continuous ground, footage sliding in with a hard edge)158 may and should share a single ground - it is the only way glows can carry across a beat159 boundary instead of restarting at it. Worked reference: `components/loop-ground.html`,160 `examples/loop-ground/`. Check "does anything here crossfade?" before reusing it.161 Ground choice: the kit ground is navy; the light Layer B exists for pieces that *dissolve*162 into a light app UI (see `references/footage-pieces.md` -> 5, which is scoped to dissolves).1633. **Exits are banned only when they would break the seam - which is not the same as "loop".**164 In a **veil-closed loop** (teaser / illustration / hero) animate IN every element and keep exit165 tweens on the loop tail, because the tail is what makes frame N == frame 0. In **every other166 piece - linear, AND a loop that closes by returning to frame 0's STATE rather than by a veil -167 exits are required**: the strongest join is a *handoff*, where the outgoing element travels off168 toward the point the next one launches from, so the eye is never asked to re-find the subject169 (`style-guide.md` -> Transitions -> The handoff). A cluster must never travel as one rigid170 block; stagger its children (`style-guide.md` -> Overlap / follow-through).171 What stays banned everywhere is the *decorative* fade-out that exists only to clear the screen.1724. **No emoji.** Iconography is hand-drawn - CSS dots, a drawn SVG check path, a green pill -173 never an emoji glyph.1745. **Deterministic.** No `Math.random()` / `Date.now()`; finite repeats only (never175 `repeat: -1`) - renders must be reproducible.1766. **A seamless loop has TWO recipes, and a footage piece can use the second one.**177 (a) **Veil close** - teaser / illustration / hero: glows start hidden and `#loopveil` fades to178 the ground colour near T-end, so the final frame == frame 0.179 (b) **Return to frame 0's state** - no veil at all: open on a static card, take it away, bring180 it back untouched, and land every travelling thing (glows especially) back on its opening181 arrangement. This is what lets a **footage piece loop**, which the kit previously said was182 impossible. `outputs/checkup-15s` does it at **48.6 dB PSNR** between first and last frame.183 Prove it with `ffmpeg -lavfi psnr`, never by eye - see `footage-pieces.md` -> 10. Expect >40 dB;184 anything lower means something really moved.185 A piece that is genuinely one-shot may still just end, but say so deliberately.186187## File map188| Path | Role |189|---|---|190| `.claude/skills/_pf-brand/` | **Source of truth** for brand identity tokens + badge palette + label rules (`brand-identity.css`, `badges.html`, `label-rules.md`). It is a SIBLING skill dir, not a subdir of anima - every bare `_pf-brand/` in this skill resolves to this path. Layer A + badges are synced from here into `references/brand.css`. Consumed by anima today; illustra still keeps its own fork at `illustra/references/brand.css` and does NOT read this file. |191| `references/brand.css` | Brand preset: Layer A identity + badge palette (synced from `_pf-brand/`) + Layer B video surface palette. **Swap to rebrand.** |192| `references/style-guide.md` | Motion gu: pacing, eases, glow, loop-seam, transitions, labels / badges, do/don't. **Training ledger** - append every refine. |193| `references/footage-pieces.md` | **Read before any piece using a real recording.** Intake questions, explainer-beat structure, the no-partial-border framing rule, capture de-chroming, light palette, pre-render approval. |194| `scripts/solve-crop.py` | `solve` a border-free crop for a cut; `verify` a cut has no window backdrop / browser tab strip on any edge. |195| `references/component-catalog.md` | Kit index + promotion governance + built-parts log. |196| `components/*.html` | Self-contained motion blocks (`pf-` prefixed): leading comment + scoped `<style>` + markup + a `<!-- Timeline recipe -->`. Paste into the case timeline. |197| `templates/canvas.html` | Blank on-brand 1600x900 HF stage; brand.css inlined, loop veil ready. Start here for a freeform case. |198| `examples/before-after/` | Page Checkup intro = worked example, parametrized via `data-composition-variables`. Start here for a before/after. |199| `examples/loop-ground/` | **The current reference for anything with footage in it.** A 14.8s explainer that LOOPS (48.6 dB seam, no veil): one continuous ground, travelling glows, clusters that stagger, footage that slides. Its `index.html` header records five traps that each cost a render - two of which pass `lint`, `check` and `render` while being visibly broken. |200201Rendering is `npx hyperframes render` directly - no bespoke render script (unlike illustra's202`render.mjs` for Playwright PNGs). `scripts/solve-crop.py` is a measuring tool, not a renderer.203204## Governance - the training model ("don't redo it each time")205Every refine compounds into the shared layers, never into a throwaway file:206- tweak a brand color / glow -> `references/brand.css` (identity + badge palette live in `_pf-brand/`)207- change a label / badge taste -> `_pf-brand/label-rules.md` + `_pf-brand/badges.html`208- lock a new ease / pacing / technique ("modern PF") -> `references/style-guide.md`209- a block hand-built a second time -> **promote it into `components/`** via suggest + approve.210- a footage / capture lesson -> `references/footage-pieces.md`211212After each piece, scan what you hand-built against 3 criteria:2131. **Reusable** - a generic motion concept, not specific to this case's content. ("Would I214 plausibly build this again for a different feature?")2152. **Self-contained & parameterizable** - own markup + scoped CSS + a clean timeline recipe;216 accepts text / color / size.2173. **On-brand & clean** - var-only, follows the style-guide.218219All 3 -> **PROPOSE** promoting it: a new `components/<name>.html` + a `component-catalog.md`220built-parts row (yes/no to the member; never auto-add, never ask them to judge technicals).221**Strong signal:** a part built a 2nd time almost always promotes. Keep the kit lean (YAGNI) -222do not seed a part until a real case needs it. Each use thickens kit + gu, so the next piece is223less work. Default gate = suggest + approve.