diff --git a/CLAUDE.md b/CLAUDE.md index 5ce35eff..11957f9a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ ## "Refined" design scope (civic-editorial facelift) -Full spec + rollout checklist: **`docs/refined-design-system.md`** (read this before extending the look to more pages). Migrated so far: `/` (`src/pages/index.js`), `/projects` (`src/components/ProjectList/*`), the global NavBar (incl. a refined mobile layout — centered logo + refined dropdown), `/about`, `/about/judges`, `/about/mentors` (`Mentorship` rewritten as refined sections), `/about/success-stories`, `/about/completion`, `/sponsor`, `/nonprofits` (`NonProfitList` + new calm card `NonProfitListTileRefined`; original `NonProfitListTile` stays for event pages), `/blog` (`BlogPage`; reused `News` list kept), `/onboarding` (refined chrome; wizard logic intact; **July 2026 content overhaul** — 9 steps: Welcome, Mission, How It Works [full lifecycle incl. post-hackathon Definition of Done + project-status ladder], Get Involved [hacker/mentor/judge/volunteer/nonprofit role cards], Using the Site [site map + first-steps guide + "public portfolio" band: GitHub/Slack/demo-videos/public-profile are what recruiters can review; work is documented as GitHub Issues for public credit — no issues in a repo → pull the code and write them like a PM], Slack, Introduce Yourself, FAQs, Feedback. New sections: `HowItWorksSection`/`RolesSection`/`WebsiteTourSection` in `src/components/Onboarding/`; `JudgingOverview`/`MentoringOverview`/`BuddySystem` are now orphaned — don't re-add them or their claims. FAQ answers must reflect real flows: there are NO project leads, NO commenting on projects, NO buddy system — joining a project = "Want to help?" toggle / its Slack channel / GitHub repo (write Issues like a PM if none exist) / next hackathon. **Design contract:** step content renders inside a scoped `onboardingTheme` ThemeProvider (in `pages/onboarding/index.js` — Fraunces headings, Hanken body, navy/terracotta palette, flat hairline Papers/Cards; don't reintroduce elevation shadows, hover-lifts, or rainbow chips) and every step opens with the shared `StepHeader` (renders `

`; the page masthead owns the `

