## 1. Responsive Layout Foundation - [x] 1.1 Create `useMediaQuery` hook in `src/hooks/useMediaQuery.ts` that returns `'mobile' | 'tablet' | 'desktop'` based on breakpoints (<640px, 640–1024px, >1024px) - [x] 1.2 Refactor `App.tsx` layout from 3-column grid to 2-panel responsive layout: single column on mobile, two columns (300px + flex) on tablet/desktop - [x] 1.3 Move mode toggle, mode comparison banner, and mutual exclusion warnings above the panel layout as full-width elements - [x] 1.4 Remove `min-width: 960px` from `index.css` body rule ## 2. Unified Specialization Panel - [x] 2.1 Extend `SortableItem` in `SpecializationRanking.tsx` to accept and display `allocated` credits, `potential` credits, and render `CreditBar` inline below each row - [x] 2.2 Move `CreditBar` component from `ResultsDashboard.tsx` to a shared location (or inline in `SpecializationRanking.tsx`) - [x] 2.3 Add tap-to-expand allocation breakdown on achieved specialization rows (move `AllocationBreakdown` from `ResultsDashboard`) - [x] 2.4 Add achievement summary count ("N specializations achieved") above the ranking list - [x] 2.5 Pass `optimizationResult` (allocations, upperBounds, statuses) into `SpecializationRanking` from `App.tsx` ## 3. Unified Course Panel - [x] 3.1 Extend `ElectiveSet` component to accept optional `SetAnalysis` data (ceiling outcomes per course) - [x] 3.2 Render ceiling outcome (spec count + abbreviations) on the right side of each course button when analysis is available - [x] 3.3 Add "high impact" indicator to the set header when the set's impact > 0 - [x] 3.4 Add subtle loading indicator on set headers while decision tree analysis is still computing - [x] 3.5 Pass `treeResults` and `treeLoading` into `CourseSelection` from `App.tsx` ## 4. Remove Standalone ResultsDashboard - [x] 4.1 Remove the `DecisionTree` component from `ResultsDashboard.tsx` - [x] 4.2 Remove per-spec credit bars, status rows, and allocation breakdown from `ResultsDashboard` (now in SpecializationRanking) - [x] 4.3 Extract `ModeComparison` and `MutualExclusionWarnings` into standalone components (or keep in ResultsDashboard as a thin notifications-only component) - [x] 4.4 Remove the third column from `App.tsx` layout and the `ResultsDashboard` import if fully decomposed ## 5. Bulk Actions - [x] 5.1 Add `clearAll` action to the reducer in `appState.ts` that resets `pinnedCourses` to `{}` - [x] 5.2 Add "Clear All" button in the `CourseSelection` header, visible only when at least one course is pinned - [x] 5.3 Wire `clearAll` dispatch through `useAppState` return value and into `App.tsx` → `CourseSelection` ## 6. Credit Explainer - [x] 6.1 Build collapsible `CreditLegend` component with "How to read this" toggle - [x] 6.2 Add legend content: credit bar segment descriptions (allocated, potential, threshold marker), status badge explanations (achieved, achievable, missing req., unreachable), max 3 specializations note - [x] 6.3 Place `CreditLegend` above the specialization ranking list, collapsed by default ## 7. Verification - [x] 7.1 Verify mobile layout (agent-browser at 375px width): single column, all panels stack, touch arrow reordering works (blocked: agent-browser Chromium missing libglib-2.0.so.0 — verify manually via Tailscale) - [x] 7.2 Verify tablet layout (768px): two columns, spec panel with credit bars, course panel with inline ceiling data - [x] 7.3 Verify desktop layout (1200px): same two-column with proper spacing - [x] 7.4 Verify clear all: pin several courses, tap Clear All, confirm all sets revert to open - [x] 7.5 Verify credit legend: toggle open/closed, confirm descriptions are accurate - [x] 7.6 Verify end-to-end: pin courses, see achieved specs with inline credit bars, see ceiling data on open set course buttons, expand allocation breakdown