# S03 Research: Global Search & Mobile Navigation

## Summary

This slice adds two features to the app shell in `App.tsx`: (1) a compact search bar in the nav on all non-home pages with Cmd+K focus, and (2) a hamburger menu for mobile viewports (<768px). Both are well-understood patterns. The existing `SearchAutocomplete` component already supports `heroSize` toggling — a new compact/nav mode is a straightforward addition. No mobile nav exists yet; it's greenfield.

**Requirements owned:** R020 (Global Search in Navigation), R021 (Mobile Hamburger Menu)

## Recommendation

Straightforward implementation — no novel tech, no risky integration. Build the nav search first (it affects desktop and mobile layout), then add the hamburger menu. Both modify `App.tsx` and `App.css` primarily.

## Implementation Landscape

### Current App Shell Structure (`App.tsx`)

```
<header className="app-header">
  <Link className="app-header__brand"><h1>Chrysopedia</h1></Link>
  <div className="app-header__right">
    <nav className="app-nav">
      <Link>Home</Link>
      <Link>Topics</Link>
      <Link>Creators</Link>
      <AdminDropdown />
    </nav>
  </div>
</header>
```

- No `useLocation` imported yet — will need it to conditionally render nav search (hide on `/`)
- `app-header` is `display: flex; justify-content: space-between; padding: 0.75rem 1.5rem`
- `app-header__right` is `display: flex; align-items: center; gap: 1.5rem`
- At `max-width: 640px`: header becomes `flex-direction: column` and right section wraps
- At `max-width: 768px`: no header changes currently (only technique columns go single-col)

### Existing SearchAutocomplete Component

File: `frontend/src/components/SearchAutocomplete.tsx`

- Props: `onSearch`, `placeholder`, `heroSize`, `initialQuery`, `autoFocus`
- `heroSize` boolean toggles between `search-form--hero` and `search-form--inline` CSS classes
- Input class toggles between `search-input--hero` and `search-input--inline`
- Typeahead dropdown is absolutely positioned from `.search-container`
- Has popular suggestions on empty focus + debounced search on 2+ chars
- The component is self-contained with outside-click close and Escape handling

**For nav mode:** Need a third variant (beyond hero/inline). Options:
1. Add a `navCompact` boolean prop that applies `search-form--nav` and `search-input--nav` classes
2. Change `heroSize` to a `variant: 'hero' | 'inline' | 'nav'` string prop

Option 2 is cleaner. The nav variant needs: smaller padding, no submit button (Enter submits), narrower max-width (~16rem), and the typeahead dropdown needs `z-index` high enough to overlay page content.

### Nav Search Layout Plan

- Add `SearchAutocomplete` between brand and nav links in `app-header__right`
- Conditionally render: show when `location.pathname !== '/'` (home has its own hero search)
- Cmd+K handler: global `keydown` listener in App.tsx, calls `inputRef.current?.focus()`. Need to expose a ref or imperative handle from SearchAutocomplete — or add the listener inside SearchAutocomplete when variant is 'nav'.

**Cmd+K implementation:** The simplest approach is a `useEffect` in `App.tsx` that listens for `Cmd+K` (or `Ctrl+K` on non-Mac) and focuses a ref. SearchAutocomplete already has an internal `inputRef` — need to either:
- Forward the ref via `React.forwardRef` so App.tsx can call `.focus()`
- Or put the global keydown listener inside SearchAutocomplete when `variant === 'nav'`

The second is simpler — the component already manages its own input ref. Add `globalShortcut?: boolean` prop, and when true, register `keydown` listener for Cmd+K/Ctrl+K that focuses the input and prevents default browser behavior.

### Mobile Hamburger Menu Plan

No mobile nav exists. Current behavior at 640px: header stacks vertically, nav links wrap. At 768px: no header changes.

