← back to Crcp Pitch Video

src/Video.tsx

340 lines

import React from 'react';
import {
  AbsoluteFill, Sequence, useCurrentFrame, useVideoConfig, interpolate, spring, Easing,
} from 'remotion';

export const FPS = 30;
export const TOTAL_FRAMES = 5400; // 180s

// ── palette ──
const C = {
  bg: '#0a0d13', bg2: '#11151f', card: '#161b26', line: '#2a3242',
  ink: '#EAECEF', mut: '#8A93A2', gold: '#C8A24B', goldD: '#9c7a2f',
  blue: '#58a6ff', green: '#6BCB77', red: '#f0616a',
};
const SERIF = 'Georgia, "Times New Roman", serif';
const SANS = '-apple-system, "Helvetica Neue", Arial, sans-serif';

// ── helpers ──
const useFade = (inAt: number, dur = 18, outAt?: number, outDur = 18) => {
  const f = useCurrentFrame();
  let o = interpolate(f, [inAt, inAt + dur], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });
  if (outAt != null) o *= interpolate(f, [outAt, outAt + outDur], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });
  return o;
};
const useRise = (inAt: number, dist = 40) => {
  const f = useCurrentFrame();
  const p = interpolate(f, [inAt, inAt + 22], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic) });
  return (1 - p) * dist;
};

const Bg: React.FC<{ tone?: string }> = ({ tone = C.bg }) => {
  const f = useCurrentFrame();
  const drift = interpolate(f, [0, 300], [0, 30]);
  return (
    <AbsoluteFill style={{ background: `radial-gradient(1200px 700px at ${50 + drift / 6}% 18%, ${C.bg2} 0%, ${tone} 62%)` }}>
      <AbsoluteFill style={{
        backgroundImage: `linear-gradient(${C.line}22 1px, transparent 1px), linear-gradient(90deg, ${C.line}22 1px, transparent 1px)`,
        backgroundSize: '64px 64px', opacity: 0.5,
      }} />
      <AbsoluteFill style={{ background: `linear-gradient(180deg, transparent 55%, ${tone} 100%)` }} />
    </AbsoluteFill>
  );
};

const Kicker: React.FC<{ children: React.ReactNode; delay?: number; color?: string }> = ({ children, delay = 0, color = C.gold }) => (
  <div style={{
    fontFamily: SANS, fontSize: 22, letterSpacing: 6, textTransform: 'uppercase', color,
    opacity: useFade(delay), transform: `translateY(${useRise(delay, 20)}px)`, fontWeight: 700,
  }}>{children}</div>
);

const Counter: React.FC<{ to: number; delay: number; fmt?: (n: number) => string }> = ({ to, delay, fmt }) => {
  const f = useCurrentFrame();
  const v = Math.round(interpolate(f, [delay, delay + 55], [0, to], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic) }));
  return <>{fmt ? fmt(v) : v.toLocaleString()}</>;
};

// ═══════════════ SCENE 1 — TITLE ═══════════════
const S1: React.FC = () => {
  const { fps } = useVideoConfig();
  const f = useCurrentFrame();
  const logo = spring({ frame: f - 4, fps, config: { damping: 14, mass: 0.8 } });
  return (
    <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center', textAlign: 'center' }}>
      <Bg />
      <div style={{ transform: `scale(${0.9 + logo * 0.1})`, opacity: useFade(4) }}>
        <div style={{
          fontFamily: SERIF, fontSize: 46, color: C.gold, letterSpacing: 10, fontWeight: 700,
          border: `2px solid ${C.gold}`, borderRadius: 14, padding: '14px 30px', display: 'inline-block',
        }}>CRCP</div>
      </div>
      <div style={{ fontFamily: SERIF, fontSize: 84, color: C.ink, marginTop: 34, fontWeight: 700, letterSpacing: 1, opacity: useFade(24), transform: `translateY(${useRise(24)}px)` }}>
        Every licensed agent.<br />America's priciest markets.
      </div>
      <div style={{ fontFamily: SANS, fontSize: 30, color: C.mut, marginTop: 30, opacity: useFade(54), maxWidth: 1100 }}>
        The prospecting platform for loan officers, escrow &amp; title professionals.
      </div>
      <div style={{ fontFamily: SANS, fontSize: 22, color: C.gold, marginTop: 46, letterSpacing: 3, opacity: useFade(80) }}>
        crcp.agentabrams.com
      </div>
    </AbsoluteFill>
  );
};

