diff --git a/frontend/src/assets/dup-report.css b/frontend/src/assets/dup-report.css
new file mode 100644
index 0000000..d7d1785
--- /dev/null
+++ b/frontend/src/assets/dup-report.css
@@ -0,0 +1,50 @@
+/* The near-duplicate report, shared by KnowledgeView (notes/tasks) and
+ SnippetListView (snippets) so the two reports read as one feature. Load
+ with beside the view's scoped
+ block; the view keeps only its own extras (.dup-claimed, .dup-action).
+ Promoted from two identical scoped copies in #2903 (milestone 299). */
+.dup-panel {
+ margin-bottom: 1.25rem;
+ padding: 0.85rem 1rem;
+ border: 1px solid var(--fs-border-color);
+ border-radius: 8px;
+ background: var(--fs-surface-hover);
+}
+.dup-empty,
+.dup-head {
+ margin: 0 0 0.5rem;
+ font-size: 0.85rem;
+ color: var(--fs-text-tertiary);
+}
+.dup-empty { margin-bottom: 0; }
+.dup-group {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ flex-wrap: wrap;
+ padding: 0.5rem 0;
+ border-top: 1px solid var(--fs-border-color);
+}
+.dup-members {
+ display: flex;
+ gap: 0.4rem;
+ flex-wrap: wrap;
+ flex: 1 1 20rem;
+ min-width: 0;
+}
+.dup-member {
+ font-size: 0.8rem;
+ padding: 0.1rem 0.45rem;
+ border-radius: 4px;
+ background: color-mix(in srgb, var(--fs-text-tertiary) 12%, transparent);
+ color: var(--fs-text-primary);
+ text-decoration: none;
+ overflow-wrap: anywhere;
+}
+.dup-member:hover { background: var(--fs-surface-hover); }
+.dup-score {
+ font-size: 0.75rem;
+ color: var(--fs-text-tertiary);
+ font-variant-numeric: tabular-nums;
+ white-space: nowrap;
+}
diff --git a/frontend/src/views/KnowledgeView.vue b/frontend/src/views/KnowledgeView.vue
index b954d75..73a09d1 100644
--- a/frontend/src/views/KnowledgeView.vue
+++ b/frontend/src/views/KnowledgeView.vue
@@ -574,6 +574,7 @@ onUnmounted(() => {
+