# Reference: Tasks 16-18 — Interactions

## Task 16: Tile Expansion System

### Overview

Three tiles have expandable items: CareerActivity (roles), Projects, and CoreSkills. Clicking an item expands it in-place to reveal detail, like expanding a clinical record entry.

### Expansion Pattern (consistent across all tiles)

**Animation:**
- Framer Motion `AnimatePresence` + `motion.div`
- Height-only animation: 200ms, ease-out
- **No opacity fade on content** (guardrail)
- `overflow: hidden` on the animated container

```typescript
<AnimatePresence initial={false}>
  {isExpanded && (
    <motion.div
      initial={{ height: 0 }}
      animate={{ height: 'auto' }}
      exit={{ height: 0 }}
      transition={prefersReducedMotion ? { duration: 0 } : { duration: 0.2, ease: 'easeOut' }}
      style={{ overflow: 'hidden' }}
    >
      {/* expanded content */}
    </motion.div>
  )}
</AnimatePresence>
```

**Behavior:**
- Single-expand accordion: only one item expanded at a time within each tile
- Click expanded item again to collapse
- Click different item: collapses current, expands new
- State: `expandedItemId: string | null` in each tile component

**Keyboard:**
- Enter/Space: toggle expand/collapse
- Escape: collapse current item
- `aria-expanded` on each clickable item

**Visual:**
- Expanded content has slightly different background (`var(--bg)` or subtle border-left)
- Colored left border on expanded panel (accent color for roles, amber for projects, teal for skills)
- Content padding: 12-16px

### CareerActivity Expansion (roles)

When a role-type activity item is expanded:
- Show full role details from corresponding consultation entry
- Structure: role title, organization, date range
- Achievement bullets (examination array from consultation)
- Coded entries if available
- Match expanded content to `consultations` data by mapping activity item to consultation

### Projects Expansion

When a project item is expanded:
- Show from investigation data:
  - Methodology
  - Tech stack (as tags or inline list)
  - Results (bulleted)
  - External URL link if available ("View Results" button)

### CoreSkills Expansion

When a skill item is expanded:
- Show "prescribing history" — a timeline of skill development
- **Data source:** `import { medications } from '@/data/medications'` (NOT `skills.ts`). The `medications.ts` file has 18 entries, each with a `prescribingHistory` array of `{ year, description }` entries. Map from `skills.ts` to `medications.ts` by matching skill name to medication name (e.g., "Data Analysis" in skills.ts → find the medication with `name: "Data Analysis"` in medications.ts to get its `prescribingHistory`).
- Format: vertical timeline with year markers and descriptions
  - Timeline dots: accent color, 6px, with connecting line
  - Year: mono font, 12px, semibold
  - Description: 12px, regular

---

## Task 17: KPI Flip Cards

### Overview

In the LatestResults tile, each metric card can be clicked to "flip" and reveal an explanation of that KPI.

### Flip Animation

**CSS Perspective approach:**
```css
.metric-card {
  perspective: 1000px;
  cursor: pointer;
}

.metric-card-inner {
  transition: transform 0.4s ease-in-out;
  transform-style: preserve-3d;
  position: relative;
}

.metric-card-inner.flipped {
  transform: rotateY(180deg);
}

.metric-card-front,
.metric-card-back {
  backface-visibility: hidden;
  position: absolute;
  inset: 0;
}

.metric-card-back {
  transform: rotateY(180deg);
}
```

Or use Framer Motion `animate={{ rotateY: isFlipped ? 180 : 0 }}` with `perspective` on parent.

**Behavior:**
- Click to flip front → back
- Click again to flip back → front
- Only one card flipped at a time (clicking another card flips the current one back)
- State: `flippedCardId: string | null` in LatestResultsTile

**Front face:** Current metric display (value + label + sub) — same as Task 10.

**Back face:**
- `background: var(--accent-light)` (subtle teal tint)
- `padding: 14px`
- Text: 12px, text-secondary, `line-height: 1.5`
- The explanation text from KPI data's `explanation` field

**Reduced motion:**
- No 3D flip animation
- Instant content swap (front → back)
- Could use a simple crossfade or just replace content immediately

**Keyboard:**
- Enter/Space to flip
- Each metric card should be `tabIndex={0}` with appropriate `aria-label`

**KPI Explanations** (from `src/data/kpis.ts`):
- £220M: Budget management with forecasting models
- £14.6M: Efficiency programme through data analysis
- 9+ Years: NHS service progression since 2016
- 12: Cross-functional team leadership

---

## Task 18: Command Palette

### File: `src/components/CommandPalette.tsx`

### Trigger
- **Ctrl+K** (global `keydown` listener on `document`)
- **Click** on TopBar search bar (or focus on search input)
- The TopBar search input does NOT do inline search — it opens the palette