// ═══════════════ SCENE 2 — THE PROBLEM ═══════════════
const S2: React.FC = () => {
  const lines = [
    'Your business runs on ONE thing:',
    'relationships with the real-estate agents who close deals.',
  ];
  return (
    <AbsoluteFill style={{ justifyContent: 'center', paddingLeft: 150, paddingRight: 150 }}>
      <Bg />
      <Kicker delay={4}>The reality</Kicker>
      <div style={{ marginTop: 30 }}>
        {lines.map((t, i) => (
          <div key={i} style={{
            fontFamily: SERIF, fontSize: i === 0 ? 46 : 60, color: i === 0 ? C.mut : C.ink, lineHeight: 1.2,
            marginTop: i ? 10 : 0, fontWeight: i ? 700 : 400,
            opacity: useFade(20 + i * 26), transform: `translateY(${useRise(20 + i * 26)}px)`,
          }}>{t}</div>
        ))}
      </div>
      <div style={{ fontFamily: SANS, fontSize: 32, color: C.gold, marginTop: 54, opacity: useFade(96), lineHeight: 1.5 }}>
        But <b>which</b> agents? <b>Where?</b> And how do you reach them<br />before every other rep in town does?
      </div>
    </AbsoluteFill>
  );
};

// ═══════════════ SCENE 3 — WHAT IT IS ═══════════════
const Stat: React.FC<{ big: React.ReactNode; label: string; delay: number }> = ({ big, label, delay }) => (
  <div style={{ opacity: useFade(delay), transform: `translateY(${useRise(delay, 30)}px)`, textAlign: 'center' }}>
    <div style={{ fontFamily: SERIF, fontSize: 96, color: C.gold, fontWeight: 700, lineHeight: 1 }}>{big}</div>
    <div style={{ fontFamily: SANS, fontSize: 24, color: C.mut, marginTop: 12, letterSpacing: 2, textTransform: 'uppercase' }}>{label}</div>
  </div>
);
const S3: React.FC = () => {
  const cities = ['Beverly Hills', 'Manhattan', 'Aspen', 'Naples', 'Silicon Valley', 'Greenwich', 'Malibu', 'Palm Beach', 'The Hamptons', 'Vail', 'Newport Beach', 'Telluride', 'Miami', 'San Francisco'];
  return (
    <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center' }}>
      <Bg />
      <Kicker delay={2}>Meet CRCP</Kicker>
      <div style={{ display: 'flex', gap: 120, marginTop: 44 }}>
        <Stat big={<Counter to={355354} delay={16} />} label="Licensed agents" delay={12} />
        <Stat big={<Counter to={21} delay={16} />} label="Elite markets" delay={22} />
        <Stat big={<><Counter to={20} delay={16} /></>} label="Priciest US cities" delay={32} />
      </div>
      <div style={{ fontFamily: SANS, fontSize: 26, color: C.ink, marginTop: 40, opacity: useFade(60), maxWidth: 1300, textAlign: 'center', lineHeight: 1.5 }}>
        Backed by <b style={{ color: C.green }}>government license data</b> — real, verified licenses from the
        DRE, DOS, DBPR &amp; more. Not scraped guesses.
      </div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 14, justifyContent: 'center', maxWidth: 1400, marginTop: 40 }}>
        {cities.map((c, i) => {
          const d = 78 + i * 5;
          return (
            <div key={c} style={{
              fontFamily: SANS, fontSize: 24, color: C.gold, border: `1px solid ${C.goldD}`,
              borderRadius: 30, padding: '10px 22px', background: `${C.gold}12`,
              opacity: useFade(d), transform: `translateY(${useRise(d, 18)}px)`,
            }}>{c}</div>
          );
        })}
      </div>
      <div style={{ fontFamily: SANS, fontSize: 24, color: C.mut, marginTop: 40, opacity: useFade(170), letterSpacing: 1 }}>
        Search any market → every agent, their brokerage, license &amp; listings.
      </div>
    </AbsoluteFill>
  );
};

