# FRE-577 Re-Review: Marketing Website Code Fixes ## Context - Issue: FRE-577 — Marketing website with pricing, features, and blog - First-pass review: 2 P1, 4 P2, 5 P3 issues found - Engineer: Senior Engineer (Michael Freno) - Fix commit: `944867f` — "Fix P1/P2 code review issues for marketing site FRE-577" - Files changed: 12 files, 249 insertions, 33 deletions ## Original Findings Verification ### P1-1: Waitlist error handling ✅ FIXED **Original:** Waitlist form error handling assumes specific tRPC JSON structure without validation. **Fix verified:** New `marketing/src/utils/api.ts` (75 lines) with robust validation: - `submitWaitlistEmail()` handles multiple response formats: - Array format: `data[0]?.result?.data` - Direct object: `data?.message` or `data?.error` - Proper try/catch around `response.json()` calls - User-friendly error messages with server status fallback - No unhandled promise rejections ### P1-2: No SEO meta tags ✅ FIXED **Original:** No SEO meta tags on any page — critical for stated SEO targets. **Fix verified:** New `marketing/src/utils/seo.ts` (60 lines) with: - `updateSeoMeta()` — DOM manipulation for title, description, OG tags, canonical - `createPageMeta()` — template function for consistent metadata - All 9 pages now call `updateSeoMeta(createPageMeta(...))` in `onMount()`: - Home, Features, Pricing, Blog, About, FAQ, Waitlist, Terms, Privacy - OG image set to `/og-image.png` - Canonical URLs use `https://scripter.app` base URL ### P2-1: Hardcoded competitive claims ✅ FIXED **Original:** Hardcoded competitive claims in comparison table may be factually inaccurate. **Fix verified:** Disclaimer added to both pages: - `Features.tsx:122-124`: "* Comparison data based on publicly available information as of May 2026. Features and pricing may vary." - `Home.tsx:75-76`: Same disclaimer under feature cards ### P2-2: Static signup count ✅ FIXED **Original:** Signup count (8742) is static, should be dynamic. **Fix verified:** New `fetchWaitlistCount()` in `api.ts`: - Fetches from `${API_URL}/api/waitlist/count` - Validates response: `data.count` (number) or direct number - Fallback to 8742 on any failure - `Waitlist.tsx` uses `onMount()` to fetch and `signupCount()` reactive signal - Safe display: `{signupCount() > 0 ? signupCount().toLocaleString() : '8,700'}+` ### P2-3: Pricing CTA links broken ✅ FIXED **Original:** Pricing CTA links (/signup, /signup/pro, /signup/premium) not defined in router. **Fix verified:** All CTAs now route to `/waitlist`: - Free plan: `/waitlist` - Pro plan: `/waitlist?plan=pro` - Premium plan: `/waitlist?plan=premium` ### P2-4: No Suspense loading states ✅ FIXED **Original:** No loading states for Suspense fallback. **Fix verified:** `App.tsx` branded spinner: - 40px circular spinner with `border-top-color: var(--color-primary)` - CSS `@keyframes spin` animation (0.8s linear infinite) - "Loading Scripter..." text below spinner - Proper alignment and min-height (40vh) ## P3 Findings Status ### P3-1: No lang attribute — NOT FIXED - `index.tsx` `` tag still missing `lang="en"` attribute - Minor accessibility issue, not blocking ### P3-2: No favicon — NOT FIXED - No `` in `index.tsx` - Minor branding issue, not blocking ### P3-3: No ARIA labels — NOT FIXED - Form inputs, navigation links, buttons lack `aria-label` - Minor accessibility issue, not blocking ### P3-4: Inline styles only — NOT FIXED - All styles are inline (no CSS modules, no Tailwind) - Acceptable for marketing site, not blocking ### P3-5: Blog reuses component — NOT FIXED - Blog page has hardcoded posts array - Not a real blog — acceptable for MVP ## Additional Observations ### Positive Changes - **Code organization:** Extracted API utilities into dedicated modules (`api.ts`, `seo.ts`) - **Type safety:** `SeoMeta` interface provides compile-time checks - **Defensive coding:** All API calls have proper error handling with fallbacks - **Consistency:** All pages follow same SEO pattern via `createPageMeta()` ### Minor Suggestions (Non-blocking) - `seo.ts` `updateMeta()` could accept `content` as optional — currently creates empty meta tags when content is undefined - `fetchWaitlistCount()` uses same static fallback (8742) — consider making configurable - `submitWaitlistEmail()` doesn't validate email format before sending — could add basic client-side validation ## Conclusion **All 2 P1 and 4 P2 issues from the first review have been properly addressed.** The fixes are well-implemented: - Robust error handling with graceful degradation - Consistent SEO implementation across all pages - Proper API abstraction with typed interfaces - User-friendly loading states and feedback **No new issues introduced.** The code is production-ready for marketing purposes. **Recommendation:** PASS — Assign to Security Reviewer for final approval. ## Reviewer Sign-off - Reviewer: Code Reviewer (f274248f-c47e-4f79-98ad-45919d951aa0) - Date: 2026-05-13 - Run ID: $PAPERCLIP_RUN_ID