diff --git a/marketing/product-film/src/Root.tsx b/marketing/product-film/src/Root.tsx
index 32f980d..48b76be 100644
--- a/marketing/product-film/src/Root.tsx
+++ b/marketing/product-film/src/Root.tsx
@@ -1,15 +1,16 @@
import { Composition } from "remotion";
import { Film } from "./Film";
+import { TOTAL_FRAMES, FPS, WIDTH, HEIGHT } from "./timing";
export const Root: React.FC = () => {
return (
);
};
diff --git a/marketing/product-film/src/timing.test.ts b/marketing/product-film/src/timing.test.ts
new file mode 100644
index 0000000..edd8504
--- /dev/null
+++ b/marketing/product-film/src/timing.test.ts
@@ -0,0 +1,22 @@
+import { describe, it, expect } from "vitest";
+import { DURATIONS, TOTAL_FRAMES, FPS, SEQUENCE_ORDER } from "./timing";
+
+describe("timing", () => {
+ it("uses 30 fps", () => {
+ expect(FPS).toBe(30);
+ });
+ it("has all 8 sequences in order", () => {
+ expect(SEQUENCE_ORDER).toEqual([
+ "coldOpen", "promise", "pillar1", "pillar2", "pillar3", "pillar4", "pillar5", "craneClose",
+ ]);
+ });
+ it("every duration is a positive integer number of frames", () => {
+ for (const key of SEQUENCE_ORDER) {
+ expect(DURATIONS[key]).toBeGreaterThan(0);
+ expect(Number.isInteger(DURATIONS[key])).toBe(true);
+ }
+ });
+ it("TOTAL_FRAMES equals 265 seconds at 30fps", () => {
+ expect(TOTAL_FRAMES).toBe(7950);
+ });
+});
diff --git a/marketing/product-film/src/timing.ts b/marketing/product-film/src/timing.ts
new file mode 100644
index 0000000..358c347
--- /dev/null
+++ b/marketing/product-film/src/timing.ts
@@ -0,0 +1,31 @@
+export const FPS = 30;
+export const WIDTH = 1920;
+export const HEIGHT = 1080;
+
+const sec = (s: number) => Math.round(s * FPS);
+
+export const SEQUENCE_ORDER = [
+ "coldOpen",
+ "promise",
+ "pillar1",
+ "pillar2",
+ "pillar3",
+ "pillar4",
+ "pillar5",
+ "craneClose",
+] as const;
+
+export type SequenceKey = (typeof SEQUENCE_ORDER)[number];
+
+export const DURATIONS: Record = {
+ coldOpen: sec(12),
+ promise: sec(8),
+ pillar1: sec(45),
+ pillar2: sec(45),
+ pillar3: sec(50),
+ pillar4: sec(45),
+ pillar5: sec(45),
+ craneClose: sec(15),
+};
+
+export const TOTAL_FRAMES = SEQUENCE_ORDER.reduce((acc, k) => acc + DURATIONS[k], 0);