diff --git a/web/components/apikeys/ApiKeysPanel.tsx b/web/components/apikeys/ApiKeysPanel.tsx
index 7602fce..d8ee83f 100644
--- a/web/components/apikeys/ApiKeysPanel.tsx
+++ b/web/components/apikeys/ApiKeysPanel.tsx
@@ -14,6 +14,7 @@ import {
useToast,
} from "@/components/ui";
import { KeyLedger, LedgerSkeleton } from "./KeyLedger";
+import { KeyPosture } from "./KeyPosture";
import { CreateKeyDialog, EXPIRY_OPTIONS } from "./CreateKeyDialog";
const ROLES: Role[] = ["owner", "admin", "member"];
@@ -137,12 +138,7 @@ export function ApiKeysPanel() {
return (
-
-
API Keys
-
- {count} key{count !== 1 ? "s" : ""} · {showAll ? "instance-wide" : "yours"}
-
-
+
API Keys
)}
+ {count > 0 &&
}
+
keyLifetime(t, capDays, now));
+
+ const cells: { n: number; label: string; tone: string }[] = [
+ { n: tokens.length, label: `key${tokens.length === 1 ? "" : "s"} listed`, tone: "text-text-primary" },
+ { n: lifetimes.filter((l) => l.state === "soon").length, label: "expire within 7 days", tone: "text-warning" },
+ { n: lifetimes.filter((l) => l.state === "eternal").length, label: "never expire", tone: "text-danger" },
+ // Muted, not coloured: an unused key is a cleanup candidate, not an
+ // incident.
+ { n: tokens.filter((t) => !t.last_used_at).length, label: "unused since issue", tone: "text-text-secondary" },
+ ];
+
+ return (
+
+ {cells.map((c) => (
+
+ {c.n}
+ {c.label}
+
+ ))}
+
+ );
+}