diff --git a/.claude/launch.json b/.claude/launch.json
new file mode 100644
index 0000000..6783658
--- /dev/null
+++ b/.claude/launch.json
@@ -0,0 +1,7 @@
+{
+ "version": "0.0.1",
+ "configurations": [
+ { "name": "server", "runtimeExecutable": "npm", "runtimeArgs": ["--prefix", "mykindlebox/server", "run", "dev"], "port": 3001 },
+ { "name": "client", "runtimeExecutable": "npm", "runtimeArgs": ["--prefix", "mykindlebox/client", "run", "dev"], "port": 5173 }
+ ]
+}
diff --git a/memory-bank/architecture.md b/memory-bank/architecture.md
index 656eff5..3e504ee 100644
--- a/memory-bank/architecture.md
+++ b/memory-bank/architecture.md
@@ -48,7 +48,7 @@ docker-compose.yml — orchestrates both services for self-hosted deployment
|---|---|
| `src/App.jsx` | Root component. Wraps the app in `QueryClientProvider` (React Query) and `BrowserRouter`, defining every IA route nested under a single `Layout` layout-route (`}>`) so the sidebar/bottom-nav persists across page navigation. Includes a catch-all `*` → `NotFound` route. |
| `src/api.js` | Plain JS (not `.ts` — the client is deliberately plain JS/JSX per Step 1.3, not TypeScript), one fetch function per backend endpoint (rabbits CRUD/search/quick-search/ancestors/photo, litters CRUD, events CRUD (`getEvents`/`createEvent`/`updateEvent`/`deleteEvent`), settings, stats, export, `importData` which POSTs a `FormData` file to `/api/import`, shows (`getArbaShows`/`refreshArbaShows`), show events (`getShowEvents`/`getShowEvent`/`createShowEvent`/`updateShowEvent`/`deleteShowEvent`/`addShowToEvent`/`removeShowFromEvent`), show entries + report (`addShowEntry`/`updateShowEntry`/`removeShowEntry`/`getEventReport`), judges + placements (`addShowJudge`/`updateShowJudge`/`removeShowJudge`/`setShowEntryPlacement`)), all targeting `http://localhost:3001/api`. Shared `request()` helper throws on non-2xx (surfacing the JSON `{ error }` body's message) and only sets `Content-Type: application/json` when the body is a JSON string, so the multipart `FormData` photo-upload call isn't given the wrong header. |
-| `src/components/Layout.jsx` | Persistent app shell rendered via React Router's ``. Desktop: 240px left sidebar (`hidden md:flex`) with `NavLink`s for Dashboard/Herd/Litters/Genetics/Shows/Settings. Mobile (`≤768px`): fixed-bottom nav (`md:hidden`, icon + label, 56px tall, padded by `env(safe-area-inset-bottom)`) with Dashboard/Herd/Add (→ `/herd/add`)/Litters/More — "More" opens a small menu for Genetics/Shows/Settings so every page is reachable on a phone. The sidebar is `sticky` and the content column is `min-w-0` + `max-w-6xl`, so wide children (tables, the 900px pedigree tree) scroll inside their own containers instead of stretching the page. `` owns the page gutter (`px-4 md:px-8`); pages must not add their own horizontal padding. Active-route styling comes from `NavLink`'s `isActive` callback, not manual path matching. Also renders `` at the top of the `` content area (above ``) on every page **except** `/herd` (which has its own search box) and the add/edit form routes (`showsQuickSearch`), where jumping to a result would silently discard the unsaved form. |
+| `src/components/Layout.jsx` | Persistent app shell rendered via React Router's `` (Confetti redesign, 2026-11-01). Desktop (`md+`): a **floating sidebar** — a sticky rounded panel inset 16px from the viewport (`h-[calc(100vh-2rem)]`) with the brand mark + rabbitry name, a primary "Add rabbit" pill, `NavLink`s each with a colored icon tile (each section keeps its hue: Dashboard yellow `c5`, Herd violet `c1` + live active-herd count from `/api/stats`, Litters orange `c3`, Shows teal `c4`, Settings blue `c6`), and a footer with the user's avatar, app version, `ThemeToggle` and sign-out. Mobile: a brand row at the top of `` and a **floating pill bottom nav** (`fixed inset-x-3`, above `env(safe-area-inset-bottom)`) with Home/Herd/Add (filled accent tab → `/herd/add`)/Litters/More — More opens a menu with Shows/Settings, the version and a `ThemeToggle`. The content column is `min-w-0` + `max-w-[1180px]`; `` owns the page gutter (`px-4 md:px-10`) and the bottom padding that clears the floating nav (`pb-[calc(7rem+safe-area)]`); pages must not add their own horizontal padding. Herd's active state is computed (`/herd*` except `/herd/add`). Also renders `` above `` on every page **except** `/herd` and the add/edit form routes (`showsQuickSearch`). |
| `src/components/QuickSearch.jsx` | Global quick-search bar (top of every page via `Layout`). A debounced (200ms) text input + a "Filters" popover of four checkboxes (Farm Name / Rabbit Name / Ear Number / Color), **all selected by default**, controlling which fields `GET /api/rabbits/quick-search` prefix-matches. Results render in a dropdown as the user types; each row shows name · farm plus a chip per `matched_fields` entry (value-bearing chips for ear/color, e.g. `Ear #: A15`). Selecting a result navigates to `/herd/:id`. Results outside the active herd show a status `Badge` (pedigree-materialized ancestors are status `other`). Reuses `lib/keyboardNav.js`'s `handleSearchInputKeyDown`/`handleResultKeyDown` for the same `