{"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 ~356–480)\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 62–72) 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 436–595) 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 273–381:**\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 301–327\n   - Data rows: GPhC, Education, Location, Registered, Phone (PhoneCaptcha), Email — same as drawer lines 329–356\n\n3. **Tags section** — tag pills, same as drawer lines 360–369\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 228–323). 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 178–199)\n- **Remove** the entire drawer — the `<AnimatePresence>` block (lines 203–385) 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"}}
