diff --git a/services/web-ui/public/screens-home.jsx b/services/web-ui/public/screens-home.jsx
new file mode 100644
index 0000000..ae3ce87
--- /dev/null
+++ b/services/web-ui/public/screens-home.jsx
@@ -0,0 +1,182 @@
+// screens-home.jsx - home dashboard
+
+const { ACTIVITY, RECORDERS, JOBS, ASSETS } = window.ZAMPP_DATA;
+
+function Home({ navigate }) {
+ const sparkAssets = [12, 14, 13, 18, 16, 22, 25, 24, 28, 30, 32, 34, 38];
+ const sparkIngest = [4, 6, 5, 8, 9, 12, 10, 14, 13, 18, 20, 22, 24];
+ const sparkJobs = [8, 7, 10, 9, 14, 12, 16, 13, 18, 15, 12, 10, 11];
+ const sparkStorage = [42, 44, 46, 47, 48, 50, 52, 54, 56, 58, 60, 62, 64];
+
+ const liveRecorders = RECORDERS.filter(r => r.status === "recording").slice(0, 4);
+
+ return (
+
+
+
Good evening, Zach
+
4 recorders live · 3 jobs running · cluster healthy across 4 nodes
+
+
+
+
+
Library
+
1,553
+
+38 today
+
+
+
+
Ingest (24h)
+
412 GB
+
+82 GB last hr
+
+
+
+
Jobs queued
+
3 / 247 done
+
avg 4.2 min
+
+
+
+
Object store
+
64% of 18 TB
+
11.5 TB used
+
+
+
+
+
+
+
navigate("recorders")} moreLabel="All recorders" />
+
+ {liveRecorders.map((r, i) => (
+
navigate("recorders")}>
+
+ REC
+
+
+
+
+ {r.name}
+ {r.elapsed}
+
+
+ ))}
+
+
+
+
+
+
+ {ACTIVITY.map(a => (
+
+
+
+
+
+ {a.who} {a.what} {a.target}
+
+
{a.time}
+
+ ))}
+
+
+
+
+
navigate("jobs")} moreLabel="View all" />
+
+ {JOBS.slice(0, 5).map(j => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function SectionHead({ title, onMore, moreLabel = "View all" }) {
+ return (
+
+
{title}
+ {onMore && (
+
+ )}
+
+ );
+}
+
+function MiniJobRow({ job }) {
+ return (
+
+
+
+
+ {job.kind}
+ ·
+ {job.asset}
+
+ {job.status === "running" && (
+
+ )}
+ {job.status === "failed" && (
+
{job.error}
+ )}
+
+
+ {job.status === "running" ? job.eta : job.status}
+
+
+ );
+}
+
+function StorageBar() {
+ const segments = [
+ { label: "Master files", color: "#5B7CFA", value: 38 },
+ { label: "Proxies", color: "#2DD4A8", value: 12 },
+ { label: "Live captures", color: "#FF3B30", value: 8 },
+ { label: "Audio", color: "#B57CFA", value: 3 },
+ { label: "Other", color: "#6B7280", value: 3 },
+ ];
+ const total = segments.reduce((a, b) => a + b.value, 0);
+ const cap = 100;
+ return (
+
+
+
+ 11.5 TB / 18 TB
+
+
64% used
+
+
+ {segments.map((s, i) => (
+
+ ))}
+
+
+ {segments.map((s, i) => (
+
+
+ {s.label}
+ {s.value}%
+
+ ))}
+
+
+ );
+}
+
+function iconForKind(kind) {
+ return { comment: "comment", record: "record", job: "jobs", upload: "upload", sync: "refresh", approve: "check", error: "alert" }[kind] || "clock";
+}
+
+window.Home = Home;