diff --git a/web/src/lib/components/ui/Hovercard.stories.svelte b/web/src/lib/components/ui/Hovercard.stories.svelte
new file mode 100644
index 00000000..2c82b019
--- /dev/null
+++ b/web/src/lib/components/ui/Hovercard.stories.svelte
@@ -0,0 +1,49 @@
+
+
+{#snippet card()}
+
+
Rich content
+
+ Any markup can live in the card — it stays open while you move the pointer into it.
+
+
+{/snippet}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/lib/components/ui/Hovercard.svelte b/web/src/lib/components/ui/Hovercard.svelte
new file mode 100644
index 00000000..ba65a5a5
--- /dev/null
+++ b/web/src/lib/components/ui/Hovercard.svelte
@@ -0,0 +1,206 @@
+
+
+
+
+
+ {@render children()}
+
+
+{#if !disabled}
+
+ {@render content()}
+
+{/if}
+
+