### Overlay
- `position: fixed`, `inset: 0`
- `background: rgba(26,43,42,0.45)`
- `backdrop-filter: blur(4px)`
- `z-index: 1000`
- Fade in: `opacity: 0 → 1`, `visibility: hidden → visible`, 200ms transition
- Click overlay (outside modal) to close

### Palette Modal
- `width: 580px`, `max-height: 520px`
- `background: var(--surface)` (#FFFFFF)
- `border-radius: 12px`
- `box-shadow: 0 20px 60px rgba(26,43,42,0.2), 0 0 0 1px rgba(26,43,42,0.08)`
- `overflow: hidden`
- Entrance: `transform: scale(0.97) translateY(-8px)` → `scale(1) translateY(0)`, 200ms cubic-bezier

### Search Input
- Flex row: search icon (18px, accent) + input + "ESC" hint badge
- `padding: 14px 18px`, `border-bottom: 1px solid var(--border-light)`
- Input: 15px, font-body, placeholder "Search records, experience, skills..."
- ESC badge: mono 10px, tertiary, bg var(--bg), border, padding 2px 7px, radius 4px

### Results Area
- `overflow-y: auto`, `padding: 8px`, `flex: 1`
- Custom scrollbar (4px)

### Result Sections
Section label: 10px, 600 weight, uppercase, `letter-spacing: 0.08em`, text-tertiary, `padding: 8px 10px 5px`

### Result Items
- `display: flex`, `align-items: center`, `gap: 10px`
- `padding: 9px 10px`, `border-radius: var(--radius-sm)` (6px)
- `cursor: pointer`, `transition: background 0.1s`
- 13px, text-primary
- Hover/selected: `background: var(--accent-light)`
- Selected also gets: `outline: 1.5px solid var(--accent-border)`

**Item structure:**
- Icon container: 28px square, 6px radius, colored bg per section
  - Experience: teal
  - Core Skills: green
  - Active Projects: amber
  - Achievements: amber
  - Education: purple
  - Quick Actions: teal
- Text: title (500 weight) + subtitle (11px, tertiary, truncated)
- Optional badge: 10px, mono, tertiary

### Fuzzy Search

Adapt existing `src/lib/search.ts` (fuse.js v7.0.0, already installed):

**Existing code:** `src/lib/search.ts` has `buildSearchIndex()` which creates a Fuse index from consultations, medications, problems, investigations, and documents. It groups results by `sectionLabel` via `groupResultsBySection()`. The `SearchResult` interface has `{ id, title, section: ViewId, sectionLabel, highlight }`.

**What needs changing:**
- The `section: ViewId` field is designed for view-switching navigation (navigating to `#consultations`, `#medications`, etc.). The new dashboard has no views — it's a single scrollable page. Results should either scroll to the relevant tile or expand an item within a tile.
- Add `skills.ts` data to the index (currently only `medications.ts` is indexed, not the new 5-skill entries)
- Add `kpis.ts` data to the index
- Add Quick Actions (Download CV, Send Email, View LinkedIn, View Projects)
- Update section labels to match palette grouping: "Experience", "Core Skills", "Active Projects", "Achievements", "Education", "Quick Actions"
- Add an `action` field to `SearchResult` so each result knows what to do when selected (scroll to tile, expand item, open link, etc.)

**Config (keep existing):**
- `threshold: 0.3`, weighted keys (title: 2, content: 1)
- `minMatchCharLength: 2`
- Group results by section
- Highlight matching text in titles using `<mark>` with accent-light background

### Keyboard Navigation
- **Arrow Down/Up**: move selection through results
- **Enter**: select highlighted result (navigate to section or trigger action)
- **Escape**: close palette
- `selectedIndex` state tracks which result is highlighted
- Auto-scroll highlighted result into view

### Quick Actions Section
| Title | Subtitle | Action |
|-------|----------|--------|
| Download CV | Export as PDF | Trigger download |
| Send Email | andy@charlwood.xyz | `mailto:` link |
| View LinkedIn | Professional profile | External link |
| View Projects | GitHub & portfolio | External link |

### Footer
- `display: flex`, `gap: 12px`
- `padding: 10px 18px`, `border-top: 1px solid var(--border-light)`
- 11px, text-tertiary
- Keyboard hints: `↑ ↓ Navigate`, `Enter Select`, `Esc Close`
- Each key in `<kbd>` styled element

### Reduced Motion
- No scale/translate entrance animation
- Instant show/hide (opacity only, or immediate)

### State Management
```typescript
const [isOpen, setIsOpen] = useState(false)
const [query, setQuery] = useState('')
const [selectedIndex, setSelectedIndex] = useState(-1)
```

Render the palette at the DashboardLayout level so it overlays everything.