// ═══════════════ SCENE 4 — WHY IT'S VALUABLE (by role) ═══════════════
const RoleCard: React.FC<{ icon: string; role: string; line: string; delay: number }> = ({ icon, role, line, delay }) => {
  const { fps } = useVideoConfig();
  const f = useCurrentFrame();
  const s = spring({ frame: f - delay, fps, config: { damping: 15 } });
  return (
    <div style={{
      background: C.card, border: `1px solid ${C.line}`, borderRadius: 18, padding: '30px 34px', width: 1180,
      display: 'flex', gap: 26, alignItems: 'center',
      opacity: interpolate(s, [0, 1], [0, 1]), transform: `translateX(${(1 - s) * -60}px)`,
    }}>
      <div style={{ fontSize: 60, width: 78, textAlign: 'center' }}>{icon}</div>
      <div>
        <div style={{ fontFamily: SANS, fontSize: 30, color: C.gold, fontWeight: 700, letterSpacing: 1 }}>{role}</div>
        <div style={{ fontFamily: SANS, fontSize: 27, color: C.ink, marginTop: 8, lineHeight: 1.4 }}>{line}</div>
      </div>
    </div>
  );
};
const S4: React.FC = () => (
  <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center' }}>
    <Bg />
    <div style={{ opacity: useFade(2) }}><Kicker delay={2}>Why it matters to you</Kicker></div>
    <div style={{ display: 'flex', flexDirection: 'column', gap: 26, marginTop: 44 }}>
      <RoleCard icon="🏦" role="LOAN OFFICERS" delay={20}
        line="Agents send you borrowers. Target the top producers in luxury markets — where one deal is a jumbo loan." />
      <RoleCard icon="📜" role="ESCROW OFFICERS" delay={90}
        line="Agents choose escrow. Get in front of the highest-volume agents before your competitor does." />
      <RoleCard icon="🏛️" role="TITLE REPS" delay={160}
        line="Title follows relationships. Build yours with the agents controlling the priciest inventory in America." />
    </div>
    <div style={{ fontFamily: SERIF, fontSize: 34, color: C.gold, marginTop: 48, opacity: useFade(240), fontStyle: 'italic' }}>
      One platform. Every deal-maker. Before anyone else finds them.
    </div>
  </AbsoluteFill>
);

