diff --git a/web/src/components/StatusRing.tsx b/web/src/components/StatusRing.tsx
new file mode 100644
index 0000000..4b1051f
--- /dev/null
+++ b/web/src/components/StatusRing.tsx
@@ -0,0 +1,237 @@
+import { memo, useMemo } from "react";
+import { motion } from "motion/react";
+import type { LiveTarget } from "../lib/types";
+import { arcPath, circumference, latencyScale, polar } from "../lib/geometry";
+import { fmtLatency, fmtUptime, pctSign } from "../lib/format";
+import { springs, STATE_COLOR, useUI } from "../lib/ui";
+import { useSpringNumber } from "../hooks/useSpringNumber";
+
+const C = 60; // center
+const R_TRACK = 44; // uptime ring radius
+const R_LAT = 36; // latency gauge radius
+const LAT_SWEEP = 270; // latency gauge max sweep, degrees
+
+/** 60 watch-face ticks; every fifth is longer — the "fine instrument" detail. */
+function Ticks() {
+ const ticks = useMemo(() => {
+ const lines = [];
+ for (let i = 0; i < 60; i++) {
+ const major = i % 5 === 0;
+ const a = i * 6;
+ const p1 = polar(C, C, major ? 50 : 51.4, a);
+ const p2 = polar(C, C, 53, a);
+ lines.push(
+ ,
+ );
+ }
+ return lines;
+ }, []);
+ return {ticks};
+}
+
+interface StatusRingProps {
+ target: LiveTarget;
+ /** Uptime fraction 0..100 for the selected window. */
+ uptime?: number;
+ size?: number;
+ /** Mount-stagger delay in seconds (boot sequence). */
+ delay?: number;
+}
+
+/**
+ * Circular visualization #1 — the radial status ring.
+ * Uptime fills the outer arc; latency reads on an inner brass gauge;
+ * every completed probe lands as a visible pulse.
+ */
+export const StatusRing = memo(function StatusRing({
+ target,
+ uptime,
+ size = 150,
+ delay = 0,
+}: StatusRingProps) {
+ const { lang, reduced } = useUI();
+ const color = STATE_COLOR[target.state];
+
+ const frac = (uptime ?? 0) / 100;
+ const circ = circumference(R_TRACK);
+
+ const latMS = target.last?.latency_ms ?? target.latency.p50_ms ?? 0;
+ const latSweep = latencyScale(latMS) * LAT_SWEEP;
+ const latCirc = circumference(R_LAT);
+
+ const displayUptime = useSpringNumber(uptime ?? 0);
+
+ const arcTransition = reduced
+ ? { duration: 0 }
+ : { ...springs.arc, delay };
+
+ return (
+
+ );
+});