← back to Rentv Tour
Component video batch (18, Steve voice) + real bearded-Steve avatar (group 63e9372d) replacing placeholder
1a67a3b8756d3bde16dab28f53fbfb9330ee1ee1 · 2026-07-28 14:19:05 -0700 · Steve Abrams
Files touched
M AVATAR-STEVE.mdA batch-components.shA component-set.mjsA steve-real.jpgA videos/components/AGENTS.mdA videos/components/CLAUDE.mdA videos/components/hyperframes.jsonA videos/components/index.htmlA videos/components/meta.jsonA videos/components/package.json
Diff
commit 1a67a3b8756d3bde16dab28f53fbfb9330ee1ee1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jul 28 14:19:05 2026 -0700
Component video batch (18, Steve voice) + real bearded-Steve avatar (group 63e9372d) replacing placeholder
---
AVATAR-STEVE.md | 12 +++--
batch-components.sh | 17 +++++++
component-set.mjs | 85 +++++++++++++++++++++++++++++++++
steve-real.jpg | Bin 0 -> 94841 bytes
videos/components/AGENTS.md | 95 +++++++++++++++++++++++++++++++++++++
videos/components/CLAUDE.md | 95 +++++++++++++++++++++++++++++++++++++
videos/components/hyperframes.json | 13 +++++
videos/components/index.html | 40 ++++++++++++++++
videos/components/meta.json | 5 ++
videos/components/package.json | 11 +++++
10 files changed, 369 insertions(+), 4 deletions(-)
diff --git a/AVATAR-STEVE.md b/AVATAR-STEVE.md
index f194c84..a9aa3b8 100644
--- a/AVATAR-STEVE.md
+++ b/AVATAR-STEVE.md
@@ -4,7 +4,7 @@ Real-person digital twin of Steve Abrams, built from his headshot. Used as the P
presenter on RENTV videos (Master Reel + pitch films), driven by his cloned ElevenLabs voice.
## Appearance
-- Reference: /Users/macstudio3/Projects/Kamatera/DW-Agents/agent-avatars/steve.png
+- Reference: /Users/macstudio3/Projects/rentv-tour/steve-real.jpg (real bearded Steve, from iCloud share)
- Source: photo-based (HeyGen photo_avatar)
## Voice
@@ -12,8 +12,8 @@ presenter on RENTV videos (Master Reel + pitch films), driven by his cloned Elev
- ElevenLabs voice_id: Xa9qV4wNbvSkdUWsYLzq ("Steve Abrams")
## HeyGen
-- Group ID: c574ed3e978a46d1a9651b4ef11537b3 (stable identity anchor — never changes)
-- Look ID (landscape): 42fb945907ec4f358254ca4c1cd890e4
+- Group ID: 63e9372d3d2a47448997cbdbea5121ab (stable identity anchor — never changes)
+- Look ID (landscape): b2105c3cc1fd4464b11cd24c417b5751
- Avatar type: photo_avatar
- Supported engines: avatar_iv, avatar_iii
- Created: 2026-07-28 (status: processing/training at creation)
@@ -21,7 +21,7 @@ presenter on RENTV videos (Master Reel + pitch films), driven by his cloned Elev
- Last Synced: 2026-07-28
⚠️ look_ids are ephemeral — resolve fresh from the group_id at runtime via
-`heygen avatar looks list --group-id c574ed3e978a46d1a9651b4ef11537b3`. Never hardcode look_id
+`heygen avatar looks list --group-id 63e9372d3d2a47448997cbdbea5121ab`. Never hardcode look_id
as the primary reference.
## Usage (DTD verdict A, 2026-07-28)
@@ -29,3 +29,7 @@ PIP bookends on the LONGER videos only — Steve avatar in a bottom-right pictur
the first ~20s and last ~20s of the Master Reel + 4 pitch films; screenshots fill the middle;
ElevenLabs voice throughout. The ~17 short component videos stay clean (no avatar — too short
for a 20s bookend).
+
+
+## 2026-07-28 CORRECTION
+Old placeholder avatar (group c574ed3e, dark-haired stock man) was WRONG — not Steve. Rebuilt from Steve's REAL bearded headshot. REPLACED placeholder.
diff --git a/batch-components.sh b/batch-components.sh
new file mode 100755
index 0000000..c6bd125
--- /dev/null
+++ b/batch-components.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+cd /Users/macstudio3/Projects/rentv-tour
+SAY=~/.claude/skills/clone-voice/scripts/say.sh
+ok=0
+for id in $(node component-set.mjs ids); do
+ txt=$(node component-set.mjs vo $id)
+ bash $SAY "$txt" /tmp/c-$id.wav >/dev/null 2>&1
+ ffmpeg -v error -y -i /tmp/c-$id.wav -c:a aac -b:a 160k videos/components/assets/vo.m4a >/dev/null 2>&1
+ dur=$(ffprobe -v error -show_entries format=duration -of default=nk=1:nw=1 videos/components/assets/vo.m4a 2>/dev/null)
+ node component-set.mjs emit $id $dur >/dev/null 2>&1
+ (cd videos/components && npx hyperframes render --output renders/$id.mp4 >/dev/null 2>&1)
+ if [ -f videos/components/renders/$id.mp4 ]; then
+ has_a=$(ffprobe -v error -show_entries stream=codec_type -of csv=p=0 videos/components/renders/$id.mp4 2>/dev/null | grep -c audio)
+ echo "done $id ($(ls -la videos/components/renders/$id.mp4|awk '{printf "%.1fMB",$5/1048576}'), audio=$has_a)"; ok=$((ok+1))
+ else echo "FAIL $id"; fi
+done
+echo "BATCH DONE: $ok/18 rendered"
diff --git a/component-set.mjs b/component-set.mjs
new file mode 100644
index 0000000..61d84fb
--- /dev/null
+++ b/component-set.mjs
@@ -0,0 +1,85 @@
+import fs from 'fs';
+const W = 1920, H = 1080;
+const esc = (s) => String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
+
+// id, label, tier(front|admin), one-line VO (Steve cloned voice)
+export const COMPONENTS = [
+ // ── Front end ──
+ ['home', 'RENTV Front Page', 'front', "The RENTV front page — live commercial real estate news for the Western U.S."],
+ ['markets', 'Markets', 'front', "Markets — C.R.E. fundamentals and a live snapshot across every tracked metro."],
+ ['deals', 'Deals', 'front', "Deals — real commercial transactions, filterable by property type, deal type, and market."],
+ ['pulse', 'Market Pulse', 'front', "Market Pulse — the analytics layer over every tracked deal, from volume to top transactions."],
+ ['review', 'The REview', 'front', "The Review — RENTV's commercial real estate video platform, native in the shell."],
+ ['hub', 'Intelligence Hub', 'front', "The Intelligence Hub — every RENTV tool and data surface, one click away."],
+ ['search', 'Search', 'front', "Global search — one box across news, deals, videos, and the whole platform."],
+ ['directory', 'CRE Directory', 'front', "The C.R.E. Directory — firms and players across the Western U.S. commercial market."],
+ ['la-commercial', 'LA Commercial', 'front', "L.A. Commercial — property-level intelligence and transactions for the Los Angeles market."],
+ ['brief', 'The RENTV Brief', 'front', "The RENTV Brief — the daily digest of what moved in Western U.S. commercial real estate."],
+ // ── Admin backend ──
+ ['backend', 'Backend Hub', 'admin', "The RENTV backend hub — every internal tool in one place, admin only."],
+ ['desk', 'Broker & Owner Desk', 'admin', "The Broker and Owner Desk — a live registry of three hundred eighty-two thousand brokers and thirty-four thousand firms, plus sublease inventory."],
+ ['audience', 'Audience & CRM', 'admin', "Audience and C.R.M. — every subscriber and sublease broker, auto-segmented into one prospect list."],
+ ['admin', 'Admin Dashboards', 'admin', "Ten admin dashboards, each a different back-office view over the real, live data."],
+ ['versions', 'Site Versions', 'admin', "Ten front-page design versions — pick the look, all running on live data."],
+ ['consulting', 'Consulting', 'admin', "The consulting deliverable — RENTV's growth strategy, concepts, and command center, folded in."],
+ ['press', 'Video Library', 'admin', "The video library — every RENTV film, walkthrough, and recap in one admin gallery."],
+ ['social', 'Social Media', 'admin', "Social Media — LinkedIn, X, and Instagram channels, the content calendar, and draft posts, all in one place."],
+];
+const byId = Object.fromEntries(COMPONENTS.map(c => [c[0], c]));
+
+function emit(id, voDur) {
+ const [, label, tier] = byId[id];
+ const accent = tier === 'admin' ? '#c8a15a' : '#0a6cff';
+ const tag = tier === 'admin' ? 'BACKEND' : 'FRONT END';
+ const TITLE = 1.3;
+ const TOTAL = +Math.max(voDur + 0.4, TITLE + 3.0).toFixed(2);
+ const shotStart = TITLE, shotDur = +(TOTAL - TITLE).toFixed(3);
+ const html = `<!doctype html>
+<html lang="en"><head>
+<meta charset="UTF-8" /><meta name="viewport" content="width=1920, height=1080" />
+<title>RENTV — ${esc(label)}</title>
+<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
+<style>
+ body{margin:0;background:#070b10;font-family:system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif}
+ #root{position:relative;width:${W}px;height:${H}px;overflow:hidden}
+ .clip{position:absolute;inset:0}
+ #bg{background:radial-gradient(120% 120% at 50% 0%,#0d1725 0%,#070b10 60%)}
+ .shot{overflow:hidden}
+ .simg{width:100%;height:100%;object-fit:cover;object-position:top center;display:block;will-change:transform,opacity}
+ .card{display:grid;place-items:center}
+ .ci{text-align:center;padding:0 120px}
+ .ci .tag{font-size:20px;letter-spacing:.4em;font-weight:600;color:${accent};margin-bottom:18px}
+ .ci h1{margin:0;color:#f3f7fc;font-size:88px;font-weight:500;letter-spacing:-.01em}
+ .label{left:56px;bottom:60px;right:auto;top:auto;width:auto;height:auto}
+ .li{display:flex;align-items:center;gap:14px}
+ .li .num{color:#fff;font-size:18px;font-weight:600;letter-spacing:.08em;padding:8px 13px;border-radius:8px;background:${accent}}
+ .li .txt{color:#fff;font-size:30px;font-weight:500;background:rgba(9,15,24,.9);padding:8px 18px;border-radius:10px}
+</style></head>
+<body>
+ <div id="root" data-composition-id="main" data-start="0" data-width="${W}" data-height="${H}" data-duration="${TOTAL}" data-fps="30">
+ <div id="bg" class="clip" data-start="0" data-duration="${TOTAL}" data-track-index="0"></div>
+ <audio id="vo" src="assets/vo.m4a" data-start="0" data-volume="1"></audio>
+ <section id="title" class="clip card" data-start="0" data-duration="${TITLE}" data-track-index="1"><div class="ci"><div class="tag" id="tt">${tag}</div><h1 id="th">${esc(label)}</h1></div></section>
+ <div id="shot" class="clip shot" data-start="${shotStart}" data-duration="${shotDur}" data-track-index="2"><img id="simg" class="simg" src="assets/${id}.png" alt="${esc(label)}"></div>
+ <div id="lbl" class="clip label" data-start="${shotStart}" data-duration="${shotDur}" data-track-index="3"><div id="li" class="li"><span class="num">${tag}</span><span class="txt">${esc(label)}</span></div></div>
+ </div>
+ <script>
+ window.__timelines = window.__timelines || {};
+ const tl = gsap.timeline({ paused: true });
+ tl.from('#th',{y:32,autoAlpha:0,duration:0.55,ease:'power3.out'},0.12);
+ tl.from('#tt',{y:-14,autoAlpha:0,duration:0.45},0.04);
+ tl.from('#simg',{opacity:0,duration:0.35,ease:'power1.out'},${shotStart.toFixed(3)});
+ tl.fromTo('#simg',{scale:1.07},{scale:1.0,duration:${shotDur},ease:'none'},${shotStart.toFixed(3)});
+ tl.from('#li',{y:24,autoAlpha:0,duration:0.4,ease:'power2.out'},${(shotStart + 0.1).toFixed(3)});
+ window.__timelines['main'] = tl;
+ </script>
+</body></html>`;
+ fs.writeFileSync('videos/components/index.html', html);
+ return TOTAL;
+}
+
+// ── CLI ──
+const [cmd, a, b] = process.argv.slice(2);
+if (cmd === 'ids') console.log(COMPONENTS.map(c => c[0]).join(' '));
+else if (cmd === 'vo') process.stdout.write(byId[a][3]);
+else if (cmd === 'emit') console.log('TOTAL=' + emit(a, parseFloat(b)));
diff --git a/steve-real.jpg b/steve-real.jpg
new file mode 100644
index 0000000..7ca34d8
Binary files /dev/null and b/steve-real.jpg differ
diff --git a/videos/components/AGENTS.md b/videos/components/AGENTS.md
new file mode 100644
index 0000000..c4ff0ae
--- /dev/null
+++ b/videos/components/AGENTS.md
@@ -0,0 +1,95 @@
+# HyperFrames Composition Project
+
+## Skills — USE THESE FIRST
+
+**Always invoke the relevant skill before writing or modifying compositions.** Skills encode framework-specific patterns (e.g., `window.__timelines` registration, `data-*` attribute semantics, shader-compatible CSS rules) that are NOT in generic web docs. Skipping them produces broken compositions.
+
+**Doing anything with HyperFrames?** Start at `/hyperframes` — it tells you what HyperFrames can do and which skill or workflow handles your intent (make a video, TTS / BGM, prep footage, author / animate, render, install blocks), confirms your brief up front (the intent layer), and routes every "make me a…" request (a video, a deck, a composition port) to the right workflow. Read it first, especially when there's no project context to orient you. The workflows it routes to:
+
+- `/product-launch-video` — any **website** URL or brief / script → a product launch / SaaS / promo video, or a site tour / showcase featuring the site's own captured visuals.
+- `/faceless-explainer` — arbitrary text (topic / article / notes), **no URL, no website capture** → 60-90s faceless explainer.
+- `/embedded-captions` — an existing talking-head video (MP4) → the same footage with captions / subtitles added (rail + embed, or pure-cinematic embed); the footage itself is untouched.
+- `/talking-head-recut` — an existing talking-head / interview / podcast video (MP4) → the same footage **packaged with designed graphic overlays** (kinetic titles, lower-thirds, data callouts, pull-quotes, side panels, pip) synced to the transcript; the clip plays unchanged underneath. (Plain captions/subtitles → `/embedded-captions`.)
+- `/pr-to-video` — a GitHub PR (URL / `owner/repo#N` / "this PR") → 30-90s code-change explainer (changelog / feature reveal / fix / refactor).
+- `/motion-graphics` — a short (typically under 10s) design-led **motion graphic**, motion-is-the-message, no narration: kinetic type, a stat / number count-up, a chart, a logo sting, a lower-third / overlay, or an animated tweet / headline / captured-page highlight; rendered to MP4 or a transparent overlay. Longer / narrated / custom → `/general-video`.
+- `/music-to-video` — a **music track** (audio file, or video to pull audio from) → beat-synced video (lyric / slideshow / kinetic promo). Music drives pacing; user-supplied images / videos are cut onto the same beat grid.
+- `/slideshow` — a **presentation / pitch deck / interactive deck** — discrete slides, fragment reveals, branching, hotspot navigation, presenter mode. Output is a navigable deck, not a rendered video.
+- `/general-video` — fallback for any other video (title card, longer brand / sizzle reel, multi-scene montage, static loop, custom composition) and the home of **companion mode** — co-create with the full HyperFrames toolbox; the original hyperframes authoring flow, any length.
+
+**Porting an existing composition?** `/remotion-to-hyperframes` translates a Remotion (React) composition into HyperFrames HTML — a source migration, separate from the creation workflows above.
+
+The domain skills (`/hyperframes-core`, `/hyperframes-animation`, `/hyperframes-keyframes`, `/hyperframes-creative`, `/hyperframes-cli`, `/media-use`, `/hyperframes-registry`, `/figma`) and the full capability map live inside `/hyperframes` — it is the single source of truth for which skill handles which intent.
+
+**Changing how real footage or images look or reveal?** Load `/media-use` and its media-treatment policy before editing, even when the request only says dark, flat, boring, retro, private, or “make the reveal cooler.” Use canonical media treatments and seek-safe motion; do not improvise equivalent CSS/SVG filters or overlays.
+
+> **Tailwind v4 projects** (`hyperframes init --tailwind`): see `/hyperframes-core` → `references/tailwind.md`.
+
+> **Skill missing or stale?** Run `npx hyperframes skills update <name>` to install/refresh
+> the specific skill you need (the `/hyperframes` router does this automatically before
+> entering a workflow), or bare `npx hyperframes skills update` to refresh the core set plus
+> everything already installed — neither pulls the full set. Restart the agent session so
+> newly installed skills load.
+
+## Commands
+
+```bash
+npm run dev # start the preview server (long-running — keep it alive in background)
+npm run check # lint + runtime + layout + motion + contrast (one command)
+npm run render # render to MP4
+npm run publish # publish and get a shareable link
+npx hyperframes lint --verbose # include info-level findings
+npx hyperframes lint --json # machine-readable output for CI
+npx hyperframes docs <topic> # reference docs in terminal
+```
+
+> **`npm run dev` is a long-running server, not a one-shot command.** It blocks until stopped.
+> In Claude Code, always run it with `run_in_background: true`. Never run it as a foreground
+> command — it will time out and the server will die, breaking the browser preview.
+
+> **Pinned CLI version.** These scripts pin an exact `hyperframes@X.Y.Z` so this project re-renders identically over time. Weeks later that pin lags fixes shipped since. To move up: `npx hyperframes@latest upgrade --project . --check` (shows the delta), then `npx hyperframes@latest upgrade --project .` to rewrite the pins. Always unpinned — the pinned script re-runs the old version against itself.
+
+## Documentation
+
+**For quick reference**, use the local CLI docs command (no network required):
+
+```bash
+npx hyperframes docs <topic>
+```
+
+Topics: `data-attributes`, `gsap`, `compositions`, `rendering`, `examples`, `troubleshooting`
+
+**For full documentation**, discover pages via the machine-readable index — do NOT guess URLs:
+
+```
+https://hyperframes.heygen.com/llms.txt
+```
+
+## Project Structure
+
+- `index.html` — main composition (root timeline)
+- `compositions/` — sub-compositions referenced via `data-composition-src`
+- `meta.json` — project metadata (id, name)
+- `transcript.json` — whisper word-level transcript (if generated)
+
+## Linting — ALWAYS RUN AFTER CHANGES
+
+After creating or editing any `.html` composition, **always** run the full check before considering the task complete:
+
+```bash
+npm run check
+```
+
+Fix all errors before presenting the result. Warnings should be reviewed before rendering.
+
+## Key Rules
+
+1. Every timed element needs `data-start`, `data-duration`, and `data-track-index`
+2. Elements with timing **MUST** have `class="clip"` — the framework uses this for visibility control
+3. Timelines must be paused and registered on `window.__timelines`:
+ ```js
+ window.__timelines = window.__timelines || {};
+ window.__timelines["composition-id"] = gsap.timeline({ paused: true });
+ ```
+4. Videos use `muted` with a separate `<audio>` element for the audio track
+5. Sub-compositions use `data-composition-src="compositions/file.html"` to reference other HTML files
+6. Only deterministic logic — no `Date.now()`, no `Math.random()`, no network fetches
diff --git a/videos/components/CLAUDE.md b/videos/components/CLAUDE.md
new file mode 100644
index 0000000..c4ff0ae
--- /dev/null
+++ b/videos/components/CLAUDE.md
@@ -0,0 +1,95 @@
+# HyperFrames Composition Project
+
+## Skills — USE THESE FIRST
+
+**Always invoke the relevant skill before writing or modifying compositions.** Skills encode framework-specific patterns (e.g., `window.__timelines` registration, `data-*` attribute semantics, shader-compatible CSS rules) that are NOT in generic web docs. Skipping them produces broken compositions.
+
+**Doing anything with HyperFrames?** Start at `/hyperframes` — it tells you what HyperFrames can do and which skill or workflow handles your intent (make a video, TTS / BGM, prep footage, author / animate, render, install blocks), confirms your brief up front (the intent layer), and routes every "make me a…" request (a video, a deck, a composition port) to the right workflow. Read it first, especially when there's no project context to orient you. The workflows it routes to:
+
+- `/product-launch-video` — any **website** URL or brief / script → a product launch / SaaS / promo video, or a site tour / showcase featuring the site's own captured visuals.
+- `/faceless-explainer` — arbitrary text (topic / article / notes), **no URL, no website capture** → 60-90s faceless explainer.
+- `/embedded-captions` — an existing talking-head video (MP4) → the same footage with captions / subtitles added (rail + embed, or pure-cinematic embed); the footage itself is untouched.
+- `/talking-head-recut` — an existing talking-head / interview / podcast video (MP4) → the same footage **packaged with designed graphic overlays** (kinetic titles, lower-thirds, data callouts, pull-quotes, side panels, pip) synced to the transcript; the clip plays unchanged underneath. (Plain captions/subtitles → `/embedded-captions`.)
+- `/pr-to-video` — a GitHub PR (URL / `owner/repo#N` / "this PR") → 30-90s code-change explainer (changelog / feature reveal / fix / refactor).
+- `/motion-graphics` — a short (typically under 10s) design-led **motion graphic**, motion-is-the-message, no narration: kinetic type, a stat / number count-up, a chart, a logo sting, a lower-third / overlay, or an animated tweet / headline / captured-page highlight; rendered to MP4 or a transparent overlay. Longer / narrated / custom → `/general-video`.
+- `/music-to-video` — a **music track** (audio file, or video to pull audio from) → beat-synced video (lyric / slideshow / kinetic promo). Music drives pacing; user-supplied images / videos are cut onto the same beat grid.
+- `/slideshow` — a **presentation / pitch deck / interactive deck** — discrete slides, fragment reveals, branching, hotspot navigation, presenter mode. Output is a navigable deck, not a rendered video.
+- `/general-video` — fallback for any other video (title card, longer brand / sizzle reel, multi-scene montage, static loop, custom composition) and the home of **companion mode** — co-create with the full HyperFrames toolbox; the original hyperframes authoring flow, any length.
+
+**Porting an existing composition?** `/remotion-to-hyperframes` translates a Remotion (React) composition into HyperFrames HTML — a source migration, separate from the creation workflows above.
+
+The domain skills (`/hyperframes-core`, `/hyperframes-animation`, `/hyperframes-keyframes`, `/hyperframes-creative`, `/hyperframes-cli`, `/media-use`, `/hyperframes-registry`, `/figma`) and the full capability map live inside `/hyperframes` — it is the single source of truth for which skill handles which intent.
+
+**Changing how real footage or images look or reveal?** Load `/media-use` and its media-treatment policy before editing, even when the request only says dark, flat, boring, retro, private, or “make the reveal cooler.” Use canonical media treatments and seek-safe motion; do not improvise equivalent CSS/SVG filters or overlays.
+
+> **Tailwind v4 projects** (`hyperframes init --tailwind`): see `/hyperframes-core` → `references/tailwind.md`.
+
+> **Skill missing or stale?** Run `npx hyperframes skills update <name>` to install/refresh
+> the specific skill you need (the `/hyperframes` router does this automatically before
+> entering a workflow), or bare `npx hyperframes skills update` to refresh the core set plus
+> everything already installed — neither pulls the full set. Restart the agent session so
+> newly installed skills load.
+
+## Commands
+
+```bash
+npm run dev # start the preview server (long-running — keep it alive in background)
+npm run check # lint + runtime + layout + motion + contrast (one command)
+npm run render # render to MP4
+npm run publish # publish and get a shareable link
+npx hyperframes lint --verbose # include info-level findings
+npx hyperframes lint --json # machine-readable output for CI
+npx hyperframes docs <topic> # reference docs in terminal
+```
+
+> **`npm run dev` is a long-running server, not a one-shot command.** It blocks until stopped.
+> In Claude Code, always run it with `run_in_background: true`. Never run it as a foreground
+> command — it will time out and the server will die, breaking the browser preview.
+
+> **Pinned CLI version.** These scripts pin an exact `hyperframes@X.Y.Z` so this project re-renders identically over time. Weeks later that pin lags fixes shipped since. To move up: `npx hyperframes@latest upgrade --project . --check` (shows the delta), then `npx hyperframes@latest upgrade --project .` to rewrite the pins. Always unpinned — the pinned script re-runs the old version against itself.
+
+## Documentation
+
+**For quick reference**, use the local CLI docs command (no network required):
+
+```bash
+npx hyperframes docs <topic>
+```
+
+Topics: `data-attributes`, `gsap`, `compositions`, `rendering`, `examples`, `troubleshooting`
+
+**For full documentation**, discover pages via the machine-readable index — do NOT guess URLs:
+
+```
+https://hyperframes.heygen.com/llms.txt
+```
+
+## Project Structure
+
+- `index.html` — main composition (root timeline)
+- `compositions/` — sub-compositions referenced via `data-composition-src`
+- `meta.json` — project metadata (id, name)
+- `transcript.json` — whisper word-level transcript (if generated)
+
+## Linting — ALWAYS RUN AFTER CHANGES
+
+After creating or editing any `.html` composition, **always** run the full check before considering the task complete:
+
+```bash
+npm run check
+```
+
+Fix all errors before presenting the result. Warnings should be reviewed before rendering.
+
+## Key Rules
+
+1. Every timed element needs `data-start`, `data-duration`, and `data-track-index`
+2. Elements with timing **MUST** have `class="clip"` — the framework uses this for visibility control
+3. Timelines must be paused and registered on `window.__timelines`:
+ ```js
+ window.__timelines = window.__timelines || {};
+ window.__timelines["composition-id"] = gsap.timeline({ paused: true });
+ ```
+4. Videos use `muted` with a separate `<audio>` element for the audio track
+5. Sub-compositions use `data-composition-src="compositions/file.html"` to reference other HTML files
+6. Only deterministic logic — no `Date.now()`, no `Math.random()`, no network fetches
diff --git a/videos/components/hyperframes.json b/videos/components/hyperframes.json
new file mode 100644
index 0000000..ec2e3c2
--- /dev/null
+++ b/videos/components/hyperframes.json
@@ -0,0 +1,13 @@
+{
+ "$schema": "https://hyperframes.heygen.com/schema/hyperframes.json",
+ "registry": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry",
+ "paths": {
+ "blocks": "compositions",
+ "components": "compositions/components",
+ "assets": "assets"
+ },
+ "media": {
+ "autoProxy": true
+ },
+ "authoringSkill": "product-launch-video"
+}
diff --git a/videos/components/index.html b/videos/components/index.html
new file mode 100644
index 0000000..4b82114
--- /dev/null
+++ b/videos/components/index.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html lang="en"><head>
+<meta charset="UTF-8" /><meta name="viewport" content="width=1920, height=1080" />
+<title>RENTV — Social Media</title>
+<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
+<style>
+ body{margin:0;background:#070b10;font-family:system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif}
+ #root{position:relative;width:1920px;height:1080px;overflow:hidden}
+ .clip{position:absolute;inset:0}
+ #bg{background:radial-gradient(120% 120% at 50% 0%,#0d1725 0%,#070b10 60%)}
+ .shot{overflow:hidden}
+ .simg{width:100%;height:100%;object-fit:cover;object-position:top center;display:block;will-change:transform,opacity}
+ .card{display:grid;place-items:center}
+ .ci{text-align:center;padding:0 120px}
+ .ci .tag{font-size:20px;letter-spacing:.4em;font-weight:600;color:#c8a15a;margin-bottom:18px}
+ .ci h1{margin:0;color:#f3f7fc;font-size:88px;font-weight:500;letter-spacing:-.01em}
+ .label{left:56px;bottom:60px;right:auto;top:auto;width:auto;height:auto}
+ .li{display:flex;align-items:center;gap:14px}
+ .li .num{color:#fff;font-size:18px;font-weight:600;letter-spacing:.08em;padding:8px 13px;border-radius:8px;background:#c8a15a}
+ .li .txt{color:#fff;font-size:30px;font-weight:500;background:rgba(9,15,24,.9);padding:8px 18px;border-radius:10px}
+</style></head>
+<body>
+ <div id="root" data-composition-id="main" data-start="0" data-width="1920" data-height="1080" data-duration="7.51" data-fps="30">
+ <div id="bg" class="clip" data-start="0" data-duration="7.51" data-track-index="0"></div>
+ <audio id="vo" src="assets/vo.m4a" data-start="0" data-volume="1"></audio>
+ <section id="title" class="clip card" data-start="0" data-duration="1.3" data-track-index="1"><div class="ci"><div class="tag" id="tt">BACKEND</div><h1 id="th">Social Media</h1></div></section>
+ <div id="shot" class="clip shot" data-start="1.3" data-duration="6.21" data-track-index="2"><img id="simg" class="simg" src="assets/social.png" alt="Social Media"></div>
+ <div id="lbl" class="clip label" data-start="1.3" data-duration="6.21" data-track-index="3"><div id="li" class="li"><span class="num">BACKEND</span><span class="txt">Social Media</span></div></div>
+ </div>
+ <script>
+ window.__timelines = window.__timelines || {};
+ const tl = gsap.timeline({ paused: true });
+ tl.from('#th',{y:32,autoAlpha:0,duration:0.55,ease:'power3.out'},0.12);
+ tl.from('#tt',{y:-14,autoAlpha:0,duration:0.45},0.04);
+ tl.from('#simg',{opacity:0,duration:0.35,ease:'power1.out'},1.300);
+ tl.fromTo('#simg',{scale:1.07},{scale:1.0,duration:6.21,ease:'none'},1.300);
+ tl.from('#li',{y:24,autoAlpha:0,duration:0.4,ease:'power2.out'},1.400);
+ window.__timelines['main'] = tl;
+ </script>
+</body></html>
\ No newline at end of file
diff --git a/videos/components/meta.json b/videos/components/meta.json
new file mode 100644
index 0000000..1b5b4b8
--- /dev/null
+++ b/videos/components/meta.json
@@ -0,0 +1,5 @@
+{
+ "id": "components",
+ "name": "components",
+ "createdAt": "2026-07-28T21:10:36.704Z"
+}
\ No newline at end of file
diff --git a/videos/components/package.json b/videos/components/package.json
new file mode 100644
index 0000000..c144144
--- /dev/null
+++ b/videos/components/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "components",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "npx --yes hyperframes@0.7.78 preview",
+ "check": "npx --yes hyperframes@0.7.78 check",
+ "render": "npx --yes hyperframes@0.7.78 render",
+ "publish": "npx --yes hyperframes@0.7.78 publish"
+ }
+}
← 5986d75 Master Reel: curated hero montage + Steve HeyGen avatar circ
·
back to Rentv Tour
·
auto-save: 2026-07-28T14:31:09 (3 files) — batch-pitch.sh pi 8653362 →