← back to Prestige Car Wash Video
src/scenes/FcfsScene.tsx
64 lines
import { AbsoluteFill, Audio, staticFile, useCurrentFrame, interpolate, Easing, useVideoConfig, spring } from 'remotion';
import type { Scene } from '../story';
const FONT = 'Inter, "SF Pro Display", system-ui, -apple-system, sans-serif';
const Lane: React.FC<{ i: number; accent: string }> = ({ i, accent }) => {
const f = useCurrentFrame();
const start = 20 + i * 5;
// a car glides up each of the 6 lanes, staggered, then loops
const t = ((f - start) % 70) / 70;
const carY = interpolate(t < 0 ? 0 : t, [0, 1], [560, -80]);
const reveal = interpolate(f, [10 + i * 4, 30 + i * 4], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });
return (
<div style={{ position: 'relative', width: 150, height: 560, borderRadius: 16, overflow: 'hidden',
background: 'linear-gradient(180deg,#0f1c30,#0a1220)', border: '1px solid rgba(255,255,255,0.06)', opacity: reveal }}>
{/* lane dashes */}
{[...Array(6)].map((_, k) => (
<div key={k} style={{ position: 'absolute', left: '50%', top: 20 + k * 96, width: 8, height: 52, marginLeft: -4, borderRadius: 4, background: 'rgba(255,255,255,0.10)' }} />
))}
{f > start && <div style={{ position: 'absolute', left: 0, right: 0, top: carY, textAlign: 'center', fontSize: 62 }}>🚗</div>}
<div style={{ position: 'absolute', bottom: 12, left: 0, right: 0, textAlign: 'center', fontFamily: FONT, fontWeight: 800, fontSize: 22, color: accent }}>{i + 1}</div>
</div>
);
};
export const FcfsScene: React.FC<{ s: Scene }> = ({ s }) => {
const f = useCurrentFrame();
const { fps } = useVideoConfig();
const pop = spring({ frame: f, fps, config: { damping: 200 } });
const subO = interpolate(f, [16, 34], [0, 1], { extrapolateRight: 'clamp', extrapolateLeft: 'clamp' });
const strike = interpolate(f, [26, 46], [0, 1], { easing: Easing.out(Easing.cubic), extrapolateRight: 'clamp', extrapolateLeft: 'clamp' });
return (
<AbsoluteFill style={{ alignItems: 'center', justifyContent: 'center' }}>
{s.narr && <Audio src={staticFile(`audio/${s.narr}.wav`)} />}
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<div style={{ position: 'relative', opacity: pop, transform: `translateY(${interpolate(pop, [0, 1], [24, 0])}px)` }}>
<div style={{ fontFamily: FONT, fontWeight: 900, fontSize: 92, color: '#f4f9ff', letterSpacing: 1 }}>
No <span style={{ position: 'relative' }}>Appointments
<span style={{ position: 'absolute', left: 0, top: '54%', height: 8, width: `${strike * 100}%`, background: '#fb7185', borderRadius: 6 }} />
</span>
</div>
</div>
<div style={{ opacity: subO, marginTop: 14, fontFamily: FONT, fontWeight: 800, fontSize: 46, color: s.accent, letterSpacing: 2 }}>
First Come · First Served
</div>
<div style={{ display: 'flex', gap: 22, marginTop: 44 }}>
{[...Array(6)].map((_, i) => <Lane key={i} i={i} accent={s.accent} />)}
</div>
<div style={{ marginTop: 40, display: 'flex', gap: 46, opacity: subO, fontFamily: FONT }}>
{[['6', 'full lines'], ['Large', 'crew on deck'], ['0', 'wait to book']].map(([n, l]) => (
<div key={l} style={{ textAlign: 'center' }}>
<div style={{ fontWeight: 900, fontSize: 56, color: '#eaf2ff' }}>{n}</div>
<div style={{ fontWeight: 600, fontSize: 24, color: '#9fb3cd', letterSpacing: 1 }}>{l}</div>
</div>
))}
</div>
</div>
</AbsoluteFill>
);
};