Files
hermes-mcp/videos/remotion-demo/src/YCAppVideo.tsx
garfieldheron 22117a34a8 feat: YC application answers + Remotion video component + gitignore updates
- Add YC_APPLICATION_ANSWERS.md with full Summer 2026 application draft
  (all fields answered, updated with mortgage broker user interview quote)
- Wire OffthreadVideo into YCAppVideo.tsx for founder recording playback
- Ignore .mov files and .gstack/ in .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:20:17 -04:00

360 lines
8.8 KiB
TypeScript

import {
AbsoluteFill,
OffthreadVideo,
Sequence,
staticFile,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
} from "remotion";
import { COLORS, FONT, SPRING_CFG } from "./styles";
// ─── CONFIG ──────────────────────────────────────────────────────────
const INTRO_DURATION = 3; // seconds
const OUTRO_DURATION = 2; // seconds
const TOTAL_DURATION = 60; // seconds (YC wants ~1 min)
const TALKING_HEAD_DURATION = TOTAL_DURATION - INTRO_DURATION - OUTRO_DURATION;
// ─── INTRO CARD ──────────────────────────────────────────────────────
const IntroCard = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const nameY = spring({
frame,
fps,
config: SPRING_CFG,
from: 60,
to: 0,
delay: 0,
});
const companyY = spring({
frame,
fps,
config: SPRING_CFG,
from: 40,
to: 0,
delay: 5,
});
const taglineOpacity = interpolate(
frame,
[1.5 * fps, 2 * fps],
[0, 1],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);
const fadeOut = interpolate(
frame,
[(INTRO_DURATION - 0.4) * fps, INTRO_DURATION * fps],
[1, 0],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);
return (
<AbsoluteFill
style={{
background: `radial-gradient(ellipse at 50% 40%, ${COLORS.bgCard} 0%, ${COLORS.bg} 70%)`,
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
opacity: fadeOut,
fontFamily: FONT,
}}
>
{/* Accent glow */}
<div
style={{
position: "absolute",
width: 400,
height: 400,
borderRadius: "50%",
background: `radial-gradient(circle, ${COLORS.accentGlow} 0%, transparent 70%)`,
top: "30%",
left: "50%",
transform: "translate(-50%, -50%)",
}}
/>
{/* Founder name */}
<div
style={{
fontSize: 72,
fontWeight: 800,
color: COLORS.text,
textAlign: "center",
letterSpacing: "-0.02em",
transform: `translateY(${nameY}px)`,
textShadow: `0 0 60px ${COLORS.accentGlow}`,
}}
>
Garfield Heron
</div>
{/* Company name */}
<div
style={{
fontSize: 40,
fontWeight: 700,
color: COLORS.accentLight,
marginTop: 16,
transform: `translateY(${companyY}px)`,
display: "flex",
alignItems: "center",
gap: 12,
}}
>
<span style={{ color: COLORS.accent }}></span>
SquareMCP
</div>
{/* Subtitle */}
<div
style={{
fontSize: 20,
fontWeight: 400,
color: COLORS.textSecondary,
marginTop: 24,
opacity: taglineOpacity,
}}
>
Y Combinator Application
</div>
</AbsoluteFill>
);
};
// ─── VIDEO PLACEHOLDER (subtle frame for your recording) ─────────────
const VideoFrame = ({ children }: { children?: React.ReactNode }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// Fade in from intro
const fadeIn = interpolate(
frame,
[0, 0.5 * fps],
[0, 1],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);
// Fade out to outro
const fadeOut = interpolate(
frame,
[(TALKING_HEAD_DURATION - 0.5) * fps, TALKING_HEAD_DURATION * fps],
[1, 0],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);
return (
<AbsoluteFill
style={{
background: "#000000",
opacity: fadeIn * fadeOut,
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
{/* Corner accents */}
<div
style={{
position: "absolute",
inset: 20,
border: `1px solid ${COLORS.borderBlue}`,
borderRadius: 8,
pointerEvents: "none",
}}
/>
{/* Corner dots */}
{[
{ top: 16, left: 16 },
{ top: 16, right: 16 },
{ bottom: 16, left: 16 },
{ bottom: 16, right: 16 },
].map((pos, i) => (
<div
key={i}
style={{
position: "absolute",
width: 6,
height: 6,
borderRadius: "50%",
background: COLORS.accent,
...pos,
}}
/>
))}
{/* Lower third - name + company */}
<div
style={{
position: "absolute",
bottom: 40,
left: 40,
right: 40,
display: "flex",
justifyContent: "space-between",
alignItems: "flex-end",
fontFamily: FONT,
pointerEvents: "none",
}}
>
<div>
<div style={{ fontSize: 18, fontWeight: 700, color: COLORS.text }}>
Garfield Heron
</div>
<div style={{ fontSize: 14, fontWeight: 400, color: COLORS.textSecondary, marginTop: 2 }}>
Founder, SquareMCP
</div>
</div>
<div style={{ fontSize: 14, fontWeight: 600, color: COLORS.accentLight }}>
squaremcp.com
</div>
</div>
{/* Placeholder text (remove when you overlay your actual video) */}
{!children && (
<div
style={{
fontFamily: FONT,
color: COLORS.textSecondary,
fontSize: 24,
textAlign: "center",
}}
>
<div>YOUR RECORDING GOES HERE</div>
<div style={{ fontSize: 16, marginTop: 12, opacity: 0.6 }}>
Record yourself talking, then overlay it in Remotion
</div>
</div>
)}
{children}
</AbsoluteFill>
);
};
// ─── OUTRO CARD ──────────────────────────────────────────────────────
const OutroCard = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const outroStart = (TOTAL_DURATION - OUTRO_DURATION) * fps;
const localFrame = frame - outroStart;
const scale = spring({
frame: localFrame,
fps,
config: SPRING_CFG,
from: 0.9,
to: 1,
});
const opacity = interpolate(
localFrame,
[0, 0.5 * fps],
[0, 1],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);
return (
<AbsoluteFill
style={{
background: COLORS.bg,
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
opacity,
transform: `scale(${scale})`,
fontFamily: FONT,
}}
>
{/* Logo mark */}
<div
style={{
width: 64,
height: 64,
borderRadius: 16,
background: COLORS.accent,
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 28,
fontWeight: 800,
color: "#fff",
}}
>
</div>
<div
style={{
fontSize: 48,
fontWeight: 800,
color: COLORS.text,
marginTop: 24,
letterSpacing: "-0.02em",
}}
>
SquareMCP
</div>
<div
style={{
fontSize: 22,
fontWeight: 400,
color: COLORS.textSecondary,
marginTop: 12,
}}
>
One API. Every Platform.
</div>
<div
style={{
fontSize: 16,
color: COLORS.accentLight,
marginTop: 32,
fontWeight: 600,
}}
>
squaremcp.com
</div>
</AbsoluteFill>
);
};
// ─── MAIN COMPOSITION ────────────────────────────────────────────────
export const YCAppVideo = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const showIntro = frame < INTRO_DURATION * fps;
const showOutro = frame >= (TOTAL_DURATION - OUTRO_DURATION) * fps;
return (
<AbsoluteFill style={{ background: "#000" }}>
{showIntro && <IntroCard />}
<Sequence
from={INTRO_DURATION * fps}
durationInFrames={TALKING_HEAD_DURATION * fps}
>
<VideoFrame>
<OffthreadVideo
src={staticFile("yc-founder-recording.mov")}
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
/>
</VideoFrame>
</Sequence>
{showOutro && <OutroCard />}
</AbsoluteFill>
);
};