Files
portfolio/.ralph/history.jsonl
T
2026-02-18 12:25:53 +00:00

8 lines
26 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"ts":"2026-02-17T20:59:01.482666643Z","type":{"kind":"loop_started","prompt":"# Task: Fix Mobile Responsiveness for Small Viewport Widths (≤430px)\n\nThe portfolio website is broken on phones with viewport widths around 400px. Text overflows off-screen, elements are hidden behind `overflow: hidden`, and layout components are sized inappropriately for small screens.\n\n## Context\n\n- **Tech stack:** React + TypeScript + Tailwind CSS + Framer Motion + D3\n- **Dev server:** `npm run dev` (localhost:5173)\n- **Quality gates:** `npm run lint && npm run typecheck && npm run build`\n- **Smallest configured breakpoint:** `xs: 480px` in tailwind.config.js — there is no sub-480px handling\n- **Key layout file:** `src/components/DashboardLayout.tsx` orchestrates all dashboard tiles\n- **CSS media queries:** `src/index.css` contains most custom responsive rules\n- **Tailwind config:** `tailwind.config.js` defines breakpoints and theme\n\n## Target Viewports\n\nTest and fix at these widths (all portrait orientation, 812px height):\n- **320px** — iPhone SE / smallest realistic phone\n- **360px** — Common Android (Samsung Galaxy S series)\n- **375px** — iPhone 12 mini / iPhone SE 3rd gen\n- **390px** — iPhone 14\n- **400px** — User's specific device (primary target)\n- **414px** — iPhone 8 Plus / larger phones\n- **430px** — iPhone 14 Pro Max\n\n## Known Issues (from codebase analysis)\n\n### Critical\n1. **Sidebar must become a bottom nav bar at <600px** — The current sidebar is a 304px-wide overlay on mobile, leaving only 96px for content at 400px. At viewport widths below 600px, replace the sidebar with a **bottom navigation bar** that:\n - **Collapsed state (default):** A slim fixed bar along the bottom edge of the screen with icon-based navigation items (like a mobile tab bar / iOS-style bottom nav). Should not obscure content — main content area accounts for its height.\n - **Expanded state (on tap/click):** Slides up as a drawer/sheet showing the full sidebar content (patient name, navigation links, etc.). Tapping the bar or a close affordance collapses it back down.\n - The existing sidebar behavior for viewports ≥600px should remain completely unchanged.\n - Use Framer Motion for the drawer slide animation, consistent with existing animation patterns.\n2. **KPI grid forces 2 columns** — `repeat(2, minmax(0, 1fr))` creates cramped cards at small widths. Values use `30px` font.\n3. **Timeline text silently clipped** — `overflow: hidden` on Card.tsx hides content with no visual indication (no ellipsis, no wrapping).\n4. **Project carousel cards too small** — At 400px with 2 cards per view, each card is only ~194px wide.\n\n### Important\n5. **Constellation graph** — 520px height at <768px may be disproportionate; needs better sizing.\n6. **No sub-480px breakpoint** — The smallest Tailwind breakpoint is `xs: 480px`, leaving 320-479px unhandled.\n\n### Minor\n8. **Padding/spacing** — `p-5` (20px) main content + `24px` card padding eats significant space at 400px.\n9. **Detail panel header** — Close button (44px) + title cramped at narrow widths.\n10. **Skills/medications grid** — May need column count reduction at small widths.\n\n## Requirements\n\n- **All text must be visible** — no text clipped by `overflow: hidden` without ellipsis or wrapping. Truncated text needs `text-overflow: ellipsis` with title attribute for accessibility.\n- **All interactive elements must be reachable** — nothing hidden off-screen or behind other elements.\n- **Touch targets** — minimum 44x44px for interactive elements.\n- **Readable font sizes** — minimum 12px body text, 14px primary content.\n- **No horizontal scroll** — page must never scroll horizontally at any target viewport.\n- **Maintain visual identity** — keep PMR aesthetic, teal/coral palette, existing design language. Adapt proportionally, don't radically redesign.\n- **Respect existing patterns** — use Tailwind classes where possible, use existing CSS custom properties, follow project conventions.\n\n## Success Criteria\n\nAll of the following must be true:\n\n- [ ] `npm run lint` passes with zero errors\n- [ ] `npm run typecheck` passes with zero errors\n- [ ] `npm run build` succeeds\n- [ ] At 320px viewport: no horizontal scrollbar, all text readable, no content clipped without indication\n- [ ] At 360px viewport: same as above\n- [ ] At 375px viewport: same as above\n- [ ] At 390px viewport: same as above\n- [ ] At 400px viewport: same as above (primary target)\n- [ ] At 414px viewport: same as above\n- [ ] At 430px viewport: same as above\n- [ ] At <600px: sidebar is replaced by a bottom nav bar with collapsed (tab bar) and expanded (drawer) states\n- [ ] Bottom nav bar does not obscure page content in collapsed state (content has bottom padding/margin to account for it)\n- [ ] Bottom nav drawer expands on tap and shows full navigation content\n- [ ] Bottom nav drawer can be collapsed back down\n- [ ] At ≥600px: existing sidebar behavior is completely unchanged\n- [ ] KPI cards are readable with appropriate font sizing\n- [ ] Timeline entries show full text or have proper ellipsis truncation\n- [ ] Project carousel cards are adequately sized (consider 1 card per view if needed)\n- [ ] Constellation graph fits within viewport without excessive scrolling\n- [ ] Desktop/tablet layouts (768px+) remain unchanged and unbroken\n\n## Constraints\n\n- Do NOT change boot sequence, ECG animation, or login screen (already handle small screens)\n- Do NOT change D3 force simulation logic — only container sizing\n- Do NOT add new npm dependencies\n- Do NOT remove existing features or functionality\n- Keep changes minimal and focused — fix responsiveness, don't redesign\n- Preserve all existing breakpoint behavior for md (768px) and above\n\n## Visual Validation Method\n\nUse Playwright MCP to:\n1. Navigate to `http://localhost:5173` (dev server must be running)\n2. Get past boot sequence + ECG + login to reach dashboard\n3. Set viewport to each target width × 812px height\n4. Take screenshots of dashboard at each viewport\n5. Visually inspect for: overflow, clipping, cramped text, unreachable elements\n6. Scroll through full page and verify no content is hidden\n\n## Status\n\nTrack progress here. When all success criteria are met, print LOOP_COMPLETE.\n"}}
{"ts":"2026-02-17T21:19:40.088056091Z","type":{"kind":"loop_completed","reason":"completion_promise"}}
{"ts":"2026-02-18T00:22:20.061289826Z","type":{"kind":"loop_started","prompt":"# Task: Portfolio UX Improvements — GP Clinical System Theme Polish\n\nImplement 11 prioritised UX improvements to the portfolio site. This is an interactive CV/portfolio themed as a GP primary care clinical system (like EMIS Web / SystmOne). The site should feel like a real GP system but function as a portfolio.\n\n**Important constraints:**\n- Do NOT change the overall structure or architecture\n- Preserve the GP clinical system theme — improvements should reinforce it, not break it\n- Respect existing conventions: TypeScript strict, Tailwind + CSS custom properties, Framer Motion with `prefers-reduced-motion`\n- Path alias: `@/*` → `src/*`\n- Quality gates: `npm run lint && npm run typecheck && npm run build`\n\n## Improvements (ordered by priority)\n\n### 1. Restructure Profile Summary Text\n**File:** `src/components/tiles/PatientSummaryTile.tsx` (or wherever the narrative renders)\n**Problem:** The patient summary narrative is a dense ~80-word paragraph — a wall of text. It's the first substantive content visitors see and doesn't match the structured clinical aesthetic.\n**Change:** Break into structured clinical-style data:\n- Brief 1-2 sentence summary (like a presenting complaint)\n- Key facts as labeled fields below: Specialisation, Current System, Population, Focus Areas\n- Or collapse behind \"Read more\" with first sentence visible\n- Must feel like GP system structured data, not a LinkedIn About section\n\n### 2. Surface Impact Metrics on Project Cards\n**File:** `src/components/tiles/ProjectsTile.tsx` (or the project card component)\n**Problem:** `resultSummary` exists in the data (e.g., \"14,000 patients identified\", \"£2.6M savings\") but is not rendered on project card faces. Recruiters scan for numbers.\n**Change:** Render `resultSummary` prominently on each project card — below the title, styled as a bold stat. If a project has no `resultSummary`, don't show a placeholder.\n\n### 3. Add Prominent Contact/Download CV CTA\n**Problem:** No visible \"Get in touch\" or \"Download CV\" button in the main content area. These actions only exist in the sidebar or command palette.\n**Change:** Add a small, visible row of action buttons (Email, LinkedIn, GitHub, Download CV) in the Patient Summary section. Style them as GP system action buttons to reinforce the theme. Keep it compact — not a hero CTA, but unmissable.\n\n### 4. Reduce Boot + Login Sequence Time\n**Files:** `src/components/BootSequence.tsx`, `src/components/LoginScreen.tsx`\n**Problem:** Boot (~6-8s) + Login (~4s) = ~10 seconds before content. Too slow for repeat visitors.\n**Change:** Reduce `TYPING_SPEED` multiplier to ~1.2 (from 2). Add `sessionStorage` detection — if user has visited before in this session, auto-skip directly to dashboard. Ensure skip button still appears early for first-time visitors.\n\n### 5. Resolve Last Consultation / Timeline Duplication\n**Files:** `src/components/tiles/LastConsultationCard.tsx`, `src/components/tiles/TimelineInterventionsSubsection.tsx`\n**Problem:** Current role appears twice — once as LastConsultationCard and again as first timeline accordion entry. Redundant.\n**Change:** Differentiate LastConsultationCard as a summary-only card (role, org, band, date range, one-line summary) without the full bullet points. The full details should only appear in the timeline accordion. Add a \"Current\" badge to the first timeline accordion entry.\n\n### 6. Fix Text-Tertiary Contrast Ratio\n**File:** `src/index.css`\n**Problem:** `--text-tertiary: #8DA8A5` on `--bg-dashboard: #F0F5F4` yields ~2.8:1 contrast, failing WCAG AA.\n**Change:** Darken `--text-tertiary` to at least `#6B8886` (achieves ~4.5:1 on `#F0F5F4`). Verify the change looks good across dates, helper text, and monospace metadata.\n\n### 7. Add Mobile Identity Bar\n**Problem:** On mobile, no name or identity marker is visible without opening the drawer. Recruiters on mobile have no visual anchor.\n**Change:** Add a compact identity bar at the top of mobile layout showing \"CHARLWOOD, Andrew\" and brief role title. Only visible on mobile (below `lg` breakpoint where sidebar is hidden). Style it like a GP system patient banner strip.\n\n### 8. Simplify KPI Section Header Language\n**File:** The KPI/metrics section component\n**Problem:** \"LATEST RESULTS (CLICK TO VIEW FULL REFERENCE RANGE)\" is deep medical jargon that non-healthcare visitors won't understand.\n**Change:** Change to \"KEY METRICS\" or \"IMPACT HIGHLIGHTS\". Update the helper text to \"Select a metric to inspect methodology, impact, and outcomes\" (if not already). Keep the excellent metric cards unchanged.\n\n### 9. Add Detail Panel Exit Animation\n**Files:** `src/components/DetailPanel.tsx`\n**Problem:** Panel has `panel-slide-in` animation but closes instantly. `panel-slide-out` keyframe exists in CSS but is unused.\n**Change:** Implement exit animation — either wire up the existing `panel-slide-out` keyframe via a closing state, or use Framer Motion's `AnimatePresence`. The panel should slide out before unmounting.\n\n### 10. Fix marginBottom Typo\n**File:** `src/components/tiles/LastConsultationCard.tsx` (around line 89)\n**Problem:** `marginBottom: '1=px'` — typo, should be `'1px'` or appropriate value.\n**Change:** Fix the typo. Check surrounding styles for the correct intended value.\n\n### 11. Add Arrow Navigation to Desktop Projects Carousel\n**File:** `src/components/tiles/ProjectsTile.tsx` — `ContinuousScrollCarousel` component (lines ~356480)\n**Problem:** The ContinuousScrollCarousel (desktop ≥1024px) auto-scrolls but offers no manual browsing.\n**Change:**\n- Add prev/next arrow buttons (ChevronLeft, ChevronRight from lucide-react) positioned absolutely at left/right edges, vertically centered\n- Style following the existing FullscreenButton pattern: `var(--surface)` background, `var(--border)` border, opacity hover effect, subtle shadow\n- Arrow click handler: jump one card width + gap = `((viewportWidth - 36) / 4) + 12` pixels\n- Apply temporary CSS transition on the track (`transform 0.4s ease`) for smooth animated jump; remove transition after completion so rAF loop isn't fighting CSS\n- Handle wrapping: keep offset within `[0, firstSetWidth)` using modulo\n- Pause/resume: on arrow click set `isPausedRef = true`, clear existing timeout, start 6-second timeout to resume auto-scroll\n- Existing hover pause/resume still works independently\n- Rapid clicks: each click resets the 6s timeout; transition handles overlapping clicks by snapping to current offset\n- Reduced motion: arrows still work (instant jump, no transition), auto-scroll stays disabled per existing logic\n\n## Success Criteria\n\nAll of the following must be true:\n- [ ] Profile summary is structured data, not a text wall — feels clinical\n- [ ] Project cards display `resultSummary` when available\n- [ ] Contact/Download CV actions are visible in the main content area\n- [ ] Boot + login sequence completes in ~5 seconds or less for first visit; instant skip for return visitors\n- [ ] LastConsultationCard is a distinct summary (no duplication with timeline)\n- [ ] `--text-tertiary` passes WCAG AA contrast (4.5:1) on dashboard background\n- [ ] Mobile shows identity/name without opening drawer\n- [ ] KPI header uses plain language, not clinical jargon\n- [ ] Detail panel has exit animation (slide out, not instant disappear)\n- [ ] marginBottom typo is fixed\n- [ ] Desktop projects carousel has prev/next arrow buttons\n- [ ] Arrow buttons pause auto-scroll for 6s then resume\n- [ ] `npm run lint` passes\n- [ ] `npm run typecheck` passes\n- [ ] `npm run build` passes\n- [ ] No regressions — existing functionality preserved\n\n## Status\n\nTrack progress here. Mark items complete as you go.\nWhen all success criteria are met, print LOOP_COMPLETE.\n"}}
{"ts":"2026-02-18T00:42:07.039344987Z","type":{"kind":"loop_completed","reason":"completion_promise"}}
{"ts":"2026-02-18T03:08:49.198921526Z","type":{"kind":"loop_started","prompt":"# Task: Fix & Enhance Mobile Patient Banner\n\nFix the broken mobile patient banner (`MobilePatientBanner.tsx`) and add action buttons (Download CV, Contact Patient, LinkedIn, GitHub) to its expanded section. Ensure the referral form modal works properly on mobile screens.\n\n## Files\n\n| File | Role |\n|------|------|\n| `src/components/MobilePatientBanner.tsx` | Primary target — banner component |\n| `src/components/ReferralFormModal.tsx` | Contact form modal — needs mobile adaptation |\n| `src/components/DashboardLayout.tsx` | Mounts banner at line 303 inside `<motion.main>` |\n| `src/components/Sidebar.tsx` | Reference for existing button styles, URLs, and referral form wiring |\n\n## Part 1: Fix Banner Bugs\n\n### Bug 1: Gap at top of viewport\nVisible gap between the banner's green header and the top of the viewport. The banner uses negative margins (`-mx-3 xs:-mx-5 -mt-3 xs:-mt-5`) to counteract parent `<main>` padding (`p-3 xs:p-5`), but this doesn't work with `position: sticky; top: 0` inside a padded scroll container.\n\n### Bug 2: Click-to-collapse broken\n`handleToggle` (line 6272) only ever sets `expanded` to `true`. When already expanded, it returns `prev` unchanged. Clicking the expanded banner should collapse it.\n\n### Bug 3: Click-to-open unreliable\nTapping the collapsed banner sometimes doesn't visibly open. The scroll event listener fires immediately after the click, re-collapsing before the animation completes. The `expandedByClickRef` guard doesn't fully prevent this race.\n\n## Part 2: Add Action Buttons\n\nAdd buttons to the expanded panel (below the data rows) matching the sidebar's Contact section layout:\n\n**Layout (top to bottom):**\n1. **Download CV** — full-width button with icon + text label. Links to `/References/CV_v4.md` (same as sidebar). Style: accent-bordered, matches sidebar's download button aesthetic.\n2. **Three icon-only buttons in a row** (equal-width, 3 columns):\n - **Contact Patient** — `Send` icon. Opens `ReferralFormModal` (needs state + handler).\n - **LinkedIn** — `Linkedin` icon. Links to `https://linkedin.com/in/andycharlwood`, opens in new tab.\n - **GitHub** — `Github` icon. Links to `https://github.com/andycharlwood`, opens in new tab.\n\nThe 3 icon buttons should be the same total width as the Download CV button above them. Icon-only (no text labels) to save vertical space on mobile. Use accessible `aria-label` attributes on each.\n\n**Style reference:** The sidebar (`Sidebar.tsx` lines 436595) has these exact buttons with text labels. Match border, colour, and radius styles but make them icon-only for mobile.\n\n## Part 3: Mobile Referral Form\n\nThe `ReferralFormModal.tsx` currently uses `maxWidth: 540px` with desktop-oriented padding. It needs to work on screens ≤599px (the `useIsMobileNav` breakpoint).\n\nKey issues to address:\n- Form should use full viewport width on mobile (no side padding gaps)\n- Form fields need proper touch-target sizing (min 44px height)\n- The modal should be scrollable when content exceeds viewport height\n- Close button needs adequate touch target\n- On very small screens, reduce internal padding to prevent cramped layout\n- Validate that all form fields remain usable and form submission works\n\n**Important:** The form already has a working backend (`server.ts` POST `/api/contact`). Do NOT change the API contract or field names. Focus only on mobile layout/UX.\n\n## Success Criteria\n\nAll of the following must be true:\n\n### Banner bugs\n- [ ] No visible gap between banner and top of viewport on mobile (≤599px)\n- [ ] Tapping the collapsed banner reliably expands it (every time)\n- [ ] Tapping the expanded banner collapses it\n- [ ] Scrolling down 40px+ from top collapses an auto-expanded banner\n- [ ] Scrolling down 20px+ after a click-expand collapses it\n- [ ] Chevron animates correctly (180° rotation when expanded, bounce hint when collapsed)\n- [ ] AnimatePresence height animation is smooth\n\n### Action buttons\n- [ ] Download CV button is full-width with icon + text\n- [ ] Three icon buttons (Contact, LinkedIn, GitHub) display in equal-width columns\n- [ ] Contact button opens the referral form modal\n- [ ] LinkedIn and GitHub links open in new tabs\n- [ ] All buttons have appropriate aria-labels\n- [ ] Buttons match the design language of the sidebar's equivalent buttons\n\n### Referral form mobile\n- [ ] Form modal fills viewport width on mobile\n- [ ] All form fields are usable with touch (≥44px targets)\n- [ ] Modal is scrollable when content exceeds viewport\n- [ ] Form submission works (client-side validation + POST to /api/contact)\n- [ ] Close button has adequate touch target\n\n### Quality gates\n- [ ] `npm run lint` passes\n- [ ] `npm run typecheck` passes\n- [ ] `npm run build` passes\n- [ ] Playwright MCP verification passes on mobile viewport (≤599px)\n\n## Constraints\n\n- Do not add new npm dependencies\n- Do not change `server.ts` or the `/api/contact` API contract\n- Preserve existing Framer Motion animations\n- Preserve all accessibility attributes\n- Follow existing conventions: inline styles + Tailwind classes, TypeScript strict mode\n- Icons come from `lucide-react` (already imported: `Download`, `Github`, `Linkedin`, `Send`, `ChevronDown`)\n\n## Status\n\nTrack progress in `.ralph/plan.md`. When all success criteria are met, print LOOP_COMPLETE.\n"}}
{"ts":"2026-02-18T03:23:25.709000215Z","type":{"kind":"loop_started","prompt":"# Task: Replace Mobile Banner with Inline Overview Section\n\nRemove the sticky `MobilePatientBanner` and replace it with a static inline section at the top of the mobile dashboard. Remove the \"More\" drawer from the bottom nav, since its content now lives inline at the top of the page.\n\n## Files\n\n| File | Role |\n|------|------|\n| `src/components/MobilePatientBanner.tsx` | DELETE — replaced by new inline section |\n| `src/components/MobileBottomNav.tsx` | Remove \"More\" button + entire drawer; add Overview item; rename old Overview to \"Summary\" |\n| `src/components/DashboardLayout.tsx` | Swap MobilePatientBanner for new MobileOverviewHeader; pass onSearchClick |\n| `src/components/MobileOverviewHeader.tsx` | NEW — inline mobile header section |\n| `src/components/ReferralFormModal.tsx` | Already exists — opened from the new section's Contact button |\n| `src/components/Sidebar.tsx` | Reference only — button styles, URLs |\n\n## What to Build\n\n### 1. New `MobileOverviewHeader.tsx`\n\nA static (not sticky) section rendered at the top of mobile `<main>` content, before `PatientSummaryTile`. Visible only when `useIsMobileNav()` is true. Must have `data-tile-id=\"mobile-overview\"` so the bottom nav Overview button can scroll to it.\n\n**Layout (top to bottom), matching the existing \"More\" drawer layout in `MobileBottomNav.tsx` lines 273381:**\n\n1. **Logo + Search row** — `CvmisLogo` (cssHeight \"40px\") + search button (full-width, `minHeight: 44px`, shows search label text). Search button calls `onSearchClick` prop.\n\n2. **Patient info section** (bordered bottom with `2px solid var(--accent)`):\n - Avatar circle (44px, gradient, \"AC\") + name + role title — same layout as drawer lines 301327\n - Data rows: GPhC, Education, Location, Registered, Phone (PhoneCaptcha), Email — same as drawer lines 329356\n\n3. **Tags section** — tag pills, same as drawer lines 360369\n\n4. **Action buttons** (replacing the alerts section):\n - **Download CV** — full-width button with icon + text label. `<a>` to `/References/CV_v4.md`, new tab. Style: accent-bordered, matches sidebar's download button.\n - **Three icon-only buttons in a row** (equal-width grid, 3 columns):\n - **Contact Patient** — `Send` icon. Opens `ReferralFormModal`.\n - **LinkedIn** — `Linkedin` icon. Links to `https://linkedin.com/in/andycharlwood`, new tab.\n - **GitHub** — `Github` icon. Links to `https://github.com/andycharlwood`, new tab.\n - Use the same button styles as the existing `MobilePatientBanner.tsx` action buttons (lines 228323). Icon-only for the 3 buttons, accessible `aria-label` on each.\n\n5. **ReferralFormModal** — render it inside this component, controlled by local `showReferralForm` state.\n\n**Style notes:**\n- Use `padding: 16px` internally (it sits within the main content's `p-3 xs:p-5` padding)\n- Background: `var(--sidebar-bg)` to match the drawer look\n- Bottom margin to separate from PatientSummaryTile\n- Border-radius: `var(--radius-sm)` on the whole container\n- Border: `1px solid var(--border)`\n\n### 2. Modify `MobileBottomNav.tsx`\n\n- **Remove** the \"More\" `<button>` from the bottom tab bar (lines 178199)\n- **Remove** the entire drawer — the `<AnimatePresence>` block (lines 203385) and all drawer state/handlers (`drawerOpen`, `setDrawerOpen`, `handleDrawerKeyDown`)\n- **Remove** unused imports that were only needed by the drawer: `CvmisLogo`, `PhoneCaptcha`, `patient`, `tags`, `alerts`, `getSidebarCopy`, `TagPill`, `AlertFlag`, `X`, `Menu`, `Search`, `AlertCircle`, `AlertTriangle`, `AnimatePresence`, `motion`, `prefersReducedMotion`\n- **Rename** the existing \"Overview\" nav item to **\"Summary\"** with the `ClipboardList` icon (from lucide-react). It keeps its tileId `'patient-summary'`.\n- **Add** a new **\"Overview\"** nav item at position 0 (start of the array) with the `UserRound` icon and tileId `'mobile-overview'` so it scrolls to the new header section.\n- The final nav item order must be: **Overview, Summary, Experience, Skills** (4 items, no \"More\").\n- Clean up: remove any now-unused local components (`TagPill`, `AlertFlag`)\n\n### 3. Modify `DashboardLayout.tsx`\n\n- **Remove** `MobilePatientBanner` import and its render (`{isMobileNav && <MobilePatientBanner />}` at line 303)\n- **Add** import for new `MobileOverviewHeader`\n- **Render** `{isMobileNav && <MobileOverviewHeader onSearchClick={handleSearchClick} />}` in the same position (before `<div className=\"dashboard-grid\">`)\n\n### 4. Delete `MobilePatientBanner.tsx`\n\nThis component is fully replaced. Delete the file.\n\n## Success Criteria\n\nAll of the following must be true:\n\n### New overview section\n- [ ] `MobileOverviewHeader` renders at top of mobile content (before PatientSummaryTile)\n- [ ] Has `data-tile-id=\"mobile-overview\"` attribute\n- [ ] Shows logo + search bar at top\n- [ ] Shows patient avatar, name, role, and all data rows\n- [ ] Shows tag pills\n- [ ] Shows Download CV button (full-width, icon + text)\n- [ ] Shows 3 icon-only buttons (Contact, LinkedIn, GitHub) in a row\n- [ ] Contact button opens ReferralFormModal\n- [ ] LinkedIn and GitHub links open in new tabs\n- [ ] All buttons have appropriate aria-labels\n- [ ] Only visible on mobile (useIsMobileNav)\n\n### Bottom nav changes\n- [ ] \"More\" button is removed from bottom nav\n- [ ] Drawer is completely removed (no AnimatePresence, no overlay)\n- [ ] New \"Overview\" button (UserRound icon) is first in nav and scrolls to `mobile-overview` section\n- [ ] Old \"Overview\" is renamed to \"Summary\" with ClipboardList icon, still scrolls to `patient-summary`\n- [ ] Bottom nav has exactly 4 items in order: Overview, Summary, Experience, Skills\n\n### Cleanup\n- [ ] `MobilePatientBanner.tsx` is deleted\n- [ ] No dead imports remain in any modified file\n- [ ] No unused components (TagPill, AlertFlag) remain in MobileBottomNav\n\n### Quality gates\n- [ ] `npm run lint` passes\n- [ ] `npm run typecheck` passes\n- [ ] `npm run build` passes\n- [ ] Playwright MCP verification passes on mobile viewport (375x812)\n\n## Constraints\n\n- Do not add new npm dependencies\n- Do not change `server.ts` or the `/api/contact` API contract\n- Preserve all accessibility attributes (aria-labels, aria-expanded, etc.)\n- Follow existing conventions: inline styles + Tailwind classes, TypeScript strict mode\n- Icons from `lucide-react` only\n- Respect `prefers-reduced-motion` for any animations\n- The new section is NOT sticky — it scrolls with content\n\n## Status\n\nTrack progress in `.ralph/plan.md`. When all success criteria are met, print LOOP_COMPLETE.\n"}}
{"ts":"2026-02-18T04:13:20.978112583Z","type":{"kind":"loop_completed","reason":"max_iterations"}}