## Context The app currently uses a fixed 3-column grid layout (`280px 1fr 1fr`): specialization ranking on the left, course selection in the middle, results dashboard on the right. This breaks completely on mobile — it requires a minimum ~960px viewport. Specialization progress (credit bars, status badges) lives in a separate Results panel from the ranking list, forcing users to cross-reference two panels. The decision tree is buried at the bottom of the Results panel, disconnected from the course selection it's meant to guide. Current component structure: - `App.tsx` — 3-column grid, wires state to components - `SpecializationRanking.tsx` — drag-and-drop ranking with status badges (no credit bars) - `CourseSelection.tsx` — elective sets grouped by term, pin/unpin per set - `ResultsDashboard.tsx` — credit bars, allocation breakdown, decision tree, mode comparison, mutual exclusion warnings - `ModeToggle.tsx` — toggle between maximize-count and priority-order - `state/appState.ts` — useReducer with reorder/setMode/pinCourse/unpinCourse actions ## Goals / Non-Goals **Goals:** - Usable on mobile phones (360px+), tablets, and desktops - Single specialization panel that shows rank, reorder controls, status, and credit progress together - Course selection UI that shows decision tree ceiling outcomes inline with each course option - Clear all selections with one action - New users can understand the credit bars and status badges without external docs **Non-Goals:** - Complete visual redesign / theming — keep existing colors and inline style approach - Offline/PWA support - Changing the optimization engine or data layer - Touch-based drag reordering on mobile (arrow buttons already work; drag is a nice-to-have that already has TouchSensor) ## Decisions ### 1. Responsive layout: CSS media queries via inline styles with a `useMediaQuery` hook Use a custom `useMediaQuery` hook that returns the current breakpoint. App.tsx switches between layouts: - **Mobile (<640px)**: Single column, stacked vertically. Specializations panel, then course panel. Each is full-width. - **Tablet (640–1024px)**: Two columns, specializations left (300px), courses right (flex). - **Desktop (>1024px)**: Same two columns with more breathing room. Why not CSS classes / a CSS framework: The entire app uses inline styles. Adding a CSS framework for just responsive layout would be inconsistent. A hook-based approach keeps the pattern uniform and avoids adding dependencies. ### 2. Unified specialization panel: Extend `SpecializationRanking` to include credit bars Merge the per-spec progress display from `ResultsDashboard` directly into `SpecializationRanking`'s `SortableItem`. Each row becomes: ``` [▲▼] [⠿] [rank] [name] [credits/9.0] [status badge] [====credit bar====] ``` The row is clickable to expand allocation breakdown (for achieved specs). This replaces the top section of `ResultsDashboard`. `ResultsDashboard` is reduced to just global notifications: mode comparison banner, mutual exclusion warnings, and the summary count — displayed above the specialization panel in the layout, not as a separate column. ### 3. Unified course panel: Inline decision tree data per elective set Extend `ElectiveSet` to accept optional ceiling analysis data. When an open set has tree results, each course button shows its ceiling outcome on the right: ``` [Mergers & Acquisitions 3 specs (BNK, FIN, LCM)] [Digital Strategy 3 specs (BNK, FIN, LCM)] ``` The standalone `DecisionTree` component at the bottom of ResultsDashboard is removed. The "high impact" indicator moves to the set header. Loading state shows a subtle spinner on sets still being analyzed. ### 4. Clear all: New reducer action + button in course selection header Add a `clearAll` action to the reducer that resets `pinnedCourses` to `{}`. Place a "Clear All" button in the `CourseSelection` header, visible only when at least one course is pinned. Styled as a small text button (consistent with the per-set "clear" buttons). ### 5. Credit explainer: Collapsible legend above the specialization panel Add a small "How to read this" toggle that expands to show: - What the credit bar segments mean (dark = allocated from pinned courses, light = potential from open sets, tick mark = 9-credit threshold) - What each status badge means (achieved, achievable, missing required course, unreachable) - Brief note that max 3 specializations can be achieved (30 credits / 9 per spec) Collapsed by default to avoid visual noise for returning users. State is not persisted (resets on reload). ### 6. Notifications area: Mode comparison and warnings float above the spec panel `ModeComparison` and `MutualExclusionWarnings` render as banners at the top of the page (below the header/mode toggle, above the specialization panel). They're not tied to a specific column. ## Risks / Trade-offs - **Inline responsive styles are verbose** → Accepted; keeps the project consistent and avoids adding a CSS framework for one change. The `useMediaQuery` hook keeps conditional logic manageable. - **Unified spec rows are denser on mobile** → Mitigated by making credit bar slim (already 6px) and keeping text sizes small. Allocation breakdown is tap-to-expand. - **Decision tree data arrives asynchronously** → Course buttons render immediately without ceiling data; outcomes appear progressively as the worker completes each set. No layout shift since the ceiling text is right-aligned and fits on the same line. - **14 spec rows + 12 elective sets is a lot of vertical content on mobile** → Accepted trade-off; all content is important. The two-section layout (specs then courses) gives a clear reading order. Users can scroll naturally.