[object Object]

← back to Crcp Pitch Video

auto-save: 2026-07-12T09:47:28 (10 files) — package-lock.json package.json src/Root.tsx tsconfig.json build-walkthrough-data.js

8c29909d4561c9664e6212e2f8fb8b8eb08b8aa7 · 2026-07-12 09:47:34 -0700 · Steve Abrams

Files touched

Diff

commit 8c29909d4561c9664e6212e2f8fb8b8eb08b8aa7
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun Jul 12 09:47:34 2026 -0700

    auto-save: 2026-07-12T09:47:28 (10 files) — package-lock.json package.json src/Root.tsx tsconfig.json build-walkthrough-data.js
---
 build-walkthrough-data.js     |  43 +++++++++
 capture-walkthrough.js        |  83 +++++++++++++++++
 package-lock.json             |  13 +++
 package.json                  |   5 +-
 public/walkthrough/shot00.png | Bin 0 -> 222306 bytes
 public/walkthrough/shot01.png | Bin 0 -> 227309 bytes
 public/walkthrough/shot02.png | Bin 0 -> 232506 bytes
 public/walkthrough/shot03.png | Bin 0 -> 283753 bytes
 public/walkthrough/shot04.png | Bin 0 -> 272830 bytes
 public/walkthrough/shot05.png | Bin 0 -> 284042 bytes
 public/walkthrough/shot06.png | Bin 0 -> 284574 bytes
 public/walkthrough/shot07.png | Bin 0 -> 284574 bytes
 public/walkthrough/shot08.png | Bin 0 -> 112054 bytes
 public/walkthrough/shot09.png | Bin 0 -> 347929 bytes
 public/walkthrough/shot10.png | Bin 0 -> 347929 bytes
 public/walkthrough/shot11.png | Bin 0 -> 283874 bytes
 public/walkthrough/shot12.png | Bin 0 -> 214747 bytes
 public/walkthrough/shot13.png | Bin 0 -> 216345 bytes
 public/walkthrough/shot14.png | Bin 0 -> 234721 bytes
 src/Root.tsx                  |   2 +
 src/Walkthrough.tsx           |  75 ++++++++++++++++
 src/walkthrough-data.json     | 201 ++++++++++++++++++++++++++++++++++++++++++
 tsconfig.json                 |   1 +
 walkthrough/manifest.json     | 197 +++++++++++++++++++++++++++++++++++++++++
 walkthrough/shot00.png        | Bin 0 -> 222306 bytes
 walkthrough/shot01.png        | Bin 0 -> 227309 bytes
 walkthrough/shot02.png        | Bin 0 -> 232506 bytes
 walkthrough/shot03.png        | Bin 0 -> 283753 bytes
 walkthrough/shot04.png        | Bin 0 -> 272830 bytes
 walkthrough/shot05.png        | Bin 0 -> 284042 bytes
 walkthrough/shot06.png        | Bin 0 -> 284574 bytes
 walkthrough/shot07.png        | Bin 0 -> 284574 bytes
 walkthrough/shot08.png        | Bin 0 -> 112054 bytes
 walkthrough/shot09.png        | Bin 0 -> 347929 bytes
 walkthrough/shot10.png        | Bin 0 -> 347929 bytes
 walkthrough/shot11.png        | Bin 0 -> 283874 bytes
 walkthrough/shot12.png        | Bin 0 -> 214747 bytes
 walkthrough/shot13.png        | Bin 0 -> 216345 bytes
 walkthrough/shot14.png        | Bin 0 -> 234721 bytes
 39 files changed, 618 insertions(+), 2 deletions(-)

