# S02: Sort Controls on All List Views

**Goal:** Add sort dropdown to SearchResults, SubTopicPage, and CreatorDetail. Backend supports sort params on all relevant endpoints. Sort preference stored in sessionStorage.
**Demo:** After this: Every list view (search results, sub-topic page, creator detail) shows a visible sort dropdown. Changing sort persists across navigation within the session.

## Tasks
- [x] **T01: Added sort query parameter (relevance/newest/oldest/alpha/creator) to search, subtopic, topic, and techniques endpoints with SQL and Python-level ordering** — 1. Add `sort` query param to search endpoint in `backend/routers/search.py` — pass to `SearchService.search()`. Support values: `relevance` (default, by score desc), `newest` (created_at desc), `oldest` (created_at asc), `alpha` (title asc), `creator` (creator_name asc).
2. In `SearchService.search()` and `keyword_search()`, apply sort ORDER BY to keyword results. For semantic results from Qdrant, sort the enriched list in Python (Qdrant returns by score already; for other sorts, re-sort after enrichment).
3. Add `sort` query param to subtopic endpoint in `backend/routers/topics.py` — `get_subtopic_techniques()`. Same sort options minus 'relevance'. Default: 'alpha' (current behavior).
4. Techniques list endpoint already has `sort` param — extend it with `oldest`, `alpha`, `creator` options (currently only `recent` and `random`).
  - Estimate: 25min
  - Files: backend/routers/search.py, backend/routers/topics.py, backend/routers/techniques.py, backend/search_service.py
  - Verify: curl tests: `curl 'http://localhost:8001/api/v1/search?q=snare&sort=newest'` returns results in created_at desc order. `curl 'http://localhost:8001/api/v1/topics/sound-design/bass?sort=oldest'` returns oldest first.
- [x] **T02: Created SortDropdown component and useSortPreference hook, integrated sort controls into SearchResults, SubTopicPage, and CreatorDetail with session-persistent preference** — 1. Create a shared `SortDropdown` component: accepts `options: {value, label}[]`, `value`, `onChange`, `className`. Renders a `<select>` styled consistently with the app's dark theme. Shows the active sort visually.
2. Create a `useSortPreference(defaultSort: string)` hook that reads/writes to `sessionStorage` key `chrysopedia_sort_pref`. Returns `[sort, setSort]`. When setSort is called, it persists to sessionStorage.
3. Add SortDropdown to SearchResults.tsx: options include 'relevance' (default when query active), 'newest', 'oldest', 'alpha', 'creator'. Pass sort param to searchApi().
4. Add SortDropdown to SubTopicPage.tsx: options 'alpha' (default), 'newest', 'oldest', 'creator'. Pass sort to fetchSubTopicTechniques().
5. Add SortDropdown to CreatorDetail.tsx: options 'newest' (default), 'oldest', 'alpha'. Pass sort to fetchTechniques().
6. Update public-client.ts: add `sort` param to `searchApi()`, add `sort` param to `fetchSubTopicTechniques()`.
7. Changing sort resets offset/page to 0 but preserves query and filters.
8. Add CSS for the sort dropdown matching the dark theme.
  - Estimate: 40min
  - Files: frontend/src/components/SortDropdown.tsx, frontend/src/hooks/useSortPreference.ts, frontend/src/pages/SearchResults.tsx, frontend/src/pages/SubTopicPage.tsx, frontend/src/pages/CreatorDetail.tsx, frontend/src/api/public-client.ts
  - Verify: Browser verification: all 3 pages show sort dropdown, changing sort updates results, navigating away and back preserves sort preference.