**Implementation:**
- Add a hamburger button (☰) visible only below 768px via CSS `display: none` / media query
- Hide `.app-nav` links below 768px by default, show in an overlay/dropdown when hamburger is active
- State: `menuOpen` boolean in App.tsx
- Close on: route change (watch `location` with useEffect), outside click, Escape key
- Touch targets: 44×44px minimum per R021
- The nav search bar should move inside the mobile menu panel on small screens

**CSS approach:**
- `.hamburger-btn` — hidden on desktop, visible below 768px
- `.app-nav` — hidden below 768px when menu closed
- `.app-nav--open` — full-width dropdown below header with stacked links
- Transition: `max-height` or `transform: translateX` for slide-in

**AdminDropdown in mobile:** It's already a dropdown component. In the mobile menu, it should render inline (expanded) rather than as a nested dropdown. Simplest: just let it render as-is inside the mobile panel — it works fine as a sub-dropdown.

### Existing CSS Custom Properties (relevant)

```css
--color-bg-header        (header background)
--color-text-on-header   (nav link color)
--color-text-on-header-hover
--color-bg-input         (search input background)
--color-border           (input border)
--color-accent           (focus ring)
--color-bg-surface       (dropdown background)
--color-shadow-heavy     (dropdown shadow)
```

All needed colors already exist as custom properties.

### Existing Responsive Breakpoints

- `max-width: 640px` — header columns, action bar stacking, small card adjustments
- `max-width: 768px` — technique columns single-col
- `min-width: 600px` — key moment grid

The 768px breakpoint for hamburger aligns with the existing 768px media query. The 640px breakpoint can handle further compacting if needed.

## Natural Task Decomposition

### T01: Nav Search Bar with Cmd+K (~45 min)
1. Refactor `SearchAutocomplete` to accept `variant: 'hero' | 'inline' | 'nav'` (backward-compatible — default to hero/inline based on existing heroSize for now, or deprecate heroSize)
2. Add nav-variant CSS: compact sizing, no submit button, appropriate z-index
3. Add `globalShortcut` behavior inside SearchAutocomplete
4. In `App.tsx`: import `useLocation`, conditionally render `<SearchAutocomplete variant="nav" ... />` when not on homepage
5. Wire `onSearch` to `useNavigate` for `/search?q=...`

**Files:** `SearchAutocomplete.tsx`, `App.tsx`, `App.css`
**Verify:** Build succeeds; search bar appears in nav on `/topics`, `/creators`, `/techniques/*`; hidden on `/`; Cmd+K focuses input; typing + Enter navigates to search results

### T02: Mobile Hamburger Menu (~45 min)
1. Add hamburger button markup in `App.tsx` header
2. Add `menuOpen` state with close-on-navigate (useEffect watching location), close-on-escape, close-on-outside-click
3. CSS: hide hamburger above 768px, hide nav links below 768px unless open
4. Mobile menu panel: stacked links with 44×44px touch targets
5. Nav search bar repositioned inside mobile menu panel

**Files:** `App.tsx`, `App.css`
**Verify:** Build succeeds; at viewport <768px, nav links hidden, hamburger visible; tapping hamburger shows nav; navigating closes menu; touch targets ≥44×44px

## Risks

- **Typeahead z-index in nav:** The dropdown positions absolutely from `.search-container`. In the nav, this needs to overlay page content below the header. Setting `z-index: 100` on the typeahead dropdown (already at 50) should handle it, but verify against sticky elements.
- **SearchAutocomplete prop refactor:** Changing from `heroSize` boolean to `variant` string is a breaking change for Home.tsx and SearchResults.tsx callers. Need to update both call sites. Low risk but must be thorough.
- **Mobile menu + search interaction:** When the mobile menu is open and the user focuses the search input, the typeahead dropdown needs to render within the mobile panel, not escape it. Verify positioning.

## Verification Strategy

```bash
# Build check
cd frontend && npm run build

# Browser verification (manual)
# Desktop: visit /topics, /creators — search bar in nav, Cmd+K focuses
# Homepage: no nav search bar
# Mobile viewport (<768px): hamburger visible, nav hidden, tap shows menu
# Navigate from mobile menu: menu closes
```