// ═══════════════ SCENE 5 — HOW TO APPROACH ═══════════════
const Channel: React.FC<{ icon: string; name: string; tip: string; delay: number }> = ({ icon, name, tip, delay }) => {
  const { fps } = useVideoConfig();
  const f = useCurrentFrame();
  const s = spring({ frame: f - delay, fps, config: { damping: 16, mass: 0.9 } });
  return (
    <div style={{
      background: C.card, border: `1px solid ${C.line}`, borderRadius: 16, padding: '26px 22px', width: 400, height: 300,
      opacity: interpolate(s, [0, 1], [0, 1]), transform: `scale(${interpolate(s, [0, 1], [0.85, 1])})`,
      display: 'flex', flexDirection: 'column',
    }}>
      <div style={{ fontSize: 52 }}>{icon}</div>
      <div style={{ fontFamily: SANS, fontSize: 30, color: C.blue, fontWeight: 700, marginTop: 12, letterSpacing: 1 }}>{name}</div>
      <div style={{ fontFamily: SANS, fontSize: 23, color: C.ink, marginTop: 12, lineHeight: 1.45 }}>{tip}</div>
    </div>
  );
};
const S5: React.FC = () => (
  <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center' }}>
    <Bg />
    <Kicker delay={2}>The outreach playbook</Kicker>
    <div style={{ fontFamily: SERIF, fontSize: 44, color: C.ink, marginTop: 18, opacity: useFade(14) }}>
      Four channels. One relationship.
    </div>
    <div style={{ display: 'flex', gap: 22, marginTop: 40, flexWrap: 'wrap', justifyContent: 'center', maxWidth: 1720 }}>
      <Channel icon="📞" name="CALL" delay={30} tip="Warm, personal, fast. Reference a recent listing of theirs. Reserve for your highest-value agents." />
      <Channel icon="✉️" name="EMAIL" delay={54} tip="Scalable. Lead with value — a market stat or co-marketing offer. Short. One ask. Follow up twice." />
      <Channel icon="✉️📮" name="POSTCARD" delay={78} tip="Cuts through digital noise. Luxury agents notice physical mail. Branded, monthly, on-message." />
      <Channel icon="in" name="LINKEDIN" delay={102} tip="Connect + engage before pitching. Comment on their wins. Relationship-first, never a cold pitch." />
    </div>
    <div style={{
      fontFamily: SANS, fontSize: 27, color: C.gold, marginTop: 46, opacity: useFade(150),
      background: `${C.gold}10`, border: `1px solid ${C.goldD}`, borderRadius: 40, padding: '14px 34px',
    }}>
      Best cadence: <b>LinkedIn connect → value email → follow-up call → postcard reminder.</b>
    </div>
  </AbsoluteFill>
);

// ═══════════════ SCENE 6 — CONTACT + CTA ═══════════════
const S6: React.FC = () => {
  const f = useCurrentFrame();
  const rows = [
    ['On every record', 'Name · brokerage · license # · city · listings'],
    ['Enrich for direct contact', 'Brokerage site → LinkedIn → skip-trace for phone/email'],
    ['Start here', 'The agents closing the biggest deals in your market'],
  ];
  return (
    <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center', textAlign: 'center' }}>
      <Bg />
      <Kicker delay={2}>Getting the contact info</Kicker>
      <div style={{ marginTop: 34, display: 'flex', flexDirection: 'column', gap: 14 }}>
        {rows.map((r, i) => (
          <div key={i} style={{
            display: 'flex', gap: 20, alignItems: 'center', justifyContent: 'center',
            opacity: useFade(14 + i * 16), transform: `translateY(${useRise(14 + i * 16, 22)}px)`,
          }}>
            <span style={{ fontFamily: SANS, fontSize: 24, color: C.gold, fontWeight: 700, width: 340, textAlign: 'right' }}>{r[0]}</span>
            <span style={{ color: C.line }}>│</span>
            <span style={{ fontFamily: SANS, fontSize: 26, color: C.ink, width: 640, textAlign: 'left' }}>{r[1]}</span>
          </div>
        ))}
      </div>
      <div style={{ fontFamily: SERIF, fontSize: 62, color: C.ink, marginTop: 60, fontWeight: 700, opacity: useFade(80) }}>
        Reach the right agents first.
      </div>
      <div style={{
        fontFamily: SANS, fontSize: 30, color: C.bg, marginTop: 34, opacity: useFade(100), fontWeight: 700,
        background: C.gold, borderRadius: 12, padding: '16px 40px', letterSpacing: 2,
      }}>
        crcp.agentabrams.com
      </div>
    </AbsoluteFill>
  );
};

