diff --git a/marketing/product-film/src/components/KenBurns.tsx b/marketing/product-film/src/components/KenBurns.tsx
new file mode 100644
index 0000000..dfce3f5
--- /dev/null
+++ b/marketing/product-film/src/components/KenBurns.tsx
@@ -0,0 +1,15 @@
+import { useCurrentFrame, useVideoConfig, interpolate } from "remotion";
+
+// Slow zoom/pan over its children across the whole sequence the component lives in.
+export const KenBurns: React.FC<{
+ children: React.ReactNode;
+ from?: number;
+ to?: number;
+ durationInFrames: number;
+}> = ({ children, from = 1.0, to = 1.08, durationInFrames }) => {
+ const frame = useCurrentFrame();
+ const scale = interpolate(frame, [0, durationInFrames], [from, to], {
+ extrapolateRight: "clamp",
+ });
+ return
{children}
;
+};
diff --git a/marketing/product-film/src/components/ScreenFrame.tsx b/marketing/product-film/src/components/ScreenFrame.tsx
new file mode 100644
index 0000000..5f4b4ac
--- /dev/null
+++ b/marketing/product-film/src/components/ScreenFrame.tsx
@@ -0,0 +1,48 @@
+import { useState } from "react";
+import { AbsoluteFill, staticFile } from "remotion";
+import { theme } from "../theme";
+
+export const ScreenFrame: React.FC<{
+ captureId: string;
+ scale?: number;
+}> = ({ captureId, scale = 0.86 }) => {
+ const [errored, setErrored] = useState(false);
+ const src = staticFile(`captures/${captureId}.png`);
+
+ return (
+
+
+
+
+
+
+ app.lynkeduppro.com
+
+
+ {errored ? (
+
+ [capture missing: {captureId}]
+
+ ) : (
+

setErrored(true)}
+ style={{ width: "100%", height: "100%", objectFit: "cover", objectPosition: "top center", display: "block" }}
+ />
+ )}
+
+
+
+ );
+};