diff --git a/docs/plans/2026-02-28-ui-redesign-implementation.md b/docs/plans/2026-02-28-ui-redesign-implementation.md
new file mode 100644
index 0000000..5286cdb
--- /dev/null
+++ b/docs/plans/2026-02-28-ui-redesign-implementation.md
@@ -0,0 +1,482 @@
+# UI/UX Redesign Implementation Plan
+
+> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
+
+**Goal:** Redesign the realestate-crawler frontend from sidebar-filter layout to horizontal-filter-bar layout with React Router, redesigned property cards, refined visual system, and tabbed listing detail.
+
+**Architecture:** Replace the sidebar FilterPanel with a horizontal FilterBar component. Add react-router-dom for URL-based navigation and deep linking. Extract shared utilities to eliminate duplication. Refine the color palette from plain shadcn neutral to a teal-accented property search theme. All changes are frontend-only — no backend API changes needed.
+
+**Tech Stack:** React 19, TypeScript, Tailwind CSS 4, shadcn/ui, react-router-dom, Mapbox GL, Vite
+
+---
+
+## Phase 1: Foundation (Router + Shared Utils + Color System)
+
+### Task 1: Install react-router-dom
+
+**Files:**
+- Modify: `frontend/package.json`
+
+**Step 1: Install the dependency**
+
+Run: `cd /Users/viktorbarzin/code/realestate-crawler/frontend && npm install react-router-dom`
+
+**Step 2: Verify installation**
+
+Run: `grep react-router-dom package.json`
+Expected: `"react-router-dom": "^7..."`
+
+**Step 3: Commit**
+
+```bash
+cd /Users/viktorbarzin/code/realestate-crawler
+git add frontend/package.json frontend/package-lock.json
+git commit -m "chore: add react-router-dom dependency"
+```
+
+---
+
+### Task 2: Extract shared utility functions
+
+Currently `formatDuration`, `formatCurrency`, `formatDate`, `TravelModeIcon`, and `isTerminalStatus` are duplicated across PropertyCard.tsx, ListingDetail.tsx, MobileBottomSheet.tsx, StatsBar.tsx, TaskIndicator.tsx, TaskProgressDrawer.tsx, useTaskProgress.ts, and App.tsx.
+
+**Files:**
+- Create: `frontend/src/utils/format.ts`
+- Create: `frontend/src/utils/taskUtils.ts`
+- Modify: `frontend/src/components/PropertyCard.tsx` — remove local formatDuration/formatCurrency/formatDate, import from utils
+- Modify: `frontend/src/components/ListingDetail.tsx` — same
+- Modify: `frontend/src/components/MobileBottomSheet.tsx` — same
+- Modify: `frontend/src/components/StatsBar.tsx` — remove local formatCurrency, import from utils
+- Modify: `frontend/src/App.tsx` — remove local isTerminalStatus, import from utils
+- Modify: `frontend/src/components/TaskIndicator.tsx` — remove local isTerminalStatus/taskStateToResult, import
+- Modify: `frontend/src/components/TaskProgressDrawer.tsx` — remove local isTerminalStatus/taskStateToResult, import
+- Modify: `frontend/src/hooks/useTaskProgress.ts` — remove local isTerminalStatus, import
+
+**Step 1: Create `frontend/src/utils/format.ts`**
+
+Extract these functions by reading them from PropertyCard.tsx (lines ~18-55) and consolidating:
+- `formatCurrency(value: number): string` — formats as £X,XXX
+- `formatDuration(seconds: number): string` — formats as Xmin / Xhr Xmin
+- `formatDate(dateStr: string): string` — formats as "3d ago" / "2w ago" etc.
+- `formatPricePerSqm(price: number, sqm: number | null): string | null`
+
+**Step 2: Create `frontend/src/utils/taskUtils.ts`**
+
+Extract from App.tsx and TaskIndicator.tsx:
+- `isTerminalStatus(status: string): boolean`
+- `taskStateToResult(ts: TaskState): TaskResult`
+
+**Step 3: Update all importing files** to use the shared utils instead of local definitions. Search for each function name and replace local definitions with imports.
+
+**Step 4: Run tests**
+
+Run: `cd /Users/viktorbarzin/code/realestate-crawler/frontend && npx vitest run`
+Expected: All existing tests pass
+
+**Step 5: Commit**
+
+```bash
+git add frontend/src/utils/format.ts frontend/src/utils/taskUtils.ts frontend/src/components/ frontend/src/App.tsx frontend/src/hooks/
+git commit -m "refactor: extract shared utility functions to eliminate duplication"
+```
+
+---
+
+### Task 3: Update color palette in index.css
+
+Replace the achromatic neutral palette with a teal-accented property search palette.
+
+**Files:**
+- Modify: `frontend/src/index.css:44-77` (the `:root` block)
+
+**Step 1: Update the `:root` CSS variables**
+
+Change the `:root` block to use teal-accented colors. Key changes:
+- `--primary`: change from `oklch(0.205 0 0)` (pure black) to a dark slate `oklch(0.208 0.042 265.755)` (slate 900 with hint of blue)
+- `--accent`: change from neutral gray to teal `oklch(0.627 0.14 175.5)` (teal 600)
+- `--ring`: update to teal ring color
+- Add custom properties for deal indicators:
+ - `--color-deal-good: oklch(0.696 0.17 162.48)` (emerald 500)
+ - `--color-deal-above: oklch(0.795 0.184 86.047)` (amber 500)
+
+Also add these to the `@theme inline` block:
+```css
+--color-deal-good: var(--deal-good);
+--color-deal-above: var(--deal-above);
+```
+
+**Step 2: Verify Vite HMR picks up the changes**
+
+The running Vite dev server should hot-reload. Check the browser at localhost:5173.
+
+**Step 3: Commit**
+
+```bash
+git add frontend/src/index.css
+git commit -m "style: update color palette to teal-accented property search theme"
+```
+
+---
+
+### Task 4: Set up React Router with URL-based filter state
+
+**Files:**
+- Modify: `frontend/src/main.tsx` — wrap App in BrowserRouter
+- Create: `frontend/src/hooks/useFilterParams.ts` — hook that syncs filter state with URL search params
+- Modify: `frontend/src/App.tsx` — use useFilterParams instead of local useState for queryParameters and viewMode
+
+**Step 1: Update main.tsx**
+
+```tsx
+import { BrowserRouter } from 'react-router-dom';
+// Wrap in
+```
+
+**Step 2: Create useFilterParams hook**
+
+This hook:
+- Reads filter values from URL search params on mount
+- Returns `[filterValues, setFilterValues]` where setFilterValues updates both state and URL
+- Reads `viewMode` from the URL pathname (`/map`, `/split`, `/list`, `/saved`)
+- Falls back to DEFAULT_FILTER_VALUES for missing params
+- Handles the `/callback` route for auth
+
+Key URL params: `type`, `minPrice`, `maxPrice`, `minBeds`, `maxBeds`, `minSqm`, `maxSqm`, `minPriceSqm`, `maxPriceSqm`, `furnish`, `district`, `lastSeenDays`, `availableFrom`, `sort`, `metric`
+
+**Step 3: Update App.tsx**
+
+- Import `useFilterParams` and `useNavigate` from react-router-dom
+- Replace `useState(null)` for queryParameters with the hook
+- Replace `useState('map')` for viewMode with URL-derived state
+- Keep the `/callback` route check but use React Router's Routes/Route
+
+**Step 4: Test that the app still renders with the router**
+
+Navigate to `http://localhost:5173/map` in the browser and verify it loads.
+
+**Step 5: Run tests**
+
+Run: `cd /Users/viktorbarzin/code/realestate-crawler/frontend && npx vitest run`
+Fix any tests that break due to missing router context (wrap test renders in ``).
+
+**Step 6: Commit**
+
+```bash
+git add frontend/src/main.tsx frontend/src/hooks/useFilterParams.ts frontend/src/App.tsx
+git commit -m "feat: add React Router with URL-based filter state and deep linking"
+```
+
+---
+
+## Phase 2: Filter Bar (Replace Sidebar)
+
+### Task 5: Create the FilterBar component
+
+This is the core layout change. Create a new horizontal FilterBar that replaces the sidebar FilterPanel.
+
+**Files:**
+- Create: `frontend/src/components/FilterBar.tsx`
+- Create: `frontend/src/components/FilterChips.tsx`
+
+**Step 1: Create FilterBar.tsx**
+
+A horizontal bar with:
+- Compact dropdown selectors for: Price Range (min-max), Bedrooms (min-max), Min Size
+- A "More Filters" button that opens a Popover with: Max Size, Price/m² range, Furnishing toggles, District input, Available From date, Last Seen Days, POI travel time filters
+- Right-aligned action buttons: "Show Listings" (primary) and "Scrape New" (secondary)
+- Uses the same Zod schema and react-hook-form as the current FilterPanel
+- Emits the same `onSubmit(action, parameters)` callback
+
+The bar layout: `flex items-center gap-2 px-4 h-10 bg-muted/50 border-b`
+
+Use shadcn Popover for dropdown selectors (not full Select components — we want compact inline triggers).
+
+**Step 2: Create FilterChips.tsx**
+
+A component that renders active filter values as removable chips:
+```tsx
+