// ═══════════════ 60-SECOND SOCIAL CUT ═══════════════
export const SHORT_FRAMES = 1800; // 60s
const ShortStats: React.FC = () => {
  const cities = ['Beverly Hills', 'Manhattan', 'Aspen', 'Naples', 'Greenwich', 'Malibu', 'Palm Beach', 'The Hamptons', 'Vail', 'Miami'];
  return (
    <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center' }}>
      <Bg />
      <Kicker delay={2}>Meet CRCP</Kicker>
      <div style={{ display: 'flex', gap: 90, marginTop: 34 }}>
        <Stat big={<Counter to={355354} delay={10} />} label="Licensed agents" delay={8} />
        <Stat big={<Counter to={20} delay={10} />} label="Priciest US cities" delay={16} />
      </div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, justifyContent: 'center', maxWidth: 1300, marginTop: 44 }}>
        {cities.map((c, i) => {
          const d = 40 + i * 6;
          return <div key={c} style={{ fontFamily: SANS, fontSize: 24, color: C.gold, border: `1px solid ${C.goldD}`, borderRadius: 30, padding: '9px 20px', background: `${C.gold}12`, opacity: useFade(d), transform: `translateY(${useRise(d, 16)}px)` }}>{c}</div>;
        })}
      </div>
      <div style={{ fontFamily: SANS, fontSize: 26, color: C.ink, marginTop: 42, opacity: useFade(120), maxWidth: 1200, textAlign: 'center' }}>
        Every agent in America's priciest markets — <b style={{ color: C.green }}>government-verified.</b>
      </div>
    </AbsoluteFill>
  );
};
const ShortRoles: React.FC = () => (
  <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center' }}>
    <Bg />
    <Kicker delay={2}>Built for you</Kicker>
    <div style={{ display: 'flex', flexDirection: 'column', gap: 22, marginTop: 40 }}>
      <RoleCard icon="🏦" role="LOAN OFFICERS" delay={14} line="Target the top producers — where one deal is a jumbo loan." />
      <RoleCard icon="📜" role="ESCROW OFFICERS" delay={40} line="Reach the highest-volume agents before your competitor does." />
      <RoleCard icon="🏛️" role="TITLE REPS" delay={66} line="Build relationships with the agents controlling the priciest inventory." />
    </div>
  </AbsoluteFill>
);
const ShortChannels: React.FC = () => (
  <AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center' }}>
    <Bg />
    <Kicker delay={2}>Reach them</Kicker>
    <div style={{ display: 'flex', gap: 20, marginTop: 40 }}>
      {[['📞', 'CALL'], ['✉️', 'EMAIL'], ['📮', 'POSTCARD'], ['in', 'LINKEDIN']].map(([ic, nm], i) => (
        <div key={nm} style={{ background: C.card, border: `1px solid ${C.line}`, borderRadius: 16, padding: '26px 30px', textAlign: 'center', opacity: useFade(14 + i * 8), transform: `scale(${interpolate(useFade(14 + i * 8), [0, 1], [0.85, 1])})` }}>
          <div style={{ fontSize: 46 }}>{ic}</div>
          <div style={{ fontFamily: SANS, fontSize: 26, color: C.blue, fontWeight: 700, marginTop: 8 }}>{nm}</div>
        </div>
      ))}
    </div>
    <div style={{ fontFamily: SANS, fontSize: 26, color: C.gold, marginTop: 40, opacity: useFade(70) }}>
      LinkedIn → email → call → postcard.
    </div>
  </AbsoluteFill>
);
export const CRCPShort: React.FC = () => (
  <AbsoluteFill style={{ background: C.bg }}>
    <Sequence from={0} durationInFrames={150}><S1 /></Sequence>
    <Sequence from={150} durationInFrames={540}><ShortStats /></Sequence>
    <Sequence from={690} durationInFrames={600}><ShortRoles /></Sequence>
    <Sequence from={1290} durationInFrames={300}><ShortChannels /></Sequence>
    <Sequence from={1590} durationInFrames={210}><S6 /></Sequence>
  </AbsoluteFill>
);

// ═══════════════ ROOT SEQUENCER ═══════════════
export const CRCPPitch: React.FC = () => {
  return (
    <AbsoluteFill style={{ background: C.bg }}>
      <Sequence from={0} durationInFrames={390}><S1 /></Sequence>
      <Sequence from={390} durationInFrames={690}><S2 /></Sequence>
      <Sequence from={1080} durationInFrames={1290}><S3 /></Sequence>
      <Sequence from={2370} durationInFrames={1440}><S4 /></Sequence>
      <Sequence from={3810} durationInFrames={1260}><S5 /></Sequence>
      <Sequence from={5070} durationInFrames={330}><S6 /></Sequence>
    </AbsoluteFill>
  );
};