diff --git a/build-walkthrough-data.js b/build-walkthrough-data.js
new file mode 100644
index 0000000..c2b9594
--- /dev/null
+++ b/build-walkthrough-data.js
@@ -0,0 +1,43 @@
+/* Generate per-step cloned-voice VO, measure durations, copy shots to public/, write src/walkthrough-data.json */
+const fs = require('fs'), path = require('path'), { execSync } = require('child_process');
+const https = require('https');
+
+const M = JSON.parse(fs.readFileSync(path.join(__dirname, 'walkthrough/manifest.json'), 'utf8'));
+const VOICE = 'Xa9qV4wNbvSkdUWsYLzq';
+let KEY = '';
+for (const p of [process.env.HOME + '/Projects/secrets-manager/.env', process.env.HOME + '/.claude/skills/clone-voice/.env']) {
+  try { for (const l of fs.readFileSync(p, 'utf8').split('\n')) if (l.includes('ELEVENLABS_API_KEY')) { KEY = l.split('=')[1].trim().replace(/"/g, ''); break; } } catch {}
+  if (KEY) break;
+}
+const AUD = path.join(__dirname, 'walkthrough/audio'); fs.mkdirSync(AUD, { recursive: true });
+const PUB = path.join(__dirname, 'public/walkthrough'); fs.mkdirSync(PUB, { recursive: true });
+
+function tts(text, out) {
+  return new Promise((res, rej) => {
+    const body = JSON.stringify({ text, model_id: 'eleven_multilingual_v2', voice_settings: { stability: 0.5, similarity_boost: 0.8, style: 0.15 } });
+    const req = https.request(`https://api.elevenlabs.io/v1/text-to-speech/${VOICE}`, { method: 'POST', headers: { 'xi-api-key': KEY, 'Content-Type': 'application/json', Accept: 'audio/mpeg' } }, r => {
+      const c = []; r.on('data', d => c.push(d)); r.on('end', () => { fs.writeFileSync(out, Buffer.concat(c)); res(); });
+    });
+    req.on('error', rej); req.write(body); req.end();
+  });
+}
+const dur = f => parseFloat(execSync(`ffprobe -v error -show_entries format=duration -of csv=p=0 "${f}"`).toString().trim());
+
+(async () => {
+  const FPS = 30; const steps = []; let chars = 0;
+  for (const m of M) {
+    const mp3 = path.join(AUD, `step${String(m.i).padStart(2, '0')}.mp3`);
+    chars += m.vo.length;
+    await tts(m.vo, mp3);
+    const d = dur(mp3);
+    fs.copyFileSync(path.join(__dirname, 'walkthrough', m.shot), path.join(PUB, m.shot));
+    const durFrames = Math.ceil((d + 1.35) * FPS);
+    steps.push({ shot: `walkthrough/${m.shot}`, box: m.box, label: m.label, cap: m.cap, durFrames, audioStartSec: null });
+    console.log(`  step ${m.i}: ${d.toFixed(1)}s -> ${durFrames}f  ${m.label}`);
+  }
+  // compute audio start offsets (cumulative)
+  let acc = 0;
+  for (const s of steps) { s.audioStartSec = acc / FPS; acc += s.durFrames; }
+  fs.writeFileSync(path.join(__dirname, 'src/walkthrough-data.json'), JSON.stringify({ fps: FPS, total: acc, steps }, null, 1));
+  console.log(`✔ ${steps.length} steps, ${(acc / FPS).toFixed(0)}s total, ${chars} chars (~$${(chars / 1000 * 0.30).toFixed(2)})`);
+})();
diff --git a/capture-walkthrough.js b/capture-walkthrough.js
new file mode 100644
index 0000000..8672601
--- /dev/null
+++ b/capture-walkthrough.js
@@ -0,0 +1,83 @@
+/*
+ * capture-walkthrough.js — drive CRCP (local :9911) with Playwright, click through the
+ * lead-finding workflow + every left-panel control, and emit for each step:
+ *   walkthrough/shotNN.png  (1920x1080 screenshot)  +  a manifest entry with the clicked
+ *   element's bounding box (for the Remotion highlight), a caption, and a narration line.
+ */
+const { chromium } = require('playwright-core');
+const fs = require('fs');
+const path = require('path');
+
+const SHELL = process.env.HOME + '/Library/Caches/ms-playwright/chromium_headless_shell-1228/chrome-headless-shell-mac-arm64/chrome-headless-shell';
+const BASE = 'http://127.0.0.1:9911';
+const OUT = path.join(__dirname, 'walkthrough');
+
+// Each step: what to do, what to highlight, and what to say.
+const STEPS = [
+  { url: '/broker-grid.html', hl: '#count', label: 'Your lead database',
+    cap: 'Every broker & agent — one screen', vo: "This is your C-R-C-P command center. Every broker and agent in the database, right here, ready to work." },
+  { click: '#pills, .rail .rsec:nth-child(1) h4', railExpand: 1, hl: '.rail .rsec:nth-child(1)', label: 'Left panel · Agent type',
+    cap: 'Filter by the kind of agent', vo: "On the left panel, open Agent Type to narrow the list to exactly the kind of agent you want." },
+  { clickChip: '#fType .chip', hl: '#fType', label: 'Pick a segment',
+    cap: 'Click a segment — grid filters instantly', vo: "Click a segment, and the whole grid instantly filters to just those agents." },
+  { railExpand: 3, hl: '.rail .rsec:nth-child(3)', label: 'Left panel · Company / firm',
+    cap: 'Target a specific brokerage', vo: "Want a specific brokerage? Open Company, and pick a firm to see only its agents." },
+  { clickChip: '#fFirm .chip', hl: '#fFirm', label: 'Choose a firm',
+    cap: 'Only that brokerage’s agents', vo: "Now you’re looking at just that brokerage — a focused list of real, licensed people." },
+  { railExpand: 4, hl: '#fFields', label: 'Left panel · Fields / columns',
+    cap: 'Show or hide any column', vo: "Open Fields, and show or hide any column — turn on State and C-A confidence to see exactly where each agent is licensed." },
+  { clickHeader: 'listings', hl: 'th[data-k="listings"]', label: 'Sort by activity',
+    cap: 'Busiest agents = your best leads', vo: "Here’s the key move for finding leads. Click Listings to sort by activity — the busiest agents, the ones closing deals, rise straight to the top." },
+  { hl: '#sortsel', label: 'Or sort any field',
+    cap: 'Verified license · state · assets', vo: "You can also sort by any field from this menu — verified license, state, or total assets." },
+  { type: ['#q', 'realty'], hl: '#q', label: 'Search instantly',
+    cap: 'Name · firm · phone · email', vo: "Search by name, firm, phone, or email to jump straight to who you’re looking for." },
+  { clearType: '#q', clickRow: '.brow', waitModal: 1, hl: '#modal', label: 'Open a lead',
+    cap: 'Full card: contact + listings + sales', vo: "Click any agent to open their full card — phone, email, LinkedIn, and their current listings and recent sales." },
+  { hl: '#mgen', label: 'Draft outreach in one click',
+    cap: 'Generate a ready-to-send pitch', vo: "And right here, generate a ready-to-send pitch, tailored to that agent. Your outreach, drafted for you." },
+  { closeModal: 1, hl: '#csv', label: 'Export your list',
+    cap: 'CSV → your CRM or dialer', vo: "Happy with your filtered list? Export it to C-S-V and load it into your C-R-M or dialer." },
+  { url: '/gov-agents.html', hl: '#metros', label: '21 elite markets',
+    cap: 'Every agent, 20 priciest cities', vo: "Need more markets? The Government-Licensed tab gives you every agent across the twenty priciest cities in America." },
+  { clickChip: '#metros .mchip', hl: '#metros', label: 'Pick a market',
+    cap: 'Beverly Hills · Aspen · Greenwich…', vo: "Click a market to load every licensed agent there — Beverly Hills, Aspen, Greenwich, and more." },
+  { type: ['#q', 'a'], hl: '#q', label: 'Search the market',
+    cap: 'Targeted lead list in seconds', vo: "Search within it, sort by license type, and you’ve got a targeted lead list in seconds." },
+];
+
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+(async () => {
+  fs.mkdirSync(OUT, { recursive: true });
+  const browser = await chromium.launch({ executablePath: SHELL, headless: true });
+  const ctx = await browser.newContext({ viewport: { width: 1920, height: 1080 }, deviceScaleFactor: 1, httpCredentials: { username: 'admin', password: 'DW2024!' } });
+  const page = await ctx.newPage();
+  const manifest = [];
+
+  for (let i = 0; i < STEPS.length; i++) {
+    const s = STEPS[i];
+    try {
+      if (s.url) { await page.goto(BASE + s.url, { waitUntil: 'domcontentloaded' }); await sleep(1800); }
+      if (s.railExpand) { const h = page.locator(`.rail .rsec:nth-child(${s.railExpand}) > h4`); await h.click().catch(() => {}); await sleep(500); }
+      if (s.click && !s.railExpand) { await page.locator(s.click.split(',')[0]).first().click().catch(() => {}); await sleep(400); }
+      if (s.clickChip) { await page.locator(s.clickChip).first().click().catch(() => {}); await sleep(800); }
+      if (s.clickHeader) { await page.locator(`th[data-k="${s.clickHeader}"]`).click().catch(() => {}); await sleep(800); }
+      if (s.type) { await page.fill(s.type[0], ''); await page.type(s.type[0], s.type[1], { delay: 45 }); await sleep(900); }
+      if (s.clearType) { await page.fill(s.clearType, ''); await sleep(500); }
+      if (s.clickRow) { await page.locator(s.clickRow).first().click().catch(() => {}); }
+      if (s.waitModal) { await sleep(2200); }
+      if (s.closeModal) { await page.locator('#ov .x, #modal .x').first().click().catch(() => {}); await sleep(600); }
+      await sleep(400);
+      const shot = `shot${String(i).padStart(2, '0')}.png`;
+      await page.screenshot({ path: path.join(OUT, shot) });
+      let box = null;
+      try { box = await page.locator(s.hl).first().boundingBox(); } catch (_) {}
+      manifest.push({ i, shot, label: s.label, cap: s.cap, vo: s.vo, box });
+      console.log(`  step ${i}: ${s.label} ${box ? `[${Math.round(box.x)},${Math.round(box.y)} ${Math.round(box.width)}x${Math.round(box.height)}]` : '(no box)'}`);
+    } catch (e) { console.log(`  step ${i} ERROR: ${e.message.split('\n')[0]}`); }
+  }
+  fs.writeFileSync(path.join(OUT, 'manifest.json'), JSON.stringify(manifest, null, 1));
+  console.log(`✔ ${manifest.length} steps → ${OUT}/manifest.json`);
+  await browser.close();
+})();
diff --git a/package-lock.json b/package-lock.json
index 1f09397..239c6f7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
         "@remotion/cli": "4.0.290",
         "@remotion/google-fonts": "4.0.290",
         "@remotion/renderer": "4.0.290",
+        "playwright-core": "^1.49.0",
         "react": "19.0.0",
         "react-dom": "19.0.0",
         "remotion": "4.0.290"
@@ -1896,6 +1897,18 @@
       "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
       "license": "ISC"
     },
+    "node_modules/playwright-core": {
+      "version": "1.49.0",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0.tgz",
+      "integrity": "sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==",
+      "license": "Apache-2.0",
+      "bin": {
+        "playwright-core": "cli.js"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/postcss": {
       "version": "8.5.17",
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.17.tgz",
diff --git a/package.json b/package.json
index fa97170..dee033a 100644
--- a/package.json
+++ b/package.json
@@ -8,10 +8,11 @@
     "render": "remotion render CRCPPitch out/crcp-pitch.mp4 --concurrency=4"
   },
   "dependencies": {
-    "@remotion/cli": "4.0.290",
     "@remotion/bundler": "4.0.290",
-    "@remotion/renderer": "4.0.290",
+    "@remotion/cli": "4.0.290",
     "@remotion/google-fonts": "4.0.290",
+    "@remotion/renderer": "4.0.290",
+    "playwright-core": "^1.49.0",
     "react": "19.0.0",
     "react-dom": "19.0.0",
     "remotion": "4.0.290"
diff --git a/public/walkthrough/shot00.png b/public/walkthrough/shot00.png
new file mode 100644
index 0000000..606b0e1
Binary files /dev/null and b/public/walkthrough/shot00.png differ
diff --git a/public/walkthrough/shot01.png b/public/walkthrough/shot01.png
new file mode 100644
index 0000000..522d0a4
Binary files /dev/null and b/public/walkthrough/shot01.png differ
diff --git a/public/walkthrough/shot02.png b/public/walkthrough/shot02.png
new file mode 100644
index 0000000..f611752
Binary files /dev/null and b/public/walkthrough/shot02.png differ
diff --git a/public/walkthrough/shot03.png b/public/walkthrough/shot03.png
new file mode 100644
index 0000000..adba35c
Binary files /dev/null and b/public/walkthrough/shot03.png differ
diff --git a/public/walkthrough/shot04.png b/public/walkthrough/shot04.png
new file mode 100644
index 0000000..ce47b0d
Binary files /dev/null and b/public/walkthrough/shot04.png differ
diff --git a/public/walkthrough/shot05.png b/public/walkthrough/shot05.png
new file mode 100644
index 0000000..d8dbb82
Binary files /dev/null and b/public/walkthrough/shot05.png differ
diff --git a/public/walkthrough/shot06.png b/public/walkthrough/shot06.png
new file mode 100644
index 0000000..1fbb51b
Binary files /dev/null and b/public/walkthrough/shot06.png differ
diff --git a/public/walkthrough/shot07.png b/public/walkthrough/shot07.png
new file mode 100644
index 0000000..1fbb51b
Binary files /dev/null and b/public/walkthrough/shot07.png differ
diff --git a/public/walkthrough/shot08.png b/public/walkthrough/shot08.png
new file mode 100644
index 0000000..cae0dca
Binary files /dev/null and b/public/walkthrough/shot08.png differ
diff --git a/public/walkthrough/shot09.png b/public/walkthrough/shot09.png
new file mode 100644
index 0000000..dd3de9c
Binary files /dev/null and b/public/walkthrough/shot09.png differ
diff --git a/public/walkthrough/shot10.png b/public/walkthrough/shot10.png
new file mode 100644
index 0000000..dd3de9c
Binary files /dev/null and b/public/walkthrough/shot10.png differ
diff --git a/public/walkthrough/shot11.png b/public/walkthrough/shot11.png
new file mode 100644
index 0000000..9ff4664
Binary files /dev/null and b/public/walkthrough/shot11.png differ
diff --git a/public/walkthrough/shot12.png b/public/walkthrough/shot12.png
new file mode 100644
index 0000000..99160f0
Binary files /dev/null and b/public/walkthrough/shot12.png differ
diff --git a/public/walkthrough/shot13.png b/public/walkthrough/shot13.png
new file mode 100644
index 0000000..baf5f72
Binary files /dev/null and b/public/walkthrough/shot13.png differ
diff --git a/public/walkthrough/shot14.png b/public/walkthrough/shot14.png
new file mode 100644
index 0000000..1fe33e8
Binary files /dev/null and b/public/walkthrough/shot14.png differ
diff --git a/src/Root.tsx b/src/Root.tsx
index 306c6c2..9e6f8e7 100644
--- a/src/Root.tsx
+++ b/src/Root.tsx
@@ -1,12 +1,14 @@
 import React from 'react';
 import { Composition } from 'remotion';
 import { CRCPPitch, CRCPShort, TOTAL_FRAMES, SHORT_FRAMES, FPS } from './Video';
+import { CRCPWalkthrough, WALK_FRAMES, WALK_FPS } from './Walkthrough';
 
 export const RemotionRoot: React.FC = () => {
   return (
     <>
       <Composition id="CRCPPitch" component={CRCPPitch} durationInFrames={TOTAL_FRAMES} fps={FPS} width={1920} height={1080} />
       <Composition id="CRCPShort" component={CRCPShort} durationInFrames={SHORT_FRAMES} fps={FPS} width={1920} height={1080} />
+      <Composition id="CRCPWalkthrough" component={CRCPWalkthrough} durationInFrames={WALK_FRAMES} fps={WALK_FPS} width={1920} height={1080} />
     </>
   );
 };
diff --git a/src/Walkthrough.tsx b/src/Walkthrough.tsx
new file mode 100644
index 0000000..b9c4f1d
--- /dev/null
+++ b/src/Walkthrough.tsx
@@ -0,0 +1,75 @@
+import React from 'react';
+import { AbsoluteFill, Img, staticFile, Sequence, useCurrentFrame, useVideoConfig, interpolate, spring, Easing } from 'remotion';
+import data from './walkthrough-data.json';
+
+export const WALK_FRAMES = data.total;
+export const WALK_FPS = data.fps;
+
+const GOLD = '#C8A24B';
+const SANS = '-apple-system, "Helvetica Neue", Arial, sans-serif';
+
+type Box = { x: number; y: number; width: number; height: number } | null;
+
+const Step: React.FC<{ shot: string; box: Box; label: string; cap: string; index: number; total: number; dur: number }> = ({ shot, box, label, cap, index, total, dur }) => {
+  const f = useCurrentFrame();
+  const { fps } = useVideoConfig();
+  // subtle ken-burns; image + highlight share the transform so the box stays on the element
+  const z = interpolate(f, [0, dur], [1.0, 1.035], { extrapolateRight: 'clamp' });
+  const appear = interpolate(f, [0, 10], [0, 1], { extrapolateRight: 'clamp' });
+  // highlight pop + pulse
+  const pop = spring({ frame: f - 6, fps, config: { damping: 12, mass: 0.6 } });
+  const pulse = 0.55 + 0.45 * Math.abs(Math.sin(f / 11));
+  const pad = 10;
+
+  return (
+    <AbsoluteFill style={{ background: '#05070b' }}>
+      <AbsoluteFill style={{ transform: `scale(${z})`, transformOrigin: '50% 45%' }}>
+        <Img src={staticFile(shot)} style={{ width: 1920, height: 1080 }} />
+        <AbsoluteFill style={{ background: 'rgba(3,5,10,0.22)', opacity: appear }} />
+        {box && (
+          <div style={{
+            position: 'absolute', left: box.x - pad, top: box.y - pad, width: box.width + pad * 2, height: box.height + pad * 2,
+            border: `4px solid ${GOLD}`, borderRadius: 10,
+            boxShadow: `0 0 ${18 + pulse * 22}px ${GOLD}, inset 0 0 12px rgba(200,162,75,0.25)`,
+            opacity: appear * (0.85 + pulse * 0.15),
+            transform: `scale(${interpolate(pop, [0, 1], [1.12, 1])})`, transformOrigin: 'center',
+          }} />
+        )}
+      </AbsoluteFill>
+      {/* caption bar */}
+      <div style={{
+        position: 'absolute', left: 0, right: 0, bottom: 0, height: 132,
+        background: 'linear-gradient(180deg, rgba(5,7,11,0) 0%, rgba(5,7,11,0.92) 42%)',
+        display: 'flex', alignItems: 'flex-end', padding: '0 56px 26px',
+        transform: `translateY(${interpolate(appear, [0, 1], [40, 0])}px)`, opacity: appear,
+      }}>
+        <div style={{ display: 'flex', alignItems: 'center', gap: 22 }}>
+          <div style={{ fontFamily: SANS, fontSize: 22, fontWeight: 700, color: '#05070b', background: GOLD, borderRadius: 20, padding: '5px 14px' }}>
+            {index + 1} / {total}
+          </div>
+          <div>
+            <div style={{ fontFamily: SANS, fontSize: 38, fontWeight: 800, color: GOLD, letterSpacing: 0.3 }}>{label}</div>
+            <div style={{ fontFamily: SANS, fontSize: 26, color: '#EAECEF', marginTop: 2 }}>{cap}</div>
+          </div>
+        </div>
+      </div>
+    </AbsoluteFill>
+  );
+};
+
+export const CRCPWalkthrough: React.FC = () => {
+  let from = 0;
+  return (
+    <AbsoluteFill style={{ background: '#05070b' }}>
+      {data.steps.map((s: any, i: number) => {
+        const el = (
+          <Sequence key={i} from={from} durationInFrames={s.durFrames}>
+            <Step shot={s.shot} box={s.box} label={s.label} cap={s.cap} index={i} total={data.steps.length} dur={s.durFrames} />
+          </Sequence>
+        );
+        from += s.durFrames;
+        return el;
+      })}
+    </AbsoluteFill>
+  );
+};
diff --git a/src/walkthrough-data.json b/src/walkthrough-data.json
new file mode 100644
index 0000000..745c53b
--- /dev/null
+++ b/src/walkthrough-data.json
@@ -0,0 +1,201 @@
+{
+ "fps": 30,
+ "total": 3159,
+ "steps": [
+  {
+   "shot": "walkthrough/shot00.png",
+   "box": {
+    "x": 1737.765625,
+    "y": 21.296875,
+    "width": 164.234375,
+    "height": 17.390625
+   },
+   "label": "Your lead database",
+   "cap": "Every broker & agent — one screen",
+   "durFrames": 230,
+   "audioStartSec": 0
+  },
+  {
+   "shot": "walkthrough/shot01.png",
+   "box": {
+    "x": 0,
+    "y": 61,
+    "width": 259,
+    "height": 112.71875
+   },
+   "label": "Left panel · Agent type",
+   "cap": "Filter by the kind of agent",
+   "durFrames": 194,
+   "audioStartSec": 7.666666666666667
+  },
+  {
+   "shot": "walkthrough/shot02.png",
+   "box": {
+    "x": 16,
+    "y": 98.9375,
+    "width": 227,
+    "height": 60.78125
+   },
+   "label": "Pick a segment",
+   "cap": "Click a segment — grid filters instantly",
+   "durFrames": 186,
+   "audioStartSec": 14.133333333333333
+  },
+  {
+   "shot": "walkthrough/shot03.png",
+   "box": {
+    "x": 0,
+    "y": 225.65625,
+    "width": 259,
+    "height": 581.578125
+   },
+   "label": "Left panel · Company / firm",
+   "cap": "Target a specific brokerage",
+   "durFrames": 173,
+   "audioStartSec": 20.333333333333332
+  },
+  {
+   "shot": "walkthrough/shot04.png",
+   "box": {
+    "x": 16,
+    "y": 263.59375,
+    "width": 227,
+    "height": 529.640625
+   },
+   "label": "Choose a firm",
+   "cap": "Only that brokerage’s agents",
+   "durFrames": 184,
+   "audioStartSec": 26.1
+  },
+  {
+   "shot": "walkthrough/shot05.png",
+   "box": {
+    "x": 16,
+    "y": 861.109375,
+    "width": 227,
+    "height": 320
+   },
+   "label": "Left panel · Fields / columns",
+   "cap": "Show or hide any column",
+   "durFrames": 253,
+   "audioStartSec": 32.233333333333334
+  },
+  {
+   "shot": "walkthrough/shot06.png",
+   "box": {
+    "x": 981.984375,
+    "y": 86,
+    "width": 84.90625,
+    "height": 32.21875
+   },
+   "label": "Sort by activity",
+   "cap": "Busiest agents = your best leads",
+   "durFrames": 297,
+   "audioStartSec": 40.666666666666664
+  },
+  {
+   "shot": "walkthrough/shot07.png",
+   "box": {
+    "x": 1524.046875,
+    "y": 16,
+    "width": 137,
+    "height": 28
+   },
+   "label": "Or sort any field",
+   "cap": "Verified license · state · assets",
+   "durFrames": 214,
+   "audioStartSec": 50.56666666666667
+  },
+  {
+   "shot": "walkthrough/shot08.png",
+   "box": {
+    "x": 1153.484375,
+    "y": 13,
+    "width": 220,
+    "height": 34
+   },
+   "label": "Search instantly",
+   "cap": "Name · firm · phone · email",
+   "durFrames": 182,
+   "audioStartSec": 57.7
+  },
+  {
+   "shot": "walkthrough/shot09.png",
+   "box": {
+    "x": 610,
+    "y": 40,
+    "width": 700,
+    "height": 705.265625
+   },
+   "label": "Open a lead",
+   "cap": "Full card: contact + listings + sales",
+   "durFrames": 225,
+   "audioStartSec": 63.766666666666666
+  },
+  {
+   "shot": "walkthrough/shot10.png",
+   "box": {
+    "x": 633,
+    "y": 672.96875,
+    "width": 174.015625,
+    "height": 31
+   },
+   "label": "Draft outreach in one click",
+   "cap": "Generate a ready-to-send pitch",
+   "durFrames": 207,
+   "audioStartSec": 71.26666666666667
+  },
+  {
+   "shot": "walkthrough/shot11.png",
+   "box": {
+    "x": 1673.046875,
+    "y": 11,
+    "width": 69.34375,
+    "height": 38
+   },
+   "label": "Export your list",
+   "cap": "CSV → your CRM or dialer",
+   "durFrames": 201,
+   "audioStartSec": 78.16666666666667
+  },
+  {
+   "shot": "walkthrough/shot12.png",
+   "box": {
+    "x": 0,
+    "y": 58,
+    "width": 1920,
+    "height": 123.375
+   },
+   "label": "21 elite markets",
+   "cap": "Every agent, 20 priciest cities",
+   "durFrames": 229,
+   "audioStartSec": 84.86666666666666
+  },
+  {
+   "shot": "walkthrough/shot13.png",
+   "box": {
+    "x": 0,
+    "y": 58,
+    "width": 1920,
+    "height": 123.375
+   },
+   "label": "Pick a market",
+   "cap": "Beverly Hills · Aspen · Greenwich…",
+   "durFrames": 205,
+   "audioStartSec": 92.5
+  },
+  {
+   "shot": "walkthrough/shot14.png",
+   "box": {
+    "x": 1222.765625,
+    "y": 12,
+    "width": 230,
+    "height": 33
+   },
+   "label": "Search the market",
+   "cap": "Targeted lead list in seconds",
+   "durFrames": 179,
+   "audioStartSec": 99.33333333333333
+  }
+ ]
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 9415d8d..0a57cf6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,6 +6,7 @@
     "jsx": "react-jsx",
     "strict": false,
     "esModuleInterop": true,
+    "resolveJsonModule": true,
     "skipLibCheck": true,
     "lib": ["ES2020", "DOM"]
   },
diff --git a/walkthrough/manifest.json b/walkthrough/manifest.json
new file mode 100644
index 0000000..0cd13a9
--- /dev/null
+++ b/walkthrough/manifest.json
@@ -0,0 +1,197 @@
+[
+ {
+  "i": 0,
+  "shot": "shot00.png",
+  "label": "Your lead database",
+  "cap": "Every broker & agent — one screen",
+  "vo": "This is your C-R-C-P command center. Every broker and agent in the database, right here, ready to work.",
+  "box": {
+   "x": 1737.765625,
+   "y": 21.296875,
+   "width": 164.234375,
+   "height": 17.390625
+  }
+ },
+ {
+  "i": 1,
+  "shot": "shot01.png",
+  "label": "Left panel · Agent type",
+  "cap": "Filter by the kind of agent",
+  "vo": "On the left panel, open Agent Type to narrow the list to exactly the kind of agent you want.",
+  "box": {
+   "x": 0,
+   "y": 61,
+   "width": 259,
+   "height": 112.71875
+  }
+ },
+ {
+  "i": 2,
+  "shot": "shot02.png",
+  "label": "Pick a segment",
+  "cap": "Click a segment — grid filters instantly",
+  "vo": "Click a segment, and the whole grid instantly filters to just those agents.",
+  "box": {
+   "x": 16,
+   "y": 98.9375,
+   "width": 227,
+   "height": 60.78125
+  }
+ },
+ {
+  "i": 3,
+  "shot": "shot03.png",
+  "label": "Left panel · Company / firm",
+  "cap": "Target a specific brokerage",
+  "vo": "Want a specific brokerage? Open Company, and pick a firm to see only its agents.",
+  "box": {
+   "x": 0,
+   "y": 225.65625,
+   "width": 259,
+   "height": 581.578125
+  }
+ },
+ {
+  "i": 4,
+  "shot": "shot04.png",
+  "label": "Choose a firm",
+  "cap": "Only that brokerage’s agents",
+  "vo": "Now you’re looking at just that brokerage — a focused list of real, licensed people.",
+  "box": {
+   "x": 16,
+   "y": 263.59375,
+   "width": 227,
+   "height": 529.640625
+  }
+ },
+ {
+  "i": 5,
+  "shot": "shot05.png",
+  "label": "Left panel · Fields / columns",
+  "cap": "Show or hide any column",
+  "vo": "Open Fields, and show or hide any column — turn on State and C-A confidence to see exactly where each agent is licensed.",
+  "box": {
+   "x": 16,
+   "y": 861.109375,
+   "width": 227,
+   "height": 320
+  }
+ },
+ {
+  "i": 6,
+  "shot": "shot06.png",
+  "label": "Sort by activity",
+  "cap": "Busiest agents = your best leads",
+  "vo": "Here’s the key move for finding leads. Click Listings to sort by activity — the busiest agents, the ones closing deals, rise straight to the top.",
+  "box": {
+   "x": 981.984375,
+   "y": 86,
+   "width": 84.90625,
+   "height": 32.21875
+  }
+ },
+ {
+  "i": 7,
+  "shot": "shot07.png",
+  "label": "Or sort any field",
+  "cap": "Verified license · state · assets",
+  "vo": "You can also sort by any field from this menu — verified license, state, or total assets.",
+  "box": {
+   "x": 1524.046875,
+   "y": 16,
+   "width": 137,
+   "height": 28
+  }
+ },
+ {
+  "i": 8,
+  "shot": "shot08.png",
+  "label": "Search instantly",
+  "cap": "Name · firm · phone · email",
+  "vo": "Search by name, firm, phone, or email to jump straight to who you’re looking for.",
+  "box": {
+   "x": 1153.484375,
+   "y": 13,
+   "width": 220,
+   "height": 34
+  }
+ },
+ {
+  "i": 9,
+  "shot": "shot09.png",
+  "label": "Open a lead",
+  "cap": "Full card: contact + listings + sales",
+  "vo": "Click any agent to open their full card — phone, email, LinkedIn, and their current listings and recent sales.",
+  "box": {
+   "x": 610,
+   "y": 40,
+   "width": 700,
+   "height": 705.265625
+  }
+ },
+ {
+  "i": 10,
+  "shot": "shot10.png",
+  "label": "Draft outreach in one click",
+  "cap": "Generate a ready-to-send pitch",
+  "vo": "And right here, generate a ready-to-send pitch, tailored to that agent. Your outreach, drafted for you.",
+  "box": {
+   "x": 633,
+   "y": 672.96875,
+   "width": 174.015625,
+   "height": 31
+  }
+ },
+ {
+  "i": 11,
+  "shot": "shot11.png",
+  "label": "Export your list",
+  "cap": "CSV → your CRM or dialer",
+  "vo": "Happy with your filtered list? Export it to C-S-V and load it into your C-R-M or dialer.",
+  "box": {
+   "x": 1673.046875,
+   "y": 11,
+   "width": 69.34375,
+   "height": 38
+  }
+ },
+ {
+  "i": 12,
+  "shot": "shot12.png",
+  "label": "21 elite markets",
+  "cap": "Every agent, 20 priciest cities",
+  "vo": "Need more markets? The Government-Licensed tab gives you every agent across the twenty priciest cities in America.",
+  "box": {
+   "x": 0,
+   "y": 58,
+   "width": 1920,
+   "height": 123.375
+  }
+ },
+ {
+  "i": 13,
+  "shot": "shot13.png",
+  "label": "Pick a market",
+  "cap": "Beverly Hills · Aspen · Greenwich…",
+  "vo": "Click a market to load every licensed agent there — Beverly Hills, Aspen, Greenwich, and more.",
+  "box": {
+   "x": 0,
+   "y": 58,
+   "width": 1920,
+   "height": 123.375
+  }
+ },
+ {
+  "i": 14,
+  "shot": "shot14.png",
+  "label": "Search the market",
+  "cap": "Targeted lead list in seconds",
+  "vo": "Search within it, sort by license type, and you’ve got a targeted lead list in seconds.",
+  "box": {
+   "x": 1222.765625,
+   "y": 12,
+   "width": 230,
+   "height": 33
+  }
+ }
+]
\ No newline at end of file
diff --git a/walkthrough/shot00.png b/walkthrough/shot00.png
new file mode 100644
index 0000000..606b0e1
Binary files /dev/null and b/walkthrough/shot00.png differ
diff --git a/walkthrough/shot01.png b/walkthrough/shot01.png
new file mode 100644
index 0000000..522d0a4
Binary files /dev/null and b/walkthrough/shot01.png differ
diff --git a/walkthrough/shot02.png b/walkthrough/shot02.png
new file mode 100644
index 0000000..f611752
Binary files /dev/null and b/walkthrough/shot02.png differ
diff --git a/walkthrough/shot03.png b/walkthrough/shot03.png
new file mode 100644
index 0000000..adba35c
Binary files /dev/null and b/walkthrough/shot03.png differ
diff --git a/walkthrough/shot04.png b/walkthrough/shot04.png
new file mode 100644
index 0000000..ce47b0d
Binary files /dev/null and b/walkthrough/shot04.png differ
diff --git a/walkthrough/shot05.png b/walkthrough/shot05.png
new file mode 100644
index 0000000..d8dbb82
Binary files /dev/null and b/walkthrough/shot05.png differ
diff --git a/walkthrough/shot06.png b/walkthrough/shot06.png
new file mode 100644
index 0000000..1fbb51b
Binary files /dev/null and b/walkthrough/shot06.png differ
diff --git a/walkthrough/shot07.png b/walkthrough/shot07.png
new file mode 100644
index 0000000..1fbb51b
Binary files /dev/null and b/walkthrough/shot07.png differ
diff --git a/walkthrough/shot08.png b/walkthrough/shot08.png
new file mode 100644
index 0000000..cae0dca
Binary files /dev/null and b/walkthrough/shot08.png differ
diff --git a/walkthrough/shot09.png b/walkthrough/shot09.png
new file mode 100644
index 0000000..dd3de9c
Binary files /dev/null and b/walkthrough/shot09.png differ
diff --git a/walkthrough/shot10.png b/walkthrough/shot10.png
new file mode 100644
index 0000000..dd3de9c
Binary files /dev/null and b/walkthrough/shot10.png differ
diff --git a/walkthrough/shot11.png b/walkthrough/shot11.png
new file mode 100644
index 0000000..9ff4664
Binary files /dev/null and b/walkthrough/shot11.png differ
diff --git a/walkthrough/shot12.png b/walkthrough/shot12.png
new file mode 100644
index 0000000..99160f0
Binary files /dev/null and b/walkthrough/shot12.png differ
diff --git a/walkthrough/shot13.png b/walkthrough/shot13.png
new file mode 100644
index 0000000..baf5f72
Binary files /dev/null and b/walkthrough/shot13.png differ
diff --git a/walkthrough/shot14.png b/walkthrough/shot14.png
new file mode 100644
index 0000000..1fe33e8
Binary files /dev/null and b/walkthrough/shot14.png differ

← bb76371 CRCP video: cloned-voice narration (ElevenLabs, Steve Abrams  ·  back to Crcp Pitch Video  ·  CRCP walkthrough video — Playwright click-through capture (1 b178aa4 →