← back to Prestige Car Wash Video

src/story.ts

86 lines

import durations from './durations.json';
import dims from './dims.json';

export const FPS = 30;
export const WIDTH = 1920;
export const HEIGHT = 1080;

const CYAN = '#38bdf8';
const AMBER = '#f5b301';
const GREEN = '#34d399';
const VIOLET = '#a78bfa';

const D: Record<string, number> = durations as any;
// frames for a narrated scene = (voice length + pad), floored to a minimum
const nf = (id: string, pad = 1.1, min = 2.2) =>
  Math.max(Math.round(((D[id] || 0) + pad) * FPS), Math.round(min * FPS));

export interface Scene {
  type: 'card' | 'clip' | 'fcfs' | 'cam' | 'tips' | 'shot';
  id: string;
  narr?: string;
  frames: number;
  accent: string;
  // card
  act?: string; title?: string; sub?: string;
  // clip / option
  name?: string; price?: string; category?: string; blurb?: string; clip?: string;
  // shot
  file?: string; w?: number; h?: number; url?: string;
}

const OPTIONS: Array<[string, string, string, string, string]> = [
  // name, price, category, blurb, clipfile(without ext maps to narr opt-<x>)
  ['Express Exterior Wash', '$19', 'Wash', 'Foam bath, spot-free rinse, and a hand dry. In and out — no upsell pressure.', 'express-wash'],
  ['Full-Service Wash', '$39', 'Wash', 'Exterior hand wash, interior vacuum, dash wipe, windows, and tire dressing.', 'full-service'],
  ['Premium Hand Wash', '$79', 'Wash', 'Two-bucket, foam-cannon hand wash — no tunnel, no swirl marks. Safe for ceramic, matte & wraps.', 'hand-wash'],
  ['Wax & Ceramic Seal', '$49', 'Protection', 'Triple-foam polish plus a hydrophobic ceramic seal that shields clear coat from UV & road.', 'wax-seal'],
  ['Interior Express Detail', '$120', 'Detail', 'Deep interior: seats, carpets, dash, vents & door jambs. Pet-hair extraction and shampoo.', 'interior-detail'],
  ['Complete Detail', '$499', 'Detail', 'Full inside-and-out restoration: wash, clay bar, polish, wax, interior shampoo & engine bay.', 'complete-detail'],
  ['Ceramic Coating · 9H', 'from $999', 'Protection', 'Iron decon, clay bar, one-step polish, and a 9H nano-ceramic coating — 1–3 years of protection.', 'ceramic-coating'],
  ['Headlight Restoration', '$89', 'Restore', 'Sand, polish, and seal foggy, yellowed headlights back to clear — better night visibility.', 'headlight'],
];

const ADMIN: Array<[string, string]> = [
  ['overview', 'Overview'], ['competitors', 'Competitors'], ['best-post', 'Best Times to Post'],
  ['best-market', 'Best Times to Market'], ['holidays', 'Holiday-Driven'], ['services', 'Services'],
  ['suggestions', 'Grow the Business'], ['ads', 'Online Ads'], ['directories', 'Directories'],
  ['google', 'Google Business'], ['leads', 'Leads'], ['credentials', 'Credentials'],
];

const catAccent: Record<string, string> = { Wash: CYAN, Protection: GREEN, Detail: AMBER, Restore: VIOLET };

export const SCENES: Scene[] = [
  { type: 'card', id: 'intro', narr: 'intro', frames: nf('intro', 1.2), accent: CYAN,
    act: 'San Fernando Valley', title: 'Prestige Car Wash', sub: 'Hand wash · Detail · Ceramic' },

  { type: 'fcfs', id: 'model', narr: 'model', frames: nf('model', 1.0), accent: CYAN },

  { type: 'card', id: 'options', narr: 'options', frames: nf('options', 1.2, 3), accent: AMBER,
    act: 'The Menu', title: 'Every Option', sub: 'Flat pricing · no upsell pressure' },

  ...OPTIONS.map(([name, price, category, blurb, key]): Scene => ({
    type: 'clip', id: `opt-${key}`, narr: `opt-${key}`, frames: nf(`opt-${key}`, 1.0),
    accent: catAccent[category] || CYAN, name, price, category, blurb, clip: `media/svc-${key}.mp4`,
  })),

  { type: 'cam', id: 'livecam', narr: 'livecam', frames: nf('livecam', 1.0), accent: GREEN },

  { type: 'tips', id: 'tips', narr: 'tips', frames: nf('tips', 1.2), accent: AMBER },

  { type: 'card', id: 'act2', narr: 'admin', frames: nf('admin', 1.0), accent: AMBER,
    act: 'Behind the scenes', title: 'Growth Center', sub: 'How we keep the business sharp' },

  ...ADMIN.map(([key, title]): Scene => {
    const dk = 'admin-' + key;
    return { type: 'shot', id: dk, frames: Math.round(3.3 * FPS), accent: AMBER,
      file: `shots/${dk}.png`, w: (dims as any)[dk]?.w, h: (dims as any)[dk]?.h,
      title, url: 'prestigecarwash.com/admin', act: 'Admin · Growth Center', sub: title };
  }),

  { type: 'card', id: 'outro', narr: 'outro', frames: nf('outro', 1.4, 4), accent: GREEN,
    act: 'Prestige Car Wash', title: 'First Come, First Served', sub: 'No appointment · 6 lines · come see us' },
];

export const TOTAL = SCENES.reduce((a, s) => a + s.frames, 0);