diff --git a/apps/web/app/(app)/people/page.tsx b/apps/web/app/(app)/people/page.tsx
new file mode 100644
index 0000000..21900b2
--- /dev/null
+++ b/apps/web/app/(app)/people/page.tsx
@@ -0,0 +1,16 @@
+import type { Metadata } from "next";
+import { PeopleSearch } from "@/components/social/people-search";
+
+export const metadata: Metadata = { title: "Find People — Epicure" };
+
+export default function PeoplePage() {
+ return (
+
+
+
Find People
+
Search for cooks to follow by name or username.
+
+
+
+ );
+}
diff --git a/apps/web/app/api/v1/users/search/route.ts b/apps/web/app/api/v1/users/search/route.ts
new file mode 100644
index 0000000..977aac7
--- /dev/null
+++ b/apps/web/app/api/v1/users/search/route.ts
@@ -0,0 +1,45 @@
+import { NextRequest, NextResponse } from "next/server";
+import { db, users, userBlocks, eq, and, or, ne, ilike, isNotNull } from "@epicure/db";
+import { requireSession } from "@/lib/api-auth";
+
+export async function GET(req: NextRequest) {
+ const { session, response } = await requireSession();
+ if (response) return response;
+
+ const q = req.nextUrl.searchParams.get("q")?.trim().slice(0, 50) ?? "";
+ if (q.length < 2) return NextResponse.json({ users: [] });
+
+ const blocked = await db
+ .select({ blockedId: userBlocks.blockedId })
+ .from(userBlocks)
+ .where(eq(userBlocks.blockerId, session!.user.id));
+ const blockedByMe = new Set(blocked.map((b) => b.blockedId));
+
+ const blockedMe = await db
+ .select({ blockerId: userBlocks.blockerId })
+ .from(userBlocks)
+ .where(eq(userBlocks.blockedId, session!.user.id));
+ const haveBlockedMe = new Set(blockedMe.map((b) => b.blockerId));
+
+ const rows = await db
+ .select({
+ id: users.id,
+ name: users.name,
+ username: users.username,
+ avatarUrl: users.avatarUrl,
+ bio: users.bio,
+ })
+ .from(users)
+ .where(
+ and(
+ isNotNull(users.username),
+ ne(users.id, session!.user.id),
+ or(ilike(users.name, `%${q}%`), ilike(users.username, `%${q}%`))
+ )
+ )
+ .limit(20);
+
+ const filtered = rows.filter((u) => !blockedByMe.has(u.id) && !haveBlockedMe.has(u.id));
+
+ return NextResponse.json({ users: filtered });
+}
diff --git a/apps/web/components/search/explore-page-content.tsx b/apps/web/components/search/explore-page-content.tsx
index 1752498..b113ecc 100644
--- a/apps/web/components/search/explore-page-content.tsx
+++ b/apps/web/components/search/explore-page-content.tsx
@@ -244,7 +244,12 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
{/* Search bar */}
-
{t("title")}
+
+
{t("title")}
+
+ Find people
+
+