diff --git a/client/src/pages/ExternalDogs.jsx b/client/src/pages/ExternalDogs.jsx
new file mode 100644
index 0000000..383ffc6
--- /dev/null
+++ b/client/src/pages/ExternalDogs.jsx
@@ -0,0 +1,143 @@
+import { useState, useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { Users, Plus, Search, ExternalLink, Award, Filter } from 'lucide-react';
+
+export default function ExternalDogs() {
+ const [dogs, setDogs] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [search, setSearch] = useState('');
+ const [sexFilter, setSexFilter] = useState('all');
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ fetch('/api/dogs/external')
+ .then(r => r.json())
+ .then(data => { setDogs(data); setLoading(false); })
+ .catch(() => setLoading(false));
+ }, []);
+
+ const filtered = dogs.filter(d => {
+ const matchSearch = d.name.toLowerCase().includes(search.toLowerCase()) ||
+ (d.breed || '').toLowerCase().includes(search.toLowerCase());
+ const matchSex = sexFilter === 'all' || d.sex === sexFilter;
+ return matchSearch && matchSex;
+ });
+
+ const sires = filtered.filter(d => d.sex === 'male');
+ const dams = filtered.filter(d => d.sex === 'female');
+
+ if (loading) return
Loading external dogs...
;
+
+ return (
+
+ {/* Header */}
+
+
+
+
+
External Dogs
+
External sires, dams, and ancestors used in your breeding program
+
+
+
+
+
+ {/* Filters */}
+
+
+
+ setSearch(e.target.value)}
+ className="search-input"
+ />
+
+
+
+
+
+
{filtered.length} dog{filtered.length !== 1 ? 's' : ''}
+
+
+ {filtered.length === 0 ? (
+
+
+
No external dogs yet
+
Add sires, dams, or ancestors that aren't part of your kennel roster.
+
+
+ ) : (
+
+ {(sexFilter === 'all' || sexFilter === 'male') && sires.length > 0 && (
+
+ ♂ Sires ({sires.length})
+
+ {sires.map(dog => )}
+
+
+ )}
+ {(sexFilter === 'all' || sexFilter === 'female') && dams.length > 0 && (
+
+ ♀ Dams ({dams.length})
+
+ {dams.map(dog => )}
+
+
+ )}
+
+ )}
+
+ );
+}
+
+function DogCard({ dog, navigate }) {
+ const photo = dog.photo_urls?.[0];
+ return (
+ navigate(`/dogs/${dog.id}`)}
+ role="button"
+ tabIndex={0}
+ onKeyDown={e => e.key === 'Enter' && navigate(`/dogs/${dog.id}`)}
+ >
+
+ {photo
+ ?

+ :
+ }
+ {dog.is_champion === 1 &&
🏆}
+
Ext
+
+
+
+ {dog.is_champion === 1 &&
}
+ {dog.name}
+
+
{dog.breed}
+
+ {dog.sex === 'male' ? '\u2642 Sire' : '\u2640 Dam'}
+ {dog.birth_date && <> · {dog.birth_date}>}
+
+ {(dog.sire || dog.dam) && (
+
+ {dog.sire && S: {dog.sire.name}}
+ {dog.dam && D: {dog.dam.name}}
+
+ )}
+
+
+ );
+}