diff --git a/src/components/ServerTable.tsx b/src/components/ServerTable.tsx
index 1ecd696..061b856 100644
--- a/src/components/ServerTable.tsx
+++ b/src/components/ServerTable.tsx
@@ -105,6 +105,37 @@ function Bar({ pct, label }: { pct: number | null; label?: string }) {
)
}
+// The widest form each formatter writes, in `ch`, measured with tabular figures.
+// M is the widest unit letter, so each is measured in megabytes rather than the
+// gigabytes a reading is more often in: `compact` spans "0B" to "1023M", that is
+// 2.2 to 5.49; `bytes` reaches 7.19 at "1023 MB"; `rate` 10.09 at "1023.0 MB/s".
+// Rounded up, since other UI fonts are a few percent wider than the one these
+// were measured in.
+const SLOT = { compact: 5.6, bytes: 7.3, rate: 10.2 }
+
+/**
+ * A figure that changes on every push, held in a slot wide enough for the widest
+ * form it can take, so a node moving from 19K/s to 8.19K/s leaves the rest of the
+ * line where it was. Right-aligned, so the unit keeps its place and the digits
+ * grow towards the arrow instead.
+ *
+ * `ch` is the width of a digit under the tabular figures this selects, so a slot
+ * follows whatever size it is drawn at, down to the 10px the table uses on a
+ * phone. The width travels as a custom property rather than `min-width` itself,
+ * which is what allows a caller to drop the reservation where the column is too
+ * narrow to hold it: an inline style would outrank the class that does so.
+ */
+function Num({ ch, className, children }: { ch: number; className?: string; children: ReactNode }) {
+ return (
+
+ {children}
+
+ )
+}
+
function Expiry({ node }: { node: Node }) {
const days = expiresIn(node)
if (days === null) return {FOREVER}
@@ -181,7 +212,15 @@ function Details({ node }: { node: Node }) {