diff --git a/ui/src/pages/occurrences/occurrence-stats.tsx b/ui/src/pages/occurrences/occurrence-stats.tsx
new file mode 100644
index 000000000..7b12cbb8e
--- /dev/null
+++ b/ui/src/pages/occurrences/occurrence-stats.tsx
@@ -0,0 +1,73 @@
+import { useModelAgreement } from 'data-services/hooks/occurrences/stats/useModelAgreement'
+import { Box } from 'nova-ui-kit'
+
+interface OccurrenceStatsProps {
+ projectId?: string
+ filters: { field: string; value?: string; error?: string }[]
+}
+
+const StatBar = ({ label, value }: { label: string; value: number }) => {
+ const pct = Math.round(Math.min(Math.max(value, 0), 1) * 100)
+
+ return (
+
+ )
+}
+
+// Live verified / agreement stats for the occurrence list. Threads the same
+// filter array the list view sends so the numbers always match the result set.
+export const OccurrenceStats = ({
+ projectId,
+ filters,
+}: OccurrenceStatsProps) => {
+ const activeFilters = filters.reduce>(
+ (acc, { field, value, error }) => {
+ if (value?.length && !error) {
+ acc[field] = value
+ }
+ return acc
+ },
+ {}
+ )
+
+ const { data, isLoading, error } = useModelAgreement(projectId, activeFilters)
+
+ if (error || (!isLoading && !data)) {
+ return null
+ }
+
+ return (
+
+ Stats
+
+ {isLoading || !data ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+ )
+}
diff --git a/ui/src/pages/occurrences/occurrences.tsx b/ui/src/pages/occurrences/occurrences.tsx
index 9a1071a3c..77da98ea6 100644
--- a/ui/src/pages/occurrences/occurrences.tsx
+++ b/ui/src/pages/occurrences/occurrences.tsx
@@ -35,6 +35,7 @@ import { useSelectedView } from 'utils/useSelectedView'
import { useSort } from 'utils/useSort'
import { columns } from './occurrence-columns'
import { OccurrenceGallery } from './occurrence-gallery'
+import { OccurrenceStats } from './occurrence-stats'
import { OccurrenceNavigation } from './occurrence-navigation'
import { OccurrencesActions } from './occurrences-actions'
@@ -94,6 +95,7 @@ export const Occurrences = () => {
<>