diff --git a/apps/videos/src/components/FeaturePill.tsx b/apps/videos/src/components/FeaturePill.tsx
new file mode 100644
index 00000000..d2b751f9
--- /dev/null
+++ b/apps/videos/src/components/FeaturePill.tsx
@@ -0,0 +1,55 @@
+import type React from "react";
+import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
+import { COLOR } from "@/lib/colors";
+import { FONT } from "@/lib/fonts";
+import { SPRING } from "@/lib/motion";
+
+export const FeaturePill: React.FC<{
+ label: string;
+ enterFrame: number;
+ targetX: number;
+ targetY: number;
+ fromDirection?: "left" | "right" | "top" | "bottom";
+}> = ({ label, enterFrame, targetX, targetY, fromDirection = "left" }) => {
+ const frame = useCurrentFrame();
+ const { fps } = useVideoConfig();
+
+ const progress = spring({
+ frame: frame - enterFrame,
+ fps,
+ config: SPRING.popIn,
+ });
+
+ const offscreen = {
+ left: { x: -300, y: targetY },
+ right: { x: 1400, y: targetY },
+ top: { x: targetX, y: -100 },
+ bottom: { x: targetX, y: 1200 },
+ }[fromDirection];
+
+ const x = interpolate(progress, [0, 1], [offscreen.x, targetX]);
+ const y = interpolate(progress, [0, 1], [offscreen.y, targetY]);
+ const scale = interpolate(progress, [0, 1], [0.8, 1]);
+
+ return (
+
+ {label}
+
+ );
+};
diff --git a/apps/videos/src/components/NumberPunch.tsx b/apps/videos/src/components/NumberPunch.tsx
new file mode 100644
index 00000000..57039487
--- /dev/null
+++ b/apps/videos/src/components/NumberPunch.tsx
@@ -0,0 +1,72 @@
+import type React from "react";
+import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
+import { COLOR } from "@/lib/colors";
+import { TEXT } from "@/lib/fonts";
+import { SPRING } from "@/lib/motion";
+
+export const NumberPunch: React.FC<{
+ number: string;
+ descriptor: string;
+ enterFrame: number;
+ numberSize?: number;
+ shakeIntensity?: number;
+}> = ({ number, descriptor, enterFrame, numberSize = 120, shakeIntensity = 2 }) => {
+ const frame = useCurrentFrame();
+ const { fps } = useVideoConfig();
+
+ const elapsed = frame - enterFrame;
+ const scaleSpring = spring({ frame: elapsed, fps, config: SPRING.settle });
+ const scale = interpolate(scaleSpring, [0, 1], [1.5, 1]);
+
+ const shakeDecay = interpolate(elapsed, [0, 12], [1, 0], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ });
+ const shakeX = Math.sin(elapsed * 3) * shakeIntensity * shakeDecay;
+ const shakeY = Math.cos(elapsed * 4) * shakeIntensity * shakeDecay;
+
+ const descOpacity = interpolate(elapsed, [8, 18], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ });
+
+ const numberOpacity = interpolate(elapsed, [0, 4], [0, 1], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ });
+
+ return (
+
+
+ {number}
+
+
+ {descriptor}
+
+
+ );
+};
diff --git a/apps/videos/src/components/ProgressBar.tsx b/apps/videos/src/components/ProgressBar.tsx
new file mode 100644
index 00000000..adcccfa8
--- /dev/null
+++ b/apps/videos/src/components/ProgressBar.tsx
@@ -0,0 +1,50 @@
+import type React from "react";
+import { interpolate, useCurrentFrame } from "remotion";
+import { EASE } from "@/lib/motion";
+
+export const ProgressBar: React.FC<{
+ startFrame: number;
+ duration: number;
+ width?: number;
+ height?: number;
+ color?: string;
+ bgColor?: string;
+ style?: React.CSSProperties;
+}> = ({
+ startFrame,
+ duration,
+ width = 200,
+ height = 6,
+ color = "#3b82f6",
+ bgColor = "rgba(255,255,255,0.1)",
+ style,
+}) => {
+ const frame = useCurrentFrame();
+ const progress = interpolate(frame, [startFrame, startFrame + duration], [0, 100], {
+ extrapolateLeft: "clamp",
+ extrapolateRight: "clamp",
+ easing: EASE.smooth,
+ });
+
+ return (
+
+ );
+};