diff --git a/src/components/drill/PayerPeekContent.test.tsx b/src/components/drill/PayerPeekContent.test.tsx
new file mode 100644
index 0000000..5c39c28
--- /dev/null
+++ b/src/components/drill/PayerPeekContent.test.tsx
@@ -0,0 +1,80 @@
+// @vitest-environment happy-dom
+// PayerPeekContent uses useQuery internally via usePayerSummary. We mock
+// that hook here so the component can be rendered without standing up a
+// real QueryClient — same pattern as useClaimDetail.test.ts.
+(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
+
+import { afterEach, describe, it, expect, vi } from "vitest";
+import { cleanup, render, screen } from "@testing-library/react";
+import { MemoryRouter } from "react-router-dom";
+import type { ReactNode } from "react";
+import { PayerPeekContent } from "@/components/drill/PayerPeekContent";
+
+// Mock the hook BEFORE the import above is resolved (vitest hoists
+// `vi.mock` to the top of the file regardless of where it appears
+// syntactically).
+vi.mock("@/hooks/usePayerSummary", () => ({
+ usePayerSummary: vi.fn(),
+}));
+
+// Importing after vi.mock so we get the mocked reference.
+import { usePayerSummary } from "@/hooks/usePayerSummary";
+
+// happy-dom keeps `document.body` between tests; without cleanup,
+// `screen.getByText(...)` would find nodes from earlier renders.
+afterEach(() => cleanup());
+
+// The component renders , which requires a router context. A bare
+// MemoryRouter with no initialEntries is enough — the test asserts on the
+// generated href, not on navigation.
+function withRouter(node: ReactNode) {
+ return {node};
+}
+
+const SAMPLE_PAYER = {
+ payer_id: "SKCO0",
+ name: "CO Medicaid",
+ claim_count: 1247,
+ billed_total: 548000,
+ received_total: 521000,
+ denial_rate: 0.042,
+ top_providers: [{ npi: "1881068062", count: 184 }],
+} as const;
+
+describe("PayerPeekContent", () => {
+ it("renders loading skeleton while fetching", () => {
+ // Idle / in-flight state — `data` undefined, `isLoading` true.
+ // The component renders rows and no text,
+ // so the regex match for /claims/i must come back null (the
+ // "View all claims" link only renders once data is present).
+ (usePayerSummary as unknown as ReturnType).mockReturnValue({
+ data: undefined,
+ isLoading: true,
+ });
+
+ render(withRouter());
+ expect(screen.queryByText(/claims/i)).toBeNull();
+ });
+
+ it("renders summary stats when data loads", () => {
+ (usePayerSummary as unknown as ReturnType).mockReturnValue({
+ data: SAMPLE_PAYER,
+ isLoading: false,
+ });
+
+ render(withRouter());
+
+ expect(screen.getByText("CO Medicaid")).toBeTruthy();
+ // fmt.num(1247) === "1,247" — the "184 claims" line also matches this
+ // regex, but getByText with a regex is fine because we only assert
+ // existence.
+ expect(screen.getByText(/1,247/)).toBeTruthy();
+ expect(screen.getByText("$548,000")).toBeTruthy();
+ // denial_rate is a fraction (0.042); fmt.pct(payer.denial_rate * 100)
+ // yields "4.2%".
+ expect(screen.getByText("4.2%")).toBeTruthy();
+
+ const link = screen.getByRole("link", { name: /view all claims/i });
+ expect(link.getAttribute("href")).toBe("/claims?payer=SKCO0");
+ });
+});
\ No newline at end of file
diff --git a/src/components/drill/PayerPeekContent.tsx b/src/components/drill/PayerPeekContent.tsx
new file mode 100644
index 0000000..5b967db
--- /dev/null
+++ b/src/components/drill/PayerPeekContent.tsx
@@ -0,0 +1,118 @@
+import { Link } from "react-router-dom";
+import { Skeleton } from "@/components/ui/skeleton";
+import { fmt } from "@/lib/format";
+import { usePayerSummary } from "@/hooks/usePayerSummary";
+import type { PayerSummary } from "@/lib/api";
+
+interface Props {
+ payerId: string;
+}
+
+/**
+ * Peek body for a payer — aggregate stats card shown inside the
+ * centered PeekModal (SP21 universal drill-down).
+ *
+ * Owns its own fetch via `usePayerSummary`; the parent PeekModal only
+ * concerns itself with open/close + title. We deliberately do NOT show
+ * an error state here — the peek is a low-stakes summary, so a silent
+ * retry + skeleton on failure is acceptable (the parent modal still
+ * closes correctly).
+ *
+ * `fmt.pct` does not multiply by 100 (it's just `n.toFixed(d)%`), so the
+ * API's fraction `denial_rate` (0–1) needs `* 100` before formatting —
+ * otherwise the UI would render "0.0%" for everything.
+ */
+export function PayerPeekContent({ payerId }: Props) {
+ const { data, isLoading } = usePayerSummary(payerId);
+ if (isLoading || !data) {
+ return (
+