diff --git a/src/components/EmptyState.tsx b/src/components/EmptyState.tsx
new file mode 100644
index 0000000..003139c
--- /dev/null
+++ b/src/components/EmptyState.tsx
@@ -0,0 +1,134 @@
+import type { ComponentType, ReactNode } from 'react';
+import { Link } from 'react-router-dom';
+import { cn } from '@/lib/utils';
+
+/**
+ * Shared empty-state component.
+ *
+ * Before this existed the app had ~39 ad-hoc empty states, each free to drift
+ * in spacing, tone and — crucially — whether they offered a way *out*. The
+ * `action` slot is the valuable part: "No crews nearby → Create one" turns a
+ * dead-end into a loop. Prefer this component over hand-rolling new ones.
+ */
+
+export interface EmptyStateAction {
+ /** Button label. */
+ label: string;
+ /** Internal route to navigate to (renders a ). */
+ to?: string;
+ /** Click handler (renders a