`). Slack step leads with the two-account gotcha (an ohack.dev login [usually Google] does NOT create a Slack account — join via `/signup`) and real channel deep links: #introductions C01EY49JV8U, #ask-a-mentor C01E5CGDQ74, #random C06BRHRS5BQ, plus per-project #npo-* channels; never reference #help/#team-formation/#buddy-matching/#project-matching — they don't exist), `/signup` (full rewrite: editorial hero, "What you get" benefit cards, numbered join steps beside the framed `join_slack_1.png`, navy CTA band; primary CTA is now a real `` — dropped the JS `router.push`/`window.open` branch; `handleSignupClick` only fires the `CompleteRegistration` GA event), `/volunteer`, `/about/hackers`, `/about/process` (refined chrome; Mermaid flow + Gantt diagrams kept in calm card frames), `/praise` (refined chrome; `PraiseBoard` feed kept), `/hack/code-of-conduct` (full rewrite: editorial hero, core-values cards, numbered accessibility list, expected-vs-unacceptable two-col w/ +/× markers, navy CTA band), `/profile/[userid]` public (`PublicProfile`), and a full refined pass on the own `/profile` editor (`Profile.js` — editorial masthead, hairline sticky tab strip, scoped navy/terracotta MUI theme, `PanelHeader` on every tab, privacy legend on Basic Info, hairline rules in Volunteer History, `.ohx-card` Giveaway Entries; form logic untouched), `/myfeedback` (full rewrite: editorial hero, navy CircularProgress score ring w/ Fraunces number, hairline skill accordions w/ navy LinearProgress bars, `.ohx-card` entries + `.ohx-tag` chips) and `/feedback/[userid]` (`GiveFeedback`, `ssr:false`: hero + `Section`-framed form, navy-themed MUI Select/Radio/Slider/Checkbox, native `.ohx-btn` submit; also fixed a pre-existing hooks-order bug where `if (!user)` returned before `useEffect`), `/hack/[event_id]/manageteam` (full rewrite: `RefinedRoot`+`RefinedFonts`, editorial masthead, `.ohx-card` gating panels, calm `--surface-2` panels for auth/apply gates; split `error` → `teamsError`+`formError`; lazy Slack/nonprofit fetches gated by `activeStep`/`showNewTeamForm` + `slackFetchedRef`/`nonprofitFetchedRef`; `onTeamUpdated` callback propagated to `TeamStatusPanel` so DevPost/demo label updates without reload; `fetchMyTeams()` after submit + scroll to `#team-hub`; `noindex` meta; no PII logs; `active_days=365` for Slack fetch), and `/hack/[event_id]/team/[team_id]` (full rewrite: editorial masthead + hairline `.ohx-card` sections + a **deep-linkable sticky Table of Contents** — sections are `
` with hover `#` copy-link anchors (keyboard accessible via `&:focus-visible`), the TOC lists only present sections (`aria-current="location"`) and tracks the active one via `IntersectionObserver`; embedded `MentorTeamPanel`/`TeamCompletionChecklist` keep their own styling in anchored `headed={false}` wrappers). **Key invariants on this page:** (1) Status labels come from `TEAM_STATUS_OPTIONS.find()` via `statusLabel()` — never render raw enum strings like `NONPROFIT_SELECTED`; winning statuses get a 🏆 prefix from `getWinningStatus()`; `INACTIVE` skips the status tag since the dot already conveys it. (2) `SectionBlock` is **module-scope** (not inside the component) — defining it inside caused a remount storm on every IntersectionObserver `setActiveId` tick, wiping in-progress mentor note drafts and reloading the demo iframe; it receives `copiedId`/`onCopyLink` as props. (3) `parseLocalDate` from `src/lib/dateUtils.js` is used for all event window checks (`eventHasStarted`, `eventEnded`) so date-only strings aren't parsed as UTC midnight. (4) Membership check uses `useTeamMembership(eventId, teamId)` from `src/hooks/use-team-membership.js` — shared by the page and passed as `isOnTeam`/`membershipChecked` props to `TeamCompletionChecklist` (no duplicate fetch). Page uses result to show: "Manage your team" primary CTA for members, a combined nudge card for missing DevPost/demo, and a ghost "Want to join this team?" affordance for non-members when status is joinable. (5) Nonprofit: never render `selected_nonprofit_id` text — only show when `nonprofitData.name` is available; `getStaticProps` fetches `{ name, description }` into `nonprofitData`; a "Nonprofit partner" section card links to `/nonprofit/`. (6) `awards[]` rendered as `ohx-tag--accent` chips after the status tag. (7) All GitHub repos in `github_links[]` rendered (handles both string and `{link,name}` shapes). (8) `getStaticProps` rethrows network errors (ISR keeps the last good version) and returns `notFound` only on genuine 404. **Harmonized (not full rewrite):** `/hack` index (keeps its bespoke finder + `HackPageNav`; hero/CTAs/Support band recolored via inline `RX` tokens). The upcoming/current event cards were refined too: `HackathonList` full-mode heading → Fraunces eyebrow; `EventFeature` full card rewritten to a refined hairline card (eyebrow date + quiet type tag, Fraunces title, navy donation rings, navy-primary/ghost event-link buttons, "View event →"; fixed the old nested-anchor bug; var-fallback colors since `/hack` isn't a `RefinedRoot`). `ImpactMetrics` (shared with the archive) → warm `--surface-2` tiles with Fraunces navy numbers + "Impact at a glance" overline (was rainbow `color`-prop numbers). `/hack/[event_id]` got a **masthead-led refined pass**: body wrapped in `` (it's now the `
`; all section IDs + `TableOfContents` + `FloatingNavigation` preserved), `HackathonHeader` rebuilt into a scope-independent editorial masthead (inline styles w/ `var(--x, fallback)` so `/agenda` + `/census` also benefit), refined "Build a team" buttons + recap teaser. Phase-2 refined the **event-only** sub-components: `NonprofitList` (calm clickable cards + navy view-toggle), `EventConstraints`, `DonationProgress` (navy rings), `EventCountdown` (navy countdown card replacing the gradient + flat timeline), `TeamList`/`TeamCard` (flat hairline cards, Fraunces team names, refined "Team members" label + navy Join button — `TeamList` is event-page-only despite earlier substring matches in admin/event-teams; **profile avatars load lazily per-card via IntersectionObserver with `rootMargin: "200px"`, tracked by `fetchedTeamProfilesRef` Set** — do NOT restore the eager `fetchTeamMemberProfiles(teams)` call on mount that fired N parallel requests for all 30+ cards at once). **Backend C7 enrichment:** `get_single_hackathon_event` (`services/hackathons_service.py`) now calls `_enrich_teams_users_batch` after converting team DocumentReferences — one `db.get_all` across all members of all teams, deduped, so the frontend receives `users[]` already as `{id, user_id, name, nickname, profile_image}` objects. `HackathonResults.js`/`TeamList.js` handle both the old id-string and new object shapes (backwards-compatible). The list endpoint `get_hackathon_list` is NOT enriched — only the single-event getter., and `HackathonLeaderboard` (flat cards, warm stat tiles w/ terracotta icons + Fraunces navy numbers, squared navy Org button). **Phase 3 (done) finished the event page**: `EventLinks` (6 rainbow app buttons → one calm uniform card set + calm social-proof strip), `InteractiveFAQ` (flat hairline accordions), `HackathonResults` (calm `--surface-2` frame, Fraunces navy stat numbers; gold/silver/bronze winner medals kept), `VolunteerList` (`PersonCard` → flat hairline). The **shared** ones (`InteractiveFAQ`, `HackathonResults`) use **inline CSS-var fallbacks** (`var(--ink,#16181D)`) so they render refined on their non-`RefinedRoot` routes too (verified on `/hack/[event_id]/results` + `OnboardingFAQ`) — no per-route gating. A final chrome pass refined `TableOfContents` (navy active pill, was magenta), `FloatingNavigation` (navy FAB), `VolunteerList` (navy "Learn more" + navy availability chips; in-person stays green), and `MentorAvailability` (navy banner/Slack button). The event page is now fully refined end-to-end. Also refined: `/hack/request` (host-an-event page + `HackathonRequestForm`) and `/office-hours`. **`/hack/request` is student-first** — org-type selectable cards default to **university** (was corporate); the **$5k budget minimum was removed** (slider min `$0`, never blocks submit, reframed as optional funding); the donation-% ask is **corporate-only** (forced to 0 at submit otherwise); phone is optional; rainbow Papers → calm `--surface-2` cards; everything navy/terracotta via a scoped MUI `ThemeProvider` (`createTheme(base, …)`). **`formData` keys are preserved** — `HackathonRequestDetailDialog`, the edit page `/hack/request/[request_id]`, and the backend all depend on them. **Scroll gotcha:** step nav now `scrollIntoView`s a `formTopRef` on the form's Paper (`scrollMarginTop:88`), NOT `window.scrollTo(0,0)` (which overshot to page-top now that the form sits below the hero). `/office-hours` now renders its JSON-LD, uses the idempotent `initFacebookPixel` (was a raw `ReactPixel.init` — CWV rule), and dropped the artificial 1s loading delay + dead ICS code. Also refined: `/nonprofit/[nonprofit_id]` (`NonProfit.js` — editorial masthead, meta row as `.ohx-tag`/`.ohx-link`s, projects list, canonical www fix) and `/project/[project_id]` (`Project.js` — `RefinedRoot` chrome, soft breadcrumb, canonical). Both share `ProblemStatement.js` which was fully restyled (purple gradient hero → calm `--surface-2` band; gradient chips → `.ohx-tag`; gradient metric tiles → Fraunces navy tiles; gradient section headers → hairline `.ohx-card`; gradient CTAs → `.ohx-btn`). **Key invariant:** `ProblemStatement` is ONLY ever inside a `RefinedRoot`, so it may use scoped `.ohx-*` classes directly (no inline `var(--x, fallback)` needed). Pass `headingLevel="h2"` from the nonprofit page (N cards, nonprofit name is the `

`) and `headingLevel="h1"` from the project page (project title is the `

`). The "Want to help?" toggle is hidden when `status === "production"` (live projects don't need volunteers) but stays visible for users already helping so they can toggle off; `maintenance` status keeps the toggle. `styles/nonprofit/styles.js` is now orphaned — leave in place. +Full spec + rollout checklist: **`docs/refined-design-system.md`** (read this before extending the look to more pages). Migrated so far: `/` (`src/pages/index.js`), `/projects` (`src/components/ProjectList/*`), the global NavBar (incl. a refined mobile layout — centered logo + refined dropdown), `/about`, `/about/judges`, `/about/mentors` (`Mentorship` rewritten as refined sections), `/about/success-stories`, `/about/completion`, `/sponsor`, `/nonprofits` (`NonProfitList` + new calm card `NonProfitListTileRefined`; original `NonProfitListTile` stays for event pages), `/blog` (`BlogPage`; reused `News` list kept), `/onboarding` (refined chrome; wizard logic intact; **July 2026 content overhaul** — 9 steps: Welcome, Mission, How It Works [full lifecycle incl. post-hackathon Definition of Done + project-status ladder], Get Involved [hacker/mentor/judge/volunteer/nonprofit role cards], Using the Site [site map + first-steps guide + "Anatomy of a project page" numbered walkthrough (problem → code → plan → people; mirrors ProblemStatement.js section order — keep in sync) + "public portfolio" band: GitHub/Slack/demo-videos/public-profile are what recruiters can review; work is documented as GitHub Issues for public credit — no issues in a repo → pull the code and write them like a PM], Slack, Introduce Yourself, FAQs, Feedback. New sections: `HowItWorksSection`/`RolesSection`/`WebsiteTourSection` in `src/components/Onboarding/`; `JudgingOverview`/`MentoringOverview`/`BuddySystem` are now orphaned — don't re-add them or their claims. FAQ answers must reflect real flows: there are NO project leads, NO commenting on projects, NO buddy system — joining a project = "Want to help?" toggle / its Slack channel / GitHub repo (write Issues like a PM if none exist) / next hackathon. **Design contract:** step content renders inside a scoped `onboardingTheme` ThemeProvider (in `pages/onboarding/index.js` — Fraunces headings, Hanken body, navy/terracotta palette, flat hairline Papers/Cards; don't reintroduce elevation shadows, hover-lifts, or rainbow chips) and every step opens with the shared `StepHeader` (renders `

`; the page masthead owns the `

`). Slack step leads with the two-account gotcha (an ohack.dev login [usually Google] does NOT create a Slack account — join via `/signup`) and real channel deep links: #introductions C01EY49JV8U, #ask-a-mentor C01E5CGDQ74, #random C06BRHRS5BQ, plus per-project #npo-* channels; never reference #help/#team-formation/#buddy-matching/#project-matching — they don't exist), `/signup` (full rewrite: editorial hero, "What you get" benefit cards, numbered join steps beside the framed `join_slack_1.png`, navy CTA band; primary CTA is now a real `` — dropped the JS `router.push`/`window.open` branch; `handleSignupClick` only fires the `CompleteRegistration` GA event), `/volunteer`, `/about/hackers`, `/about/process` (refined chrome; Mermaid flow + Gantt diagrams kept in calm card frames), `/praise` (refined chrome; `PraiseBoard` feed kept), `/hack/code-of-conduct` (full rewrite: editorial hero, core-values cards, numbered accessibility list, expected-vs-unacceptable two-col w/ +/× markers, navy CTA band), `/profile/[userid]` public (`PublicProfile`), and a full refined pass on the own `/profile` editor (`Profile.js` — editorial masthead, hairline sticky tab strip, scoped navy/terracotta MUI theme, `PanelHeader` on every tab, privacy legend on Basic Info, hairline rules in Volunteer History, `.ohx-card` Giveaway Entries; form logic untouched), `/myfeedback` (full rewrite: editorial hero, navy CircularProgress score ring w/ Fraunces number, hairline skill accordions w/ navy LinearProgress bars, `.ohx-card` entries + `.ohx-tag` chips) and `/feedback/[userid]` (`GiveFeedback`, `ssr:false`: hero + `Section`-framed form, navy-themed MUI Select/Radio/Slider/Checkbox, native `.ohx-btn` submit; also fixed a pre-existing hooks-order bug where `if (!user)` returned before `useEffect`), `/hack/[event_id]/manageteam` (full rewrite: `RefinedRoot`+`RefinedFonts`, editorial masthead, `.ohx-card` gating panels, calm `--surface-2` panels for auth/apply gates; split `error` → `teamsError`+`formError`; lazy Slack/nonprofit fetches gated by `activeStep`/`showNewTeamForm` + `slackFetchedRef`/`nonprofitFetchedRef`; `onTeamUpdated` callback propagated to `TeamStatusPanel` so DevPost/demo label updates without reload; `fetchMyTeams()` after submit + scroll to `#team-hub`; `noindex` meta; no PII logs; `active_days=365` for Slack fetch), and `/hack/[event_id]/team/[team_id]` (full rewrite: editorial masthead + hairline `.ohx-card` sections + a **deep-linkable sticky Table of Contents** — sections are `
` with hover `#` copy-link anchors (keyboard accessible via `&:focus-visible`), the TOC lists only present sections (`aria-current="location"`) and tracks the active one via `IntersectionObserver`; embedded `MentorTeamPanel`/`TeamCompletionChecklist` keep their own styling in anchored `headed={false}` wrappers). **Key invariants on this page:** (1) Status labels come from `TEAM_STATUS_OPTIONS.find()` via `statusLabel()` — never render raw enum strings like `NONPROFIT_SELECTED`; winning statuses get a 🏆 prefix from `getWinningStatus()`; `INACTIVE` skips the status tag since the dot already conveys it. (2) `SectionBlock` is **module-scope** (not inside the component) — defining it inside caused a remount storm on every IntersectionObserver `setActiveId` tick, wiping in-progress mentor note drafts and reloading the demo iframe; it receives `copiedId`/`onCopyLink` as props. (3) `parseLocalDate` from `src/lib/dateUtils.js` is used for all event window checks (`eventHasStarted`, `eventEnded`) so date-only strings aren't parsed as UTC midnight. (4) Membership check uses `useTeamMembership(eventId, teamId)` from `src/hooks/use-team-membership.js` — shared by the page and passed as `isOnTeam`/`membershipChecked` props to `TeamCompletionChecklist` (no duplicate fetch). Page uses result to show: "Manage your team" primary CTA for members, a combined nudge card for missing DevPost/demo, and a ghost "Want to join this team?" affordance for non-members when status is joinable. (5) Nonprofit: never render `selected_nonprofit_id` text — only show when `nonprofitData.name` is available; `getStaticProps` fetches `{ name, description }` into `nonprofitData`; a "Nonprofit partner" section card links to `/nonprofit/`. (6) `awards[]` rendered as `ohx-tag--accent` chips after the status tag. (7) All GitHub repos in `github_links[]` rendered (handles both string and `{link,name}` shapes). (8) `getStaticProps` rethrows network errors (ISR keeps the last good version) and returns `notFound` only on genuine 404. **Harmonized (not full rewrite):** `/hack` index (keeps its bespoke finder + `HackPageNav`; hero/CTAs/Support band recolored via inline `RX` tokens). The upcoming/current event cards were refined too: `HackathonList` full-mode heading → Fraunces eyebrow; `EventFeature` full card rewritten to a refined hairline card (eyebrow date + quiet type tag, Fraunces title, navy donation rings, navy-primary/ghost event-link buttons, "View event →"; fixed the old nested-anchor bug; var-fallback colors since `/hack` isn't a `RefinedRoot`). `ImpactMetrics` (shared with the archive) → warm `--surface-2` tiles with Fraunces navy numbers + "Impact at a glance" overline (was rainbow `color`-prop numbers). `/hack/[event_id]` got a **masthead-led refined pass**: body wrapped in `` (it's now the `
`; all section IDs + `TableOfContents` + `FloatingNavigation` preserved), `HackathonHeader` rebuilt into a scope-independent editorial masthead (inline styles w/ `var(--x, fallback)` so `/agenda` + `/census` also benefit), refined "Build a team" buttons + recap teaser. Phase-2 refined the **event-only** sub-components: `NonprofitList` (calm clickable cards + navy view-toggle), `EventConstraints`, `DonationProgress` (navy rings), `EventCountdown` (navy countdown card replacing the gradient + flat timeline), `TeamList`/`TeamCard` (flat hairline cards, Fraunces team names, refined "Team members" label + navy Join button — `TeamList` is event-page-only despite earlier substring matches in admin/event-teams; **profile avatars load lazily per-card via IntersectionObserver with `rootMargin: "200px"`, tracked by `fetchedTeamProfilesRef` Set** — do NOT restore the eager `fetchTeamMemberProfiles(teams)` call on mount that fired N parallel requests for all 30+ cards at once). **Backend C7 enrichment:** `get_single_hackathon_event` (`services/hackathons_service.py`) now calls `_enrich_teams_users_batch` after converting team DocumentReferences — one `db.get_all` across all members of all teams, deduped, so the frontend receives `users[]` already as `{id, user_id, name, nickname, profile_image}` objects. `HackathonResults.js`/`TeamList.js` handle both the old id-string and new object shapes (backwards-compatible). The list endpoint `get_hackathon_list` is NOT enriched — only the single-event getter., and `HackathonLeaderboard` (flat cards, warm stat tiles w/ terracotta icons + Fraunces navy numbers, squared navy Org button). **Phase 3 (done) finished the event page**: `EventLinks` (6 rainbow app buttons → one calm uniform card set + calm social-proof strip), `InteractiveFAQ` (flat hairline accordions), `HackathonResults` (calm `--surface-2` frame, Fraunces navy stat numbers; gold/silver/bronze winner medals kept), `VolunteerList` (`PersonCard` → flat hairline). The **shared** ones (`InteractiveFAQ`, `HackathonResults`) use **inline CSS-var fallbacks** (`var(--ink,#16181D)`) so they render refined on their non-`RefinedRoot` routes too (verified on `/hack/[event_id]/results` + `OnboardingFAQ`) — no per-route gating. A final chrome pass refined `TableOfContents` (navy active pill, was magenta), `FloatingNavigation` (navy FAB), `VolunteerList` (navy "Learn more" + navy availability chips; in-person stays green), and `MentorAvailability` (navy banner/Slack button). The event page is now fully refined end-to-end. Also refined: `/hack/request` (host-an-event page + `HackathonRequestForm`) and `/office-hours`. **`/hack/request` is student-first** — org-type selectable cards default to **university** (was corporate); the **$5k budget minimum was removed** (slider min `$0`, never blocks submit, reframed as optional funding); the donation-% ask is **corporate-only** (forced to 0 at submit otherwise); phone is optional; rainbow Papers → calm `--surface-2` cards; everything navy/terracotta via a scoped MUI `ThemeProvider` (`createTheme(base, …)`). **`formData` keys are preserved** — `HackathonRequestDetailDialog`, the edit page `/hack/request/[request_id]`, and the backend all depend on them. **Scroll gotcha:** step nav now `scrollIntoView`s a `formTopRef` on the form's Paper (`scrollMarginTop:88`), NOT `window.scrollTo(0,0)` (which overshot to page-top now that the form sits below the hero). `/office-hours` now renders its JSON-LD, uses the idempotent `initFacebookPixel` (was a raw `ReactPixel.init` — CWV rule), and dropped the artificial 1s loading delay + dead ICS code. Also refined: `/nonprofit/[nonprofit_id]` (`NonProfit.js` — editorial masthead, meta row as `.ohx-tag`/`.ohx-link`s, projects list, canonical www fix) and `/project/[project_id]` (`Project.js` — `RefinedRoot` chrome, soft breadcrumb, canonical). Both share `ProblemStatement.js` which was fully restyled (purple gradient hero → calm `--surface-2` band; gradient chips → `.ohx-tag`; gradient metric tiles → Fraunces navy tiles; gradient section headers → hairline `.ohx-card`; gradient CTAs → `.ohx-btn`). **Key invariant:** `ProblemStatement` is ONLY ever inside a `RefinedRoot`, so it may use scoped `.ohx-*` classes directly (no inline `var(--x, fallback)` needed). Pass `headingLevel="h2"` from the nonprofit page (N cards, nonprofit name is the `

`) and `headingLevel="h1"` from the project page (project title is the `

`). The "Want to help?" toggle is hidden when `status === "production"` (live projects don't need volunteers) but stays visible for users already helping so they can toggle off; `maintenance` status keeps the toggle. **"Code & Tasks" (July 2026 discoverability fix):** it is an ALWAYS-VISIBLE section (`id="code-and-tasks-"` — unique per problem statement because nonprofit pages render N cards; `scrollMarginTop: 96`) directly after the Project Description — never move it back into the collapsed accordion trio (burying it was the bug; only References/Events stay accordions). Repo list = `problem_statement.github` (handles legacy string shape) MERGED with repos from hackathon teams in the already-fetched event data (`team.problem_statements` includes the ps id; `selected_nonprofit_id`-vs-`resolvedNonprofits` fallback applies ONLY to teams with no `problem_statements` — avoids claiming a sibling project's repos on multi-project nonprofits), deduped by `normalizeRepoLink`; team repos render under a "from hackathon teams" label with "Built by {team} ({event})" attribution, and a duplicate just enriches the project-level card. Live issue data (counts + top-5 open titles per repo) is fetched through the public backend proxy `GET /api/github/issues?org&repo&state=all` ONLY once the section scrolls near (fire-once IntersectionObserver, `rootMargin: 200px`) — batched Promise.all + `issuesRequestedRef` dedupe + ONE setState (never per-repo), errors swallowed (static links still render); backend caches this route 10 min. `RepoCard` + repo-link helpers are **module-scope** (SectionBlock remount lesson). When no repos exist at all, render the "Where's the code?" `--surface-2` card (event-page links + `slack_channel` button) — never hide the section silently. Header top row shows an `#code-and-tasks` anchor `.ohx-tag` when repos exist. Backend fix-forward: `approve_team` (`api/teams/teams_service.py::_link_repo_to_problem_statements`) now also appends the created repo to the linked problem statement(s)' `github` array (team `problem_statements` refs preferred; single-PS-nonprofit fallback; ambiguous → skip + log) and clears `get_single_problem_statement_old`'s cache. **Events & Teams section:** `src/components/Events/Events.js` was rewritten as refined event cards — title is `event.title` (NOT `{location} {type}` — that rendered a street address as the heading), Past/Upcoming `.ohx-tag`, compact date range via `parseLocalDate`, location + description as muted meta, constraint `.ohx-tag`s, ghost Event-page/DevPost buttons, plus a **read-only** teams list (name, member first-names, link to `/hack//team/`) fed by `teamsByEvent` from the same ProblemStatement memo that derives team repos. The old interactive join/leave path was dead (its `teams` state was never populated) and was removed from ProblemStatement along with the per-user `userDetails` fetch — `event-teams.js` and `event-team.js` are now orphaned; don't re-add join/leave here (team joining lives on the event page / manageteam). **Reference Documents:** `ReferenceItem.js` restyled from outlined MUI buttons to hairline row-cards (`ohx-card--hover` `` rows: navy kind icon + sentence-case name + kind `.ohx-tag` [GitHub/Slides/Document/Video/Link] + open-in-new indicator; `docs.google.com/presentation` now classified as Slides); video references keep their `VideoDisplay` embed. Only ProblemStatement consumes it, so `.ohx-*` classes are safe there. `styles/nonprofit/styles.js` is now orphaned — leave in place. See `docs/refined-design-system.md`. Pattern for new pages: keep `Head`/`getStaticProps`/schema verbatim, add ``, wrap body in ``, rebuild sections with `.ohx-*` classes, one `

`, preserve GA. @@ -19,7 +19,11 @@ The homepage and projects page were reimagined away from the old busy/multi-grad ### Global NavBar (part of the refined look — applies site-wide) -`src/components/Navbar/Navbar.js` + `styles.js` were restyled from the solid-blue MUI AppBar to a light, frosted "civic editorial" bar so it's cohesive with the refined pages everywhere: `backgroundColor: rgba(251,250,246,0.82)` + `backdropFilter: blur` + `borderBottom: 1px solid #E7E1D4`, `elevation={0}`, ink (`#16181D`) links, hover → brand navy `#1B3A6B`. Load button is a squared navy button (no more 1.5rem rounded pill). Links are sentence-case (`textTransform: none`) — keep the page-link `