← back to Prestige Car Wash Video
Add PrestigeAdmin composition: dedicated 68s Growth Center backend walkthrough
322c8af0293d0422c94ec63f5c1dd70d7a26624f · 2026-07-12 11:17:43 -0700 · Steve Abrams
Files touched
A src/AdminVideo.tsxM src/Root.tsxA src/adminSections.ts
Diff
commit 322c8af0293d0422c94ec63f5c1dd70d7a26624f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Jul 12 11:17:43 2026 -0700
Add PrestigeAdmin composition: dedicated 68s Growth Center backend walkthrough
---
src/AdminVideo.tsx | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/Root.tsx | 10 ++++++++
src/adminSections.ts | 37 ++++++++++++++++++++++++++++
3 files changed, 115 insertions(+)
diff --git a/src/AdminVideo.tsx b/src/AdminVideo.tsx
new file mode 100644
index 0000000..e8f5267
--- /dev/null
+++ b/src/AdminVideo.tsx
@@ -0,0 +1,68 @@
+import { AbsoluteFill, Sequence, Audio, staticFile, useCurrentFrame, useVideoConfig, interpolate } from 'remotion';
+import { ADMIN_SECTIONS } from './adminSections';
+import { Background } from './Background';
+import { BrowserShot } from './BrowserShot';
+import { Caption } from './Caption';
+import { Card } from './Card';
+
+const FONT = 'Inter, "SF Pro Display", system-ui, -apple-system, sans-serif';
+
+const FadeWrap: React.FC<{ dur: number; children: React.ReactNode }> = ({ dur, children }) => {
+ const f = useCurrentFrame();
+ const o = interpolate(f, [0, 10, dur - 9, dur], [0, 1, 1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });
+ return <AbsoluteFill style={{ opacity: o }}>{children}</AbsoluteFill>;
+};
+
+export const AdminVideo: React.FC = () => {
+ const frame = useCurrentFrame();
+ const { durationInFrames } = useVideoConfig();
+ const offsets: number[] = [];
+ let acc = 0;
+ for (const s of ADMIN_SECTIONS) { offsets.push(acc); acc += s.frames; }
+ let active = 0;
+ for (let i = 0; i < ADMIN_SECTIONS.length; i++) if (frame >= offsets[i]) active = i;
+ const accent = ADMIN_SECTIONS[active].accent;
+ const shotIndex = ADMIN_SECTIONS.slice(0, active + 1).filter((s) => s.kind === 'shot').length;
+ const shotTotal = ADMIN_SECTIONS.filter((s) => s.kind === 'shot').length;
+
+ return (
+ <AbsoluteFill style={{ backgroundColor: '#060c16' }}>
+ <Background accent={accent} />
+
+ {ADMIN_SECTIONS.map((s, i) => (
+ <Sequence key={s.id} from={offsets[i]} durationInFrames={s.frames} name={s.title}>
+ <FadeWrap dur={s.frames}>
+ {s.kind === 'card' ? (
+ <>
+ <Card s={s as any} />
+ {s.id === 'intro' && <Audio src={staticFile('audio/admin.wav')} />}
+ </>
+ ) : (
+ <>
+ <BrowserShot s={s as any} />
+ <Caption s={s as any} index={i} total={ADMIN_SECTIONS.length} />
+ </>
+ )}
+ </FadeWrap>
+ </Sequence>
+ ))}
+
+ <div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 6, background: 'rgba(255,255,255,0.08)' }}>
+ <div style={{ height: 6, width: `${(frame / durationInFrames) * 100}%`, background: accent, boxShadow: `0 0 16px ${accent}` }} />
+ </div>
+
+ {ADMIN_SECTIONS[active].kind === 'shot' && (
+ <div style={{ position: 'absolute', top: 34, right: 54, fontFamily: FONT, fontWeight: 700, fontSize: 24, color: '#c9a24a', letterSpacing: 2 }}>
+ {String(shotIndex).padStart(2, '0')}<span style={{ opacity: 0.4 }}> / {String(shotTotal).padStart(2, '0')}</span>
+ </div>
+ )}
+
+ <div style={{ position: 'absolute', top: 30, left: 54, display: 'flex', alignItems: 'center', gap: 12,
+ fontFamily: FONT, fontWeight: 800, fontSize: 24, color: '#e8f1ff', letterSpacing: 0.5 }}>
+ <span style={{ display: 'inline-flex', width: 34, height: 34, borderRadius: 9,
+ background: 'linear-gradient(135deg,#f5b301,#d97706)', alignItems: 'center', justifyContent: 'center', fontSize: 18 }}>🚗</span>
+ Prestige<span style={{ opacity: 0.5, fontWeight: 600 }}>Admin</span>
+ </div>
+ </AbsoluteFill>
+ );
+};
diff --git a/src/Root.tsx b/src/Root.tsx
index ffc4935..02dcf22 100644
--- a/src/Root.tsx
+++ b/src/Root.tsx
@@ -1,12 +1,22 @@
import { Composition } from 'remotion';
import { PrestigeShowcase } from './Video';
import { StoryVideo } from './StoryVideo';
+import { AdminVideo } from './AdminVideo';
import { FPS, WIDTH, HEIGHT, TOTAL_FRAMES } from './sections';
import { TOTAL as STORY_TOTAL } from './story';
+import { ADMIN_TOTAL } from './adminSections';
export const RemotionRoot: React.FC = () => {
return (
<>
+ <Composition
+ id="PrestigeAdmin"
+ component={AdminVideo}
+ durationInFrames={ADMIN_TOTAL}
+ fps={FPS}
+ width={WIDTH}
+ height={HEIGHT}
+ />
<Composition
id="PrestigeStory"
component={StoryVideo}
diff --git a/src/adminSections.ts b/src/adminSections.ts
new file mode 100644
index 0000000..6b8b64a
--- /dev/null
+++ b/src/adminSections.ts
@@ -0,0 +1,37 @@
+import dims from './dims.json';
+import type { Section } from './sections';
+import { FPS } from './sections';
+
+const AMBER = '#f5b301';
+const D: Record<string, { w: number; h: number }> = dims as any;
+const sec = (s: number) => Math.round(s * FPS);
+
+const TABS: Array<[string, string, string]> = [
+ ['overview', 'Overview', 'Live counts across the whole growth dataset'],
+ ['competitors', 'Competitors', '12 rival washes in the San Fernando Valley'],
+ ['best-post', 'Best Times to Post', 'When engagement peaks by channel'],
+ ['best-market', 'Best Times to Market', 'When to spend to convert'],
+ ['holidays', 'Holiday-Driven', 'Eight seasonal campaign hooks'],
+ ['services', 'Services', 'Editable service catalog behind the storefront'],
+ ['suggestions', 'Grow the Business', 'Ten ranked growth plays with rationale'],
+ ['ads', 'Online Ads', 'Platform-by-platform ad headlines'],
+ ['directories', 'Groups · Directories', 'Nextdoor, local groups & listing sites'],
+ ['google', 'Google Business', 'Places profile draft-update panel'],
+ ['leads', 'Leads', 'Inbound booking requests from the site'],
+ ['credentials', 'Credentials', 'Keys & access (presence + last-4 only)'],
+];
+
+export const ADMIN_SECTIONS: Section[] = [
+ { kind: 'card', id: 'intro', act: 'Behind the scenes', title: 'Growth Center',
+ sub: 'The admin backend that keeps Prestige sharp', frames: sec(8), accent: AMBER },
+ ...TABS.map(([key, title, sub]): Section => {
+ const dk = 'admin-' + key;
+ return { kind: 'shot', id: dk, file: `shots/${dk}.png`, w: D[dk]?.w, h: D[dk]?.h,
+ act: 'Admin · Growth Center', title, sub, url: 'prestigecarwash.com/admin',
+ frames: sec(4.6), accent: AMBER };
+ }),
+ { kind: 'card', id: 'outro', act: 'Prestige Car Wash', title: 'Growth Center',
+ sub: 'Competitors · campaigns · ads · leads — all in one place', frames: sec(4.5), accent: '#34d399' },
+];
+
+export const ADMIN_TOTAL = ADMIN_SECTIONS.reduce((a, s) => a + s.frames, 0);
← 12ff853 chore: lint, refactor (dead-code), v1.0.1 (session close)
·
back to Prestige Car Wash Video
·
(newest)