`, ``. Lists use ``/`` with `- `. No div-based tables.
-**Why:** Screen readers require native semantics.
-
-### When: Using icons
-**Rule:** Use `lucide-react` icons only. No unicode symbols, no inline SVG copied from external sources. Exception: the concept's SVG icons should be converted to their lucide-react equivalents (e.g., concept's house icon → `Home` from lucide-react).
-**Why:** Consistent icon system, tree-shakeable, accessible.
-
-## Data Guardrails
-
-### When: Displaying CV content
-**Rule:** All data must come from `src/data/*.ts` files. Do NOT hardcode content in components or change any numbers/dates. New data files (profile.ts, tags.ts, alerts.ts, kpis.ts, skills.ts) must be accurate to CV_v4.md.
-**Why:** Data has been validated against CV_v4.md. Single source of truth.
-
-### When: Building the "Repeat Medications" (skills) tile
-**Rule:** Use the exact frequencies specified by the user: Data Analysis="Twice daily", Power BI="Once weekly", Python="Daily", SQL="Daily", JavaScript/TypeScript="When required". Include "years of experience" like "length of time on medication".
-**Why:** User explicitly specified these frequency values for the medication metaphor.
-
-## Visual Review Guardrails
-
-### When: Completing any visual task
-**Rule:** After quality checks, open `http://localhost:5173` via Playwright MCP tools, take a screenshot, and compare against `References/GPSystemconcept.html`. Fix visual discrepancies. If browser tools are unavailable, note in progress.txt and proceed.
-**Why:** Code review alone cannot catch visual issues.
-
-### When: Browser tools fail
-**Rule:** Skip visual review, note it in progress.txt, continue. Do NOT retry more than twice.
-**Why:** Visual review is valuable but not blocking.
-
-## Technical Guardrails
-
-### When: Writing TypeScript
-**Rule:** No `any` types. All props must have typed interfaces.
-**Why:** Strict typing prevents runtime errors.
-
-### When: Adding animations
-**Rule:** All animations must respect `prefers-reduced-motion`. With reduced motion: all animations skip to final state instantly.
-**Why:** Accessibility requirement.
-
-### When: Running quality checks
-**Rule:** Run `npm run typecheck`, `npm run lint`, and `npm run build` after EVERY task. Fix all errors before committing.
-**Why:** Build failures compound across iterations.
-
-### When: Referencing the concept design
-**Rule:** The reference design is `References/GPSystemconcept.html`. Open it in a browser or read the HTML to understand the visual target. The concept is the LAYOUT reference; production fonts and some colors differ (see font and color guardrails).
-**Why:** The concept HTML is the single source of truth for layout and spatial composition.
diff --git a/Ralph/progress.txt b/Ralph/progress.txt
deleted file mode 100644
index caf66be..0000000
--- a/Ralph/progress.txt
+++ /dev/null
@@ -1,190 +0,0 @@
-# Progress Log — Career Constellation Refinement
-# Branch: ralph/constellation-refinement
-# Started: 2026-02-16
-
-## Codebase Patterns
-- CareerConstellation.tsx (~868 lines) is a D3 force-directed graph with React overlay buttons for accessibility
-- D3 simulation uses forceSimulation with charge, link, x, y, and collide forces
-- Module-level window.matchMedia reads for prefersReducedMotion and supportsCoarsePointer
-- DashboardLayout manages constellation state: highlightedNodeId, pinnedNodeId via callbacks
-- Work experience data in src/data/consultations.ts, constellation-specific data in src/data/constellation.ts
-- CSS layout: .pathway-columns grid — first column is .chronology-stream (work experience), second is .pathway-graph-sticky (constellation graph)
-- Current grid: minmax(0, 1.85fr) minmax(0, 1fr) at desktop — work experience ~65%, graph ~35%
-- containerHeight prop drives graph height on desktop; on mobile (viewport < 1024px) uses MOBILE_FALLBACK_HEIGHT (360px)
-- Use window.innerWidth for breakpoint checks, not container.clientWidth — the SVG container overflows on mobile
-- Design tokens in index.css :root — use var(--accent), var(--border-light), var(--text-tertiary), etc.
-- SVG shadows: use with in , apply to groups via .attr('filter', 'url(#filter-id)')
-- Role nodes are pill-shaped rects (ROLE_WIDTH=104, ROLE_HEIGHT=32, ROLE_RX=16) with orgColor badge styling
-- Skill nodes use SKILL_RADIUS_DEFAULT (7) resting, SKILL_RADIUS_ACTIVE (11) highlighted — D3 transitions, not CSS
-- Link lines are elements with quadratic bezier curves — tick handler sets d attr
-- Accessibility buttons are React
)
}
export function PatientSummaryTile() {
- const [showCoachmark, setShowCoachmark] = useState(false)
-
- useEffect(() => {
- if (typeof window === 'undefined') return
- const hasDismissed = window.localStorage.getItem(KPI_COACHMARK_KEY) === '1'
- if (!hasDismissed) {
- setShowCoachmark(true)
- }
- }, [])
-
- const handleMetricOpen = () => {
- if (!showCoachmark) return
- setShowCoachmark(false)
- window.localStorage.setItem(KPI_COACHMARK_KEY, '1')
- }
-
const profileTextStyles: React.CSSProperties = {
fontSize: '15px',
lineHeight: '1.65',
@@ -147,7 +118,8 @@ export function PatientSummaryTile() {
const kpiGridStyles: React.CSSProperties = {
display: 'grid',
- gap: '16px',
+ gap: '10px',
+ gridTemplateColumns: '1fr',
}
return (
@@ -165,20 +137,22 @@ export function PatientSummaryTile() {
{/* Latest Results subsection */}
-
-
- Select a metric to inspect methodology, impact, and outcomes.
-
-
- {kpis.map((kpi, index) => (
-
+
+
+
+ Select a metric to inspect methodology, impact, and outcomes.
+
+
+
+ {kpis.map((kpi) => (
+
))}
diff --git a/src/index.css b/src/index.css
index 842540d..17d841c 100644
--- a/src/index.css
+++ b/src/index.css
@@ -204,26 +204,6 @@ body {
animation: fadeIn 200ms ease-out forwards;
}
-@keyframes kpiPulse {
- 0%, 100% {
- box-shadow: 0 0 0 0 rgba(10, 128, 128, 0.12);
- }
- 50% {
- box-shadow: 0 0 0 8px rgba(10, 128, 128, 0);
- }
-}
-
-@keyframes coachmarkIn {
- from {
- opacity: 0;
- transform: translateY(-4px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
@@ -314,29 +294,32 @@ html {
transform: translateY(0) scale(0.992);
}
-.metric-card-pulse {
- animation: kpiPulse 1.8s ease-out infinite;
+.latest-results-header {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
}
-.kpi-card-coachmark-target {
- margin-top: 24px;
+.latest-results-header > div {
+ margin-bottom: 0 !important;
}
-.kpi-coachmark {
- position: absolute;
- top: -28px;
- left: 0;
- z-index: 2;
- padding: 4px 8px;
- border-radius: 999px;
- font-size: 11px;
- font-weight: 600;
- letter-spacing: 0.02em;
- font-family: var(--font-geist-mono);
- color: var(--accent);
- background: rgba(10, 128, 128, 0.1);
- border: 1px solid var(--accent-border);
- animation: coachmarkIn 180ms ease-out;
+.latest-results-grid {
+ margin-top: 12px;
+}
+
+@media (min-width: 768px) {
+ .latest-results-header {
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ }
+
+ .latest-results-grid {
+ grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
+ }
}
/* Dashboard card grid responsive — mobile-first */
|