diff --git a/src/pages/DisputeCardSkeleton.tsx b/src/pages/DisputeCardSkeleton.tsx
new file mode 100644
index 0000000..ba026ef
--- /dev/null
+++ b/src/pages/DisputeCardSkeleton.tsx
@@ -0,0 +1,94 @@
+import { Card, CardContent, CardFooter, CardHeader } from '@/components/ui/card';
+import { Skeleton } from '@/components/ui/skeleton';
+
+/**
+ * DisputeCardSkeleton
+ * -------------------
+ * Loading skeleton that mirrors the final shape so the
+ * first paint doesn't jump when dispute data resolves.
+ *
+ * Layout parity (against DisputeCard — populated branch):
+ * - CardHeader: `flex flex-row items-start justify-between gap-4 pb-3`
+ * ↳ Left shard: title at `text-base` (h-5) on two lines, capped at
+ * `max-w-[75%]` so the badge always has room.
+ * ↳ Right shard: badge pill — `h-6 w-24 !rounded-full shrink-0`
+ * matches DisputeStateBadge (size="md") shape.
+ * - CardContent: generic, state-agnostic placeholder (reason paragraph × 2,
+ * penalty strip, deadline row, tally strip, accordion
+ * trigger). Every section mirrors the conditional layout
+ * of the real component.
+ * - CardFooter: `border-t pt-3 flex items-center justify-between` with
+ * a caption-sized label shard + action button shard.
+ *
+ * Accessibility (WCAG 2.1 AA):
+ * - `role="status"` + `aria-busy="true"` on the Card root.
+ * `role="status"` implies `aria-live="polite"` so screen readers
+ * announce the loading state on mount.
+ * - A single sr-only message ("Loading dispute details…") is read on
+ * focus. Individual Skeletons render with `aria-hidden` (the base
+ * primitive's default) so they are not announced separately.
+ * - `prefers-reduced-motion` is honored at the level
+ * (motion-reduce:animate-none) so the shimmer stops for users who
+ * request reduced motion.
+ *
+ * Responsive behavior:
+ * - All widths are ratios (`w-full`, `w-2/3`, `w-[90%]`) — no fixed
+ * `px` values that could overflow on narrow viewports.
+ * - Badge shard pinned with `shrink-0` so its pill width stays stable.
+ * - Action button shard uses `w-32 sm:w-28` so it reflows gracefully.
+ */
+export function DisputeCardSkeleton() {
+ return (
+
+ Loading dispute details…
+
+ {/* Header: matches DisputeCard — title on the left, badge on the right */}
+
+