diff --git a/services/web-ui/public/screens-editor.jsx b/services/web-ui/public/screens-editor.jsx
new file mode 100644
index 0000000..259cbe1
--- /dev/null
+++ b/services/web-ui/public/screens-editor.jsx
@@ -0,0 +1,186 @@
+// screens-editor.jsx — Editor (timeline) stub
+const { ASSETS } = window.ZAMPP_DATA;
+
+function Editor() {
+ const [playing, setPlaying] = React.useState(false);
+ const [currentMs, setCurrentMs] = React.useState(8200);
+
+ React.useEffect(() => {
+ if (!playing) return;
+ const i = setInterval(() => setCurrentMs(t => t + 100), 100);
+ return () => clearInterval(i);
+ }, [playing]);
+
+ return (
+
+
+
DEV BUILD
+
Untitled sequence
+
Protour 2026 · auto-saved 1m ago
+
+
+
+
+
+
+
+
+
+
+
+ {!playing && (
+
+ )}
+
00:00:08:06
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function InspGroup({ title, children }) {
+ return (
+
+ );
+}
+function InspRow({ label, value }) {
+ return (
+
+ {label}
+ {value}
+
+ );
+}
+
+function EditorTimeline({ currentMs }) {
+ const v1Clips = [
+ { id: "v1a", start: 0, len: 12, color: "#5B7CFA", label: "Stage_Cam_A" },
+ { id: "v1b", start: 12, len: 8, color: "#5B7CFA", label: "Drone_Aerial" },
+ { id: "v1c", start: 20, len: 18, color: "#5B7CFA", label: "Wide_Cam_B" },
+ { id: "v1d", start: 38, len: 10, color: "#5B7CFA", label: "Trophy" },
+ ];
+ const v2Clips = [
+ { id: "v2a", start: 6, len: 4, color: "#B57CFA", label: "Lower3rd" },
+ { id: "v2b", start: 32, len: 4, color: "#B57CFA", label: "Sponsor" },
+ ];
+ const a1Clips = [
+ { id: "a1a", start: 0, len: 48, color: "#2DD4A8", label: "FOH Mix", audio: true },
+ ];
+ const a2Clips = [
+ { id: "a2a", start: 12, len: 6, color: "#F5A623", label: "VO", audio: true },
+ ];
+ const total = 60;
+ const playheadPct = ((currentMs / 1000) / total) * 100;
+
+ return (
+
+
+
Timeline · 1m total
+
+
+
+
+
+
+
+ {Array.from({ length: 13 }).map((_, i) => (
+
+ {i % 2 === 0 && {`00:${String(i * 5).padStart(2, "0")}`}}
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
+
+function TimelineTrack({ label, clips, total, hasFilm, audio }) {
+ return (
+
+
{label}
+
+ {clips.map(c => {
+ const left = (c.start / total) * 100;
+ const width = (c.len / total) * 100;
+ return (
+
+
{c.label}
+ {hasFilm && (
+
+ {Array.from({ length: 8 }).map((_, i) => (
+
+ ))}
+
+ )}
+ {audio && (
+
+
+
+ )}
+
+ );
+ })}
+
+
+ );
+}
+
+window.Editor = Editor;