All Templates
Day 7 of 77-Day AI Build Challenge

Pre-Launch Review Checklist

Review your SaaS app across 7 critical areas before launching. Every item has a severity level and specific checks.

Download .md
1

Free Tier Experience

The free tier is your acquisition funnel. If it sucks, nobody upgrades.

SeverityCheckStatus
CriticalFree users can sign up and access the core feature without payment
CriticalFree tier provides real standalone value (not a crippled product)
CriticalFree tier limits are clearly communicated before the user hits them
WarningUpgrade prompts appear at natural friction points, not random popups
WarningFree user can understand the value of Pro from the upgrade prompt
InfoFree users see social proof or testimonials on upgrade page

Free tier smoke test:

  • Create a new account (don't use your admin account)
  • Complete the core workflow as a free user
  • Hit at least one free tier limit and verify the upgrade prompt
  • Verify that free users cannot access Pro-only features via URL manipulation
2

Authentication & Authorization

Every unprotected route is a potential data leak.

SeverityCheckStatus
CriticalEvery query and mutation validates authentication via ctx.auth.getUserIdentity() or equivalent
CriticalEvery resource access verifies ownership: resource.userId === currentUser._id
CriticalUnauthenticated users cannot access any /dashboard, /project, or /settings route
CriticalUsers cannot access other users' data by modifying IDs in URLs or API calls
WarningSign-up flow works end-to-end (email + OAuth providers)
WarningSign-in flow works end-to-end
WarningPassword reset / account recovery flow works
WarningSession expiry redirects to sign-in gracefully (no error page)
InfoUser can delete their account and all associated data

Auth smoke test:

  • Sign up with email, verify you can access dashboard
  • Sign up with OAuth (Google, GitHub), verify user record created
  • Open an incognito window, try to access /dashboard directly — should redirect to sign-in
  • Copy a project URL, open in incognito, sign in as a different user — should show "not found" or "unauthorized"
3

Payments & Billing

Payment bugs lose you money and trust. Test thoroughly.

SeverityCheckStatus
CriticalStripe is in LIVE mode (not test mode) for production
CriticalCheckout flow completes successfully with a real payment method
CriticalWebhook handler verifies Stripe signature before processing
Criticalcheckout.session.completed webhook updates user plan to "pro"
Criticalcustomer.subscription.deleted webhook reverts user plan to "free"
Warninginvoice.paid webhook handles recurring payments correctly
Warninginvoice.payment_failed webhook handles failed payments (notify user)
WarningCustomer portal link works — user can manage/cancel subscription
WarningDuplicate webhook events are handled idempotently (no double-upgrades)
InfoPricing page clearly shows what's included in each plan
InfoReceipt emails are sent (Stripe handles this by default)

Payment smoke test:

  • Complete a real checkout (use Stripe's $0.50 test product if needed)
  • Verify user plan updated in database after successful payment
  • Cancel subscription via Stripe dashboard, verify plan reverted
  • Check Stripe dashboard for webhook delivery — any failures?
4

Security

One security bug can kill your product. Check every item.

SeverityCheckStatus
CriticalAll API keys and secrets are in environment variables, not in code
Critical.env / .env.local is in .gitignore (not committed to repo)
CriticalStripe webhook signatures verified via stripe.webhooks.constructEvent()
CriticalClerk/auth webhook signatures verified via svix or equivalent
CriticalEvery mutation validates input types with framework validators (Convex v, Zod, etc.)
CriticalNo raw user input passed directly into AI system prompts
WarningRate limiting on AI/expensive operations: max ___ requests/minute/user
WarningRate limiting on auth endpoints: max ___ attempts/minute/IP
WarningCORS configured correctly (only allows your domain)
WarningNo sensitive data logged to console or error tracking
WarningFile uploads (if any) validated for type and size
InfoContent Security Policy headers set
InfoHTTPS enforced (HTTP redirects to HTTPS)

Security smoke test:

  • Search codebase for hardcoded API keys: grep -r "sk_" --include="*.ts"
  • Verify .env is not in git history: git log --all --diff-filter=A -- .env
  • Open browser dev tools → Network tab → verify no API keys in client-side requests
  • Try submitting a form with malicious input (XSS payload, SQL injection) — verify it's sanitized
5

SEO & Performance

If search engines can't find you, you don't exist.

SeverityCheckStatus
WarningEvery page has a unique </code> tag</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Every page has a unique <code class="rounded bg-muted px-1.5 py-0.5 text-sm"><meta name="description"></code></td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Open Graph tags set for social sharing (<code class="rounded bg-muted px-1.5 py-0.5 text-sm">og:title</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-sm">og:description</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-sm">og:image</code>)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Sitemap.xml exists, is valid, and lists all public pages</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Robots.txt exists and allows search engine crawling of public pages</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Page load time under 3 seconds on mobile (test with PageSpeed Insights)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">All pages are mobile responsive — test at 375px width</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">Images have descriptive <code class="rounded bg-muted px-1.5 py-0.5 text-sm">alt</code> text</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">Structured data (JSON-LD) added for key pages</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">Canonical URLs set to prevent duplicate content</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">404 page returns proper 404 status code (not 200)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr></tbody></table></div><h3>SEO smoke test:</h3><ul class="not-prose my-4 space-y-2"><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Google "site:yourdomain.com" — are your pages indexed?</span></li><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Paste your URL into Twitter/LinkedIn — does the preview card look right?</span></li><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Run Google PageSpeed Insights on your landing page — score above 80?</span></li><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Check sitemap.xml in browser — does it list all public pages?</span></li></ul></div></div></div><div id="6-error-handling-ux" class="mb-10 scroll-mt-24"><div class="mb-6 rounded-xl border p-6 border-border/50 bg-card/30"><div class="mb-4 flex items-center justify-between gap-4"><div class="flex items-center gap-3"><span class="flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary">6</span><h2 class="text-xl font-bold tracking-tight">Error Handling & UX</h2></div><button class="inline-flex items-center gap-1.5 rounded-md border border-border/50 px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:border-border hover:bg-muted hover:text-foreground" title="Copy this section"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy size-3" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>Copy</button></div><div class="prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground"><p><em>Users who hit errors and get no feedback will leave and never come back.</em></p><div class="not-prose my-4 overflow-x-auto rounded-lg border border-border/50"><table class="w-full text-sm"><thead><tr class="border-b border-border/50 bg-muted/30"><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Severity</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Check</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Status</th></tr></thead><tbody><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Critical</td><td class="px-4 py-2.5 text-muted-foreground">No unhandled promise rejections or uncaught errors in production</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Loading skeletons shown during all data fetching (never a blank screen)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Error messages are user-friendly (not raw stack traces or API errors)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Empty states shown when no data exists (not blank white space)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Form validation shows clear inline error messages</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Toast notifications confirm async actions (create, update, delete)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">404 page exists and matches the app's design</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">Undo/confirmation for destructive actions (delete project, cancel subscription)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">Keyboard navigation works for core workflows</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">Error monitoring service configured (Sentry, LogRocket, etc.)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr></tbody></table></div><h3>UX smoke test:</h3><ul class="not-prose my-4 space-y-2"><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Disable JavaScript — does the page show a reasonable fallback?</span></li><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Throttle network to "Slow 3G" in dev tools — do loading states appear?</span></li><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Submit every form with empty fields — do validation errors appear?</span></li><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Open the app on your phone — does everything fit?</span></li></ul></div></div></div><div id="7-code-quality" class="mb-10 scroll-mt-24"><div class="mb-6 rounded-xl border p-6 border-border/50 bg-card/30"><div class="mb-4 flex items-center justify-between gap-4"><div class="flex items-center gap-3"><span class="flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary">7</span><h2 class="text-xl font-bold tracking-tight">Code Quality</h2></div><button class="inline-flex items-center gap-1.5 rounded-md border border-border/50 px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:border-border hover:bg-muted hover:text-foreground" title="Copy this section"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy size-3" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>Copy</button></div><div class="prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground"><p><em>Technical debt you ship on day 1 compounds forever.</em></p><div class="not-prose my-4 overflow-x-auto rounded-lg border border-border/50"><table class="w-full text-sm"><thead><tr class="border-b border-border/50 bg-muted/30"><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Severity</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Check</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Status</th></tr></thead><tbody><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Critical</td><td class="px-4 py-2.5 text-muted-foreground"><code class="rounded bg-muted px-1.5 py-0.5 text-sm">npm run build</code> (or equivalent) passes with zero errors</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground"><code class="rounded bg-muted px-1.5 py-0.5 text-sm">npm run lint</code> passes with zero warnings</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">No <code class="rounded bg-muted px-1.5 py-0.5 text-sm">console.log</code> statements in production code</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">No hardcoded URLs — all URLs use environment variables or constants</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">No <code class="rounded bg-muted px-1.5 py-0.5 text-sm">any</code> types in TypeScript (use <code class="rounded bg-muted px-1.5 py-0.5 text-sm">unknown</code> and narrow)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">No <code class="rounded bg-muted px-1.5 py-0.5 text-sm">@ts-ignore</code> or <code class="rounded bg-muted px-1.5 py-0.5 text-sm">@ts-expect-error</code> comments</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Environment variables documented in <code class="rounded bg-muted px-1.5 py-0.5 text-sm">.env.example</code> (without values)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">No unused imports or variables</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">No commented-out code blocks</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">No TODO/FIXME/HACK comments left in production code</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr></tbody></table></div><h3>Code quality smoke test:</h3><ul class="not-prose my-4 space-y-2"><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Run <code class="rounded bg-muted px-1.5 py-0.5 text-sm">npm run build && npm run lint</code> — both pass clean?</span></li><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Search for console.log: <code class="rounded bg-muted px-1.5 py-0.5 text-sm">grep -r "console.log" --include="<em>.ts" --include="</em>.tsx" src/</code></span></li><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Search for ts-ignore: <code class="rounded bg-muted px-1.5 py-0.5 text-sm">grep -r "ts-ignore\|ts-expect-error" --include="<em>.ts" --include="</em>.tsx"</code></span></li><li class="flex items-start gap-3"><span class="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground"></span><span class="text-sm text-muted-foreground">Search for TODOs: <code class="rounded bg-muted px-1.5 py-0.5 text-sm">grep -r "TODO\|FIXME\|HACK" --include="<em>.ts" --include="</em>.tsx"</code></span></li></ul></div></div></div><div id="pre-deploy-checklist" class="mb-10 scroll-mt-24"><div class="mb-6 rounded-xl border p-6 border-destructive/20 bg-destructive/5"><div class="mb-4 flex items-center justify-between gap-4"><div class="flex items-center gap-3"><span class="flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-destructive/20 text-destructive">8</span><h2 class="text-xl font-bold tracking-tight"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert mr-2 inline-block size-5 text-destructive" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path></svg>Pre-Deploy Checklist</h2></div><button class="inline-flex items-center gap-1.5 rounded-md border border-border/50 px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:border-border hover:bg-muted hover:text-foreground" title="Copy this section"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy size-3" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>Copy</button></div><div class="prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground"><p><em>Final checks before the deploy button.</em></p><div class="not-prose my-4 overflow-x-auto rounded-lg border border-border/50"><table class="w-full text-sm"><thead><tr class="border-b border-border/50 bg-muted/30"><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Severity</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Check</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Status</th></tr></thead><tbody><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Critical</td><td class="px-4 py-2.5 text-muted-foreground">All environment variables set in production (not just local)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Critical</td><td class="px-4 py-2.5 text-muted-foreground">Domain DNS configured and propagated</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Critical</td><td class="px-4 py-2.5 text-muted-foreground">SSL certificate active (HTTPS working)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Critical</td><td class="px-4 py-2.5 text-muted-foreground">Stripe in live mode with live API keys</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Critical</td><td class="px-4 py-2.5 text-muted-foreground">Auth provider configured for production domain (not localhost)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Webhook URLs point to production domain (not localhost or ngrok)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Error monitoring configured for production</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Warning</td><td class="px-4 py-2.5 text-muted-foreground">Database backups configured (if self-hosted)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">Analytics/tracking installed (Plausible, PostHog, etc.)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground">Info</td><td class="px-4 py-2.5 text-muted-foreground">Custom error pages deployed (404, 500)</td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr></tbody></table></div></div></div></div><div id="action-items" class="mb-10 scroll-mt-24"><div class="mb-6 rounded-xl border p-6 border-border/50 bg-card/30"><div class="mb-4 flex items-center justify-between gap-4"><div class="flex items-center gap-3"><span class="flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary">9</span><h2 class="text-xl font-bold tracking-tight">Action Items</h2></div><button class="inline-flex items-center gap-1.5 rounded-md border border-border/50 px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:border-border hover:bg-muted hover:text-foreground" title="Copy this section"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy size-3" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>Copy</button></div><div class="prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground"><p><em>Track every failed item here. Fix all Critical and Warning items before launch.</em></p><h3>Critical Fixes (must fix before launch)</h3><div class="not-prose my-4 overflow-x-auto rounded-lg border border-border/50"><table class="w-full text-sm"><thead><tr class="border-b border-border/50 bg-muted/30"><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Item</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Section</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Description</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Status</th></tr></thead><tbody><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr></tbody></table></div><h3>Warning Fixes (should fix before launch)</h3><div class="not-prose my-4 overflow-x-auto rounded-lg border border-border/50"><table class="w-full text-sm"><thead><tr class="border-b border-border/50 bg-muted/30"><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Item</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Section</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Description</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Status</th></tr></thead><tbody><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr></tbody></table></div><h3>Info Fixes (fix after launch)</h3><div class="not-prose my-4 overflow-x-auto rounded-lg border border-border/50"><table class="w-full text-sm"><thead><tr class="border-b border-border/50 bg-muted/30"><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Item</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Section</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Description</th><th class="px-4 py-2.5 text-left text-xs font-semibold text-foreground">Status</th></tr></thead><tbody><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr><tr class="border-b border-border/30 last:border-0"><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground"></td><td class="px-4 py-2.5 text-muted-foreground">⬜</td></tr></tbody></table></div><p><em>Want an AI to run these checks and generate a fix list? <a href="https://buildastartup.ai" class="text-primary underline underline-offset-4 hover:text-primary/80">Try Build a Startup →</a></em></p></div></div></div></article><aside class="hidden xl:block"><nav class="sticky top-20 hidden max-h-[calc(100vh-6rem)] overflow-y-auto xl:block"><p class="mb-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">Sections</p><ul class="space-y-1 border-l border-border/50"><li><a href="#1-free-tier-experience" class="block border-l-2 py-1 pl-4 text-xs transition-colors border-transparent text-muted-foreground hover:text-foreground">Free Tier Experience</a></li><li><a href="#2-authentication-authorization" class="block border-l-2 py-1 pl-4 text-xs transition-colors border-transparent text-muted-foreground hover:text-foreground">Authentication & Authorization</a></li><li><a href="#3-payments-billing" class="block border-l-2 py-1 pl-4 text-xs transition-colors border-transparent text-muted-foreground hover:text-foreground">Payments & Billing</a></li><li><a href="#4-security" class="block border-l-2 py-1 pl-4 text-xs transition-colors border-transparent text-muted-foreground hover:text-foreground">Security</a></li><li><a href="#5-seo-performance" class="block border-l-2 py-1 pl-4 text-xs transition-colors border-transparent text-muted-foreground hover:text-foreground">SEO & Performance</a></li><li><a href="#6-error-handling-ux" class="block border-l-2 py-1 pl-4 text-xs transition-colors border-transparent text-muted-foreground hover:text-foreground">Error Handling & UX</a></li><li><a href="#7-code-quality" class="block border-l-2 py-1 pl-4 text-xs transition-colors border-transparent text-muted-foreground hover:text-foreground">Code Quality</a></li><li><a href="#pre-deploy-checklist" class="block border-l-2 py-1 pl-4 text-xs transition-colors border-transparent text-muted-foreground hover:text-foreground">Pre-Deploy Checklist</a></li><li><a href="#action-items" class="block border-l-2 py-1 pl-4 text-xs transition-colors border-transparent text-muted-foreground hover:text-foreground">Action Items</a></li></ul></nav></aside></div><div class="mt-8 rounded-2xl border border-primary/20 bg-gradient-to-br from-primary/10 via-primary/5 to-transparent p-10 text-center md:p-14"><div class="mx-auto max-w-lg"><div class="mb-4 inline-flex items-center gap-2 rounded-full border border-primary/20 bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rocket size-3.5" aria-hidden="true"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"></path><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"></path><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"></path><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"></path></svg>Skip the manual work</div><h2 class="mb-3 text-2xl font-bold md:text-3xl">Want the AI to fill this out for you?</h2><p class="mb-8 text-muted-foreground">Build a Startup walks you through every step with a conversational AI that asks the right questions and pushes back on bad decisions.</p><a class="inline-flex items-center gap-2 rounded-lg bg-primary px-8 py-3.5 text-sm font-semibold text-primary-foreground transition-colors hover:bg-primary/90" href="/dashboard"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rocket size-4" aria-hidden="true"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"></path><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"></path><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"></path><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"></path></svg>Try Build a Startup — Free</a></div></div><div class="mt-12 grid gap-4 border-t border-border/50 pt-8 sm:grid-cols-2"><a class="group flex items-center gap-3 rounded-xl border border-border/50 p-4 transition-colors hover:border-border hover:bg-card/50" href="/templates/build-plan"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-left size-4 text-muted-foreground transition-transform group-hover:-translate-x-0.5" aria-hidden="true"><path d="m12 19-7-7 7-7"></path><path d="M19 12H5"></path></svg><div><p class="text-xs text-muted-foreground">Day <!-- -->6</p><p class="text-sm font-medium">Build Plan Template</p></div></a><div></div></div></div><!--$--><!--/$--></main><footer class="border-t border-border/50 bg-muted/20"><div class="mx-auto flex w-full max-w-6xl flex-col items-center gap-4 px-6 py-10 md:flex-row md:justify-between"><div class="flex items-center gap-2 text-sm text-muted-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rocket size-4" aria-hidden="true"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"></path><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"></path><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"></path><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"></path></svg><span>Build a Startup</span></div><nav class="flex gap-6 text-sm text-muted-foreground"><a class="transition-colors hover:text-foreground" href="/templates">Templates</a><a class="transition-colors hover:text-foreground" href="/pricing">Pricing</a></nav><p class="text-xs text-muted-foreground">© <!-- -->2026<!-- --> <!-- -->Build a Startup<!-- -->. All rights reserved.</p></div></footer></div><section aria-label="Notifications alt+T" tabindex="-1" aria-live="polite" aria-relevant="additions text" aria-atomic="false"></section><script src="/_next/static/chunks/616ba6460c0298bf.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[30435,[\"/_next/static/chunks/96ab537394c4677f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b3232d64c2c3de43.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b187b5c0f682e0e9.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/71e8f7ee0623bd3f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/f5084ae60443579f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b0c49ac9f3ca70ba.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/0e4d7ef867a4338b.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/1d1313ba2a492a97.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"ConvexClientProvider\"]\n3:I[40402,[\"/_next/static/chunks/96ab537394c4677f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b3232d64c2c3de43.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b187b5c0f682e0e9.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/71e8f7ee0623bd3f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/f5084ae60443579f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b0c49ac9f3ca70ba.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/0e4d7ef867a4338b.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/1d1313ba2a492a97.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"ThemeProvider\"]\n4:I[28231,[\"/_next/static/chunks/96ab537394c4677f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b3232d64c2c3de43.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b187b5c0f682e0e9.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/71e8f7ee0623bd3f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/f5084ae60443579f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b0c49ac9f3ca70ba.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/0e4d7ef867a4338b.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/1d1313ba2a492a97.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"TooltipProvider\"]\n5:I[39756,[\"/_next/static/chunks/208d109d56c94dec.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/9a13402b4e5995b7.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"default\"]\n6:I[37457,[\"/_next/static/chunks/208d109d56c94dec.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/9a13402b4e5995b7.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"default\"]\n7:I[46696,[\"/_next/static/chunks/96ab537394c4677f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b3232d64c2c3de43.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b187b5c0f682e0e9.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/71e8f7ee0623bd3f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/f5084ae60443579f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b0c49ac9f3ca70ba.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/0e4d7ef867a4338b.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/1d1313ba2a492a97.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"Toaster\"]\n8:I[22016,[\"/_next/static/chunks/96ab537394c4677f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b3232d64c2c3de43.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b187b5c0f682e0e9.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/71e8f7ee0623bd3f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/f5084ae60443579f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b0c49ac9f3ca70ba.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/0e4d7ef867a4338b.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/1d1313ba2a492a97.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/ed415703293dd1f1.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/c44be35721914ebf.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"\"]\n14:I[68027,[],\"default\"]\n:HL[\"/_next/static/chunks/2473c16c0c2f6b5f.css?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"style\"]\n:HL[\"/_next/static/chunks/1e146c72213aa834.css?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"style\"]\n:"])</script><script>self.__next_f.push([1,"HL[\"/_next/static/media/797e433ab948586e-s.p.dbea232f.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"lyV_Z3iz4-mrNufnVgvEo\",\"c\":[\"\",\"templates\",\"review-checklist\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"(marketing)\",{\"children\":[\"templates\",{\"children\":[[\"slug\",\"review-checklist\",\"d\"],{\"children\":[\"__PAGE__\",{}]}]}]}]},\"$undefined\",\"$undefined\",true],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/2473c16c0c2f6b5f.css?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/1e146c72213aa834.css?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/96ab537394c4677f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/b3232d64c2c3de43.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-2\",{\"src\":\"/_next/static/chunks/b187b5c0f682e0e9.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-3\",{\"src\":\"/_next/static/chunks/71e8f7ee0623bd3f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-4\",{\"src\":\"/_next/static/chunks/f5084ae60443579f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-5\",{\"src\":\"/_next/static/chunks/b0c49ac9f3ca70ba.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-6\",{\"src\":\"/_next/static/chunks/0e4d7ef867a4338b.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-7\",{\"src\":\"/_next/static/chunks/1d1313ba2a492a97.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"dark\",\"suppressHydrationWarning\":true,\"children\":[\"$\",\"body\",null,{\"className\":\"geist_a71539c9-module__T19VSG__variable geist_mono_8d43a2aa-module__8Li5zG__variable antialiased\",\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}],[\"$\",\"$L7\",null,{\"theme\":\"dark\",\"position\":\"bottom-right\",\"toastOptions\":{\"className\":\"border-border bg-background text-foreground\"}}]]}]}]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/ed415703293dd1f1.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"div\",null,{\"className\":\"flex min-h-screen flex-col bg-background\",\"children\":[[\"$\",\"header\",null,{\"className\":\"sticky top-0 z-50 border-b border-border/50 bg-background/80 backdrop-blur-lg\",\"children\":[\"$\",\"div\",null,{\"className\":\"mx-auto flex h-16 w-full max-w-6xl items-center justify-between px-6\",\"children\":[[\"$\",\"$L8\",null,{\"href\":\"/\",\"className\":\"flex items-center gap-2.5 font-bold text-lg\",\"children\":[[\"$\",\"svg\",null,{\"ref\":\"$undefined\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"width\":24,\"height\":24,\"viewBox\":\"0 0 24 24\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":2,\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"className\":\"lucide lucide-rocket size-5\",\"aria-hidden\":\"true\",\"children\":[\"$L9\",\"$La\",\"$Lb\",\"$Lc\",\"$undefined\"]}],\"Build a Startup\"]}],\"$Ld\"]}]}],\"$Le\",\"$Lf\"]}]]}],{\"children\":[\"$L10\",{\"children\":[\"$L11\",{\"children\":[\"$L12\",{},null,false,false]},null,false,false]},null,false,false]},null,false,false]},null,false,false],\"$L13\",false]],\"m\":\"$undefined\",\"G\":[\"$14\",[]],\"S\":true}\n"])</script><script>self.__next_f.push([1,"16:I[97367,[\"/_next/static/chunks/208d109d56c94dec.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/9a13402b4e5995b7.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"OutletBoundary\"]\n17:\"$Sreact.suspense\"\n19:I[97367,[\"/_next/static/chunks/208d109d56c94dec.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/9a13402b4e5995b7.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"ViewportBoundary\"]\n1b:I[97367,[\"/_next/static/chunks/208d109d56c94dec.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/9a13402b4e5995b7.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"MetadataBoundary\"]\n9:[\"$\",\"path\",\"m3kijz\",{\"d\":\"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z\"}]\na:[\"$\",\"path\",\"1fmvmk\",{\"d\":\"m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z\"}]\nb:[\"$\",\"path\",\"1f8sc4\",{\"d\":\"M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0\"}]\nc:[\"$\",\"path\",\"qeys4\",{\"d\":\"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5\"}]\nd:[\"$\",\"nav\",null,{\"className\":\"flex items-center gap-6\",\"children\":[[\"$\",\"$L8\",null,{\"href\":\"/templates\",\"className\":\"text-sm text-muted-foreground transition-colors hover:text-foreground\",\"children\":\"Templates\"}],[\"$\",\"$L8\",null,{\"href\":\"/#signup\",\"className\":\"rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90\",\"children\":\"Start the Challenge\"}]]}]\n"])</script><script>self.__next_f.push([1,"e:[\"$\",\"main\",null,{\"className\":\"flex-1\",\"children\":[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:0:props:children:props:notFound:0:1:props:style\",\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:0:props:children:props:notFound:0:1:props:children:props:children:1:props:style\",\"children\":404}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:0:props:children:props:notFound:0:1:props:children:props:children:2:props:style\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:0:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style\",\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]\n"])</script><script>self.__next_f.push([1,"f:[\"$\",\"footer\",null,{\"className\":\"border-t border-border/50 bg-muted/20\",\"children\":[\"$\",\"div\",null,{\"className\":\"mx-auto flex w-full max-w-6xl flex-col items-center gap-4 px-6 py-10 md:flex-row md:justify-between\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-2 text-sm text-muted-foreground\",\"children\":[[\"$\",\"svg\",null,{\"ref\":\"$undefined\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"width\":24,\"height\":24,\"viewBox\":\"0 0 24 24\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":2,\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"className\":\"lucide lucide-rocket size-4\",\"aria-hidden\":\"true\",\"children\":[[\"$\",\"path\",\"m3kijz\",{\"d\":\"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z\"}],[\"$\",\"path\",\"1fmvmk\",{\"d\":\"m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z\"}],[\"$\",\"path\",\"1f8sc4\",{\"d\":\"M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0\"}],[\"$\",\"path\",\"qeys4\",{\"d\":\"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5\"}],\"$undefined\"]}],[\"$\",\"span\",null,{\"children\":\"Build a Startup\"}]]}],[\"$\",\"nav\",null,{\"className\":\"flex gap-6 text-sm text-muted-foreground\",\"children\":[[\"$\",\"$L8\",null,{\"href\":\"/templates\",\"className\":\"transition-colors hover:text-foreground\",\"children\":\"Templates\"}],[\"$\",\"$L8\",null,{\"href\":\"/pricing\",\"className\":\"transition-colors hover:text-foreground\",\"children\":\"Pricing\"}]]}],[\"$\",\"p\",null,{\"className\":\"text-xs text-muted-foreground\",\"children\":[\"© \",2026,\" \",\"Build a Startup\",\". All rights reserved.\"]}]]}]}]\n"])</script><script>self.__next_f.push([1,"10:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\n11:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\n12:[\"$\",\"$1\",\"c\",{\"children\":[\"$L15\",[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/c44be35721914ebf.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L16\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@18\"}]}]]}]\n13:[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L19\",null,{\"children\":\"$L1a\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L1b\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L1c\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}]\n"])</script><script>self.__next_f.push([1,"1d:I[79538,[\"/_next/static/chunks/96ab537394c4677f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b3232d64c2c3de43.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b187b5c0f682e0e9.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/71e8f7ee0623bd3f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/f5084ae60443579f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b0c49ac9f3ca70ba.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/0e4d7ef867a4338b.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/1d1313ba2a492a97.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/ed415703293dd1f1.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/c44be35721914ebf.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"TemplateCopyButton\"]\n1e:T2a82,"])</script><script>self.__next_f.push([1,"# Pre-Launch Review Checklist — [Product Name]\n\n*Review every item. Mark as Pass, Fail, or Skip. Track all failures in the Action Items section at the bottom.*\n\n**Severity levels:**\n- **Critical** — Blocks launch. Must fix before going live.\n- **Warning** — Should fix before launch. Causes user friction or security risk.\n- **Info** — Nice to have. Fix after launch if time permits.\n\n---\n\n## 1. Free Tier Experience\n*The free tier is your acquisition funnel. If it sucks, nobody upgrades.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | Free users can sign up and access the core feature without payment | ⬜ |\n| Critical | Free tier provides real standalone value (not a crippled product) | ⬜ |\n| Critical | Free tier limits are clearly communicated before the user hits them | ⬜ |\n| Warning | Upgrade prompts appear at natural friction points, not random popups | ⬜ |\n| Warning | Free user can understand the value of Pro from the upgrade prompt | ⬜ |\n| Info | Free users see social proof or testimonials on upgrade page | ⬜ |\n\n### Free tier smoke test:\n- [ ] Create a new account (don't use your admin account)\n- [ ] Complete the core workflow as a free user\n- [ ] Hit at least one free tier limit and verify the upgrade prompt\n- [ ] Verify that free users cannot access Pro-only features via URL manipulation\n\n---\n\n## 2. Authentication \u0026 Authorization\n*Every unprotected route is a potential data leak.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | Every query and mutation validates authentication via `ctx.auth.getUserIdentity()` or equivalent | ⬜ |\n| Critical | Every resource access verifies ownership: `resource.userId === currentUser._id` | ⬜ |\n| Critical | Unauthenticated users cannot access any `/dashboard`, `/project`, or `/settings` route | ⬜ |\n| Critical | Users cannot access other users' data by modifying IDs in URLs or API calls | ⬜ |\n| Warning | Sign-up flow works end-to-end (email + OAuth providers) | ⬜ |\n| Warning | Sign-in flow works end-to-end | ⬜ |\n| Warning | Password reset / account recovery flow works | ⬜ |\n| Warning | Session expiry redirects to sign-in gracefully (no error page) | ⬜ |\n| Info | User can delete their account and all associated data | ⬜ |\n\n### Auth smoke test:\n- [ ] Sign up with email, verify you can access dashboard\n- [ ] Sign up with OAuth (Google, GitHub), verify user record created\n- [ ] Open an incognito window, try to access `/dashboard` directly — should redirect to sign-in\n- [ ] Copy a project URL, open in incognito, sign in as a different user — should show \"not found\" or \"unauthorized\"\n\n---\n\n## 3. Payments \u0026 Billing\n*Payment bugs lose you money and trust. Test thoroughly.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | Stripe is in LIVE mode (not test mode) for production | ⬜ |\n| Critical | Checkout flow completes successfully with a real payment method | ⬜ |\n| Critical | Webhook handler verifies Stripe signature before processing | ⬜ |\n| Critical | `checkout.session.completed` webhook updates user plan to \"pro\" | ⬜ |\n| Critical | `customer.subscription.deleted` webhook reverts user plan to \"free\" | ⬜ |\n| Warning | `invoice.paid` webhook handles recurring payments correctly | ⬜ |\n| Warning | `invoice.payment_failed` webhook handles failed payments (notify user) | ⬜ |\n| Warning | Customer portal link works — user can manage/cancel subscription | ⬜ |\n| Warning | Duplicate webhook events are handled idempotently (no double-upgrades) | ⬜ |\n| Info | Pricing page clearly shows what's included in each plan | ⬜ |\n| Info | Receipt emails are sent (Stripe handles this by default) | ⬜ |\n\n### Payment smoke test:\n- [ ] Complete a real checkout (use Stripe's $0.50 test product if needed)\n- [ ] Verify user plan updated in database after successful payment\n- [ ] Cancel subscription via Stripe dashboard, verify plan reverted\n- [ ] Check Stripe dashboard for webhook delivery — any failures?\n\n---\n\n## 4. Security\n*One security bug can kill your product. Check every item.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | All API keys and secrets are in environment variables, not in code | ⬜ |\n| Critical | `.env` / `.env.local` is in `.gitignore` (not committed to repo) | ⬜ |\n| Critical | Stripe webhook signatures verified via `stripe.webhooks.constructEvent()` | ⬜ |\n| Critical | Clerk/auth webhook signatures verified via svix or equivalent | ⬜ |\n| Critical | Every mutation validates input types with framework validators (Convex `v`, Zod, etc.) | ⬜ |\n| Critical | No raw user input passed directly into AI system prompts | ⬜ |\n| Warning | Rate limiting on AI/expensive operations: max ___ requests/minute/user | ⬜ |\n| Warning | Rate limiting on auth endpoints: max ___ attempts/minute/IP | ⬜ |\n| Warning | CORS configured correctly (only allows your domain) | ⬜ |\n| Warning | No sensitive data logged to console or error tracking | ⬜ |\n| Warning | File uploads (if any) validated for type and size | ⬜ |\n| Info | Content Security Policy headers set | ⬜ |\n| Info | HTTPS enforced (HTTP redirects to HTTPS) | ⬜ |\n\n### Security smoke test:\n- [ ] Search codebase for hardcoded API keys: `grep -r \"sk_\" --include=\"*.ts\"`\n- [ ] Verify `.env` is not in git history: `git log --all --diff-filter=A -- .env`\n- [ ] Open browser dev tools → Network tab → verify no API keys in client-side requests\n- [ ] Try submitting a form with malicious input (XSS payload, SQL injection) — verify it's sanitized\n\n---\n\n## 5. SEO \u0026 Performance\n*If search engines can't find you, you don't exist.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Warning | Every page has a unique `\u003ctitle\u003e` tag | ⬜ |\n| Warning | Every page has a unique `\u003cmeta name=\"description\"\u003e` | ⬜ |\n| Warning | Open Graph tags set for social sharing (`og:title`, `og:description`, `og:image`) | ⬜ |\n| Warning | Sitemap.xml exists, is valid, and lists all public pages | ⬜ |\n| Warning | Robots.txt exists and allows search engine crawling of public pages | ⬜ |\n| Warning | Page load time under 3 seconds on mobile (test with PageSpeed Insights) | ⬜ |\n| Warning | All pages are mobile responsive — test at 375px width | ⬜ |\n| Info | Images have descriptive `alt` text | ⬜ |\n| Info | Structured data (JSON-LD) added for key pages | ⬜ |\n| Info | Canonical URLs set to prevent duplicate content | ⬜ |\n| Info | 404 page returns proper 404 status code (not 200) | ⬜ |\n\n### SEO smoke test:\n- [ ] Google \"site:yourdomain.com\" — are your pages indexed?\n- [ ] Paste your URL into Twitter/LinkedIn — does the preview card look right?\n- [ ] Run Google PageSpeed Insights on your landing page — score above 80?\n- [ ] Check sitemap.xml in browser — does it list all public pages?\n\n---\n\n## 6. Error Handling \u0026 UX\n*Users who hit errors and get no feedback will leave and never come back.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | No unhandled promise rejections or uncaught errors in production | ⬜ |\n| Warning | Loading skeletons shown during all data fetching (never a blank screen) | ⬜ |\n| Warning | Error messages are user-friendly (not raw stack traces or API errors) | ⬜ |\n| Warning | Empty states shown when no data exists (not blank white space) | ⬜ |\n| Warning | Form validation shows clear inline error messages | ⬜ |\n| Warning | Toast notifications confirm async actions (create, update, delete) | ⬜ |\n| Warning | 404 page exists and matches the app's design | ⬜ |\n| Info | Undo/confirmation for destructive actions (delete project, cancel subscription) | ⬜ |\n| Info | Keyboard navigation works for core workflows | ⬜ |\n| Info | Error monitoring service configured (Sentry, LogRocket, etc.) | ⬜ |\n\n### UX smoke test:\n- [ ] Disable JavaScript — does the page show a reasonable fallback?\n- [ ] Throttle network to \"Slow 3G\" in dev tools — do loading states appear?\n- [ ] Submit every form with empty fields — do validation errors appear?\n- [ ] Open the app on your phone — does everything fit?\n\n---\n\n## 7. Code Quality\n*Technical debt you ship on day 1 compounds forever.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | `npm run build` (or equivalent) passes with zero errors | ⬜ |\n| Warning | `npm run lint` passes with zero warnings | ⬜ |\n| Warning | No `console.log` statements in production code | ⬜ |\n| Warning | No hardcoded URLs — all URLs use environment variables or constants | ⬜ |\n| Warning | No `any` types in TypeScript (use `unknown` and narrow) | ⬜ |\n| Warning | No `@ts-ignore` or `@ts-expect-error` comments | ⬜ |\n| Warning | Environment variables documented in `.env.example` (without values) | ⬜ |\n| Info | No unused imports or variables | ⬜ |\n| Info | No commented-out code blocks | ⬜ |\n| Info | No TODO/FIXME/HACK comments left in production code | ⬜ |\n\n### Code quality smoke test:\n- [ ] Run `npm run build \u0026\u0026 npm run lint` — both pass clean?\n- [ ] Search for console.log: `grep -r \"console.log\" --include=\"*.ts\" --include=\"*.tsx\" src/`\n- [ ] Search for ts-ignore: `grep -r \"ts-ignore\\|ts-expect-error\" --include=\"*.ts\" --include=\"*.tsx\"`\n- [ ] Search for TODOs: `grep -r \"TODO\\|FIXME\\|HACK\" --include=\"*.ts\" --include=\"*.tsx\"`\n\n---\n\n## Pre-Deploy Checklist\n*Final checks before the deploy button.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | All environment variables set in production (not just local) | ⬜ |\n| Critical | Domain DNS configured and propagated | ⬜ |\n| Critical | SSL certificate active (HTTPS working) | ⬜ |\n| Critical | Stripe in live mode with live API keys | ⬜ |\n| Critical | Auth provider configured for production domain (not localhost) | ⬜ |\n| Warning | Webhook URLs point to production domain (not localhost or ngrok) | ⬜ |\n| Warning | Error monitoring configured for production | ⬜ |\n| Warning | Database backups configured (if self-hosted) | ⬜ |\n| Info | Analytics/tracking installed (Plausible, PostHog, etc.) | ⬜ |\n| Info | Custom error pages deployed (404, 500) | ⬜ |\n\n---\n\n## Action Items\n*Track every failed item here. Fix all Critical and Warning items before launch.*\n\n### Critical Fixes (must fix before launch)\n\n| Item | Section | Description | Status |\n|------|---------|-------------|--------|\n| | | | ⬜ |\n| | | | ⬜ |\n| | | | ⬜ |\n\n### Warning Fixes (should fix before launch)\n\n| Item | Section | Description | Status |\n|------|---------|-------------|--------|\n| | | | ⬜ |\n| | | | ⬜ |\n| | | | ⬜ |\n\n### Info Fixes (fix after launch)\n\n| Item | Section | Description | Status |\n|------|---------|-------------|--------|\n| | | | ⬜ |\n| | | | ⬜ |\n| | | | ⬜ |\n\n---\n\n*Want an AI to run these checks and generate a fix list? [Try Build a Startup →](https://buildastartup.ai)*"])</script><script>self.__next_f.push([1,"15:[\"$\",\"div\",null,{\"className\":\"mx-auto max-w-7xl px-6 py-16 md:py-24\",\"children\":[[\"$\",\"$L8\",null,{\"href\":\"/templates\",\"className\":\"mb-8 inline-flex items-center gap-2 text-sm text-muted-foreground transition-colors hover:text-foreground\",\"children\":[[\"$\",\"svg\",null,{\"ref\":\"$undefined\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"width\":24,\"height\":24,\"viewBox\":\"0 0 24 24\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":2,\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"className\":\"lucide lucide-arrow-left size-4\",\"aria-hidden\":\"true\",\"children\":[[\"$\",\"path\",\"1l729n\",{\"d\":\"m12 19-7-7 7-7\"}],[\"$\",\"path\",\"x3x0zl\",{\"d\":\"M19 12H5\"}],\"$undefined\"]}],\"All Templates\"]}],[\"$\",\"div\",null,{\"className\":\"mb-10 max-w-3xl\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-3 flex items-center gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"rounded-full border border-primary/20 bg-primary/5 px-3 py-1 text-xs font-medium text-primary\",\"children\":[\"Day \",7,\" of 7\"]}],[\"$\",\"span\",null,{\"className\":\"text-xs text-muted-foreground\",\"children\":\"7-Day AI Build Challenge\"}]]}],[\"$\",\"h1\",null,{\"className\":\"mb-3 text-3xl font-bold tracking-tight md:text-4xl lg:text-5xl\",\"children\":\"Pre-Launch Review Checklist\"}],[\"$\",\"p\",null,{\"className\":\"text-lg text-muted-foreground\",\"children\":\"Review your SaaS app across 7 critical areas before launching. Every item has a severity level and specific checks.\"}]]}],[\"$\",\"div\",null,{\"className\":\"mb-12 flex flex-wrap gap-3\",\"children\":[[\"$\",\"$L1d\",null,{\"content\":\"$1e\"}],\"$L1f\"]}],\"$L20\",\"$L21\",\"$L22\"]}]\n"])</script><script>self.__next_f.push([1,"25:I[74992,[\"/_next/static/chunks/96ab537394c4677f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b3232d64c2c3de43.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b187b5c0f682e0e9.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/71e8f7ee0623bd3f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/f5084ae60443579f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b0c49ac9f3ca70ba.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/0e4d7ef867a4338b.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/1d1313ba2a492a97.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/ed415703293dd1f1.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/c44be35721914ebf.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"SectionCopyButton\"]\n23:T42f7,"])</script><script>self.__next_f.push([1,"data:text/markdown;charset=utf-8,%23%20Pre-Launch%20Review%20Checklist%20%E2%80%94%20%5BProduct%20Name%5D%0A%0A*Review%20every%20item.%20Mark%20as%20Pass%2C%20Fail%2C%20or%20Skip.%20Track%20all%20failures%20in%20the%20Action%20Items%20section%20at%20the%20bottom.*%0A%0A**Severity%20levels%3A**%0A-%20**Critical**%20%E2%80%94%20Blocks%20launch.%20Must%20fix%20before%20going%20live.%0A-%20**Warning**%20%E2%80%94%20Should%20fix%20before%20launch.%20Causes%20user%20friction%20or%20security%20risk.%0A-%20**Info**%20%E2%80%94%20Nice%20to%20have.%20Fix%20after%20launch%20if%20time%20permits.%0A%0A---%0A%0A%23%23%201.%20Free%20Tier%20Experience%0A*The%20free%20tier%20is%20your%20acquisition%20funnel.%20If%20it%20sucks%2C%20nobody%20upgrades.*%0A%0A%7C%20Severity%20%7C%20Check%20%7C%20Status%20%7C%0A%7C----------%7C-------%7C--------%7C%0A%7C%20Critical%20%7C%20Free%20users%20can%20sign%20up%20and%20access%20the%20core%20feature%20without%20payment%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Free%20tier%20provides%20real%20standalone%20value%20(not%20a%20crippled%20product)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Free%20tier%20limits%20are%20clearly%20communicated%20before%20the%20user%20hits%20them%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Upgrade%20prompts%20appear%20at%20natural%20friction%20points%2C%20not%20random%20popups%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Free%20user%20can%20understand%20the%20value%20of%20Pro%20from%20the%20upgrade%20prompt%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Free%20users%20see%20social%20proof%20or%20testimonials%20on%20upgrade%20page%20%7C%20%E2%AC%9C%20%7C%0A%0A%23%23%23%20Free%20tier%20smoke%20test%3A%0A-%20%5B%20%5D%20Create%20a%20new%20account%20(don't%20use%20your%20admin%20account)%0A-%20%5B%20%5D%20Complete%20the%20core%20workflow%20as%20a%20free%20user%0A-%20%5B%20%5D%20Hit%20at%20least%20one%20free%20tier%20limit%20and%20verify%20the%20upgrade%20prompt%0A-%20%5B%20%5D%20Verify%20that%20free%20users%20cannot%20access%20Pro-only%20features%20via%20URL%20manipulation%0A%0A---%0A%0A%23%23%202.%20Authentication%20%26%20Authorization%0A*Every%20unprotected%20route%20is%20a%20potential%20data%20leak.*%0A%0A%7C%20Severity%20%7C%20Check%20%7C%20Status%20%7C%0A%7C----------%7C-------%7C--------%7C%0A%7C%20Critical%20%7C%20Every%20query%20and%20mutation%20validates%20authentication%20via%20%60ctx.auth.getUserIdentity()%60%20or%20equivalent%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Every%20resource%20access%20verifies%20ownership%3A%20%60resource.userId%20%3D%3D%3D%20currentUser._id%60%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Unauthenticated%20users%20cannot%20access%20any%20%60%2Fdashboard%60%2C%20%60%2Fproject%60%2C%20or%20%60%2Fsettings%60%20route%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Users%20cannot%20access%20other%20users'%20data%20by%20modifying%20IDs%20in%20URLs%20or%20API%20calls%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Sign-up%20flow%20works%20end-to-end%20(email%20%2B%20OAuth%20providers)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Sign-in%20flow%20works%20end-to-end%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Password%20reset%20%2F%20account%20recovery%20flow%20works%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Session%20expiry%20redirects%20to%20sign-in%20gracefully%20(no%20error%20page)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20User%20can%20delete%20their%20account%20and%20all%20associated%20data%20%7C%20%E2%AC%9C%20%7C%0A%0A%23%23%23%20Auth%20smoke%20test%3A%0A-%20%5B%20%5D%20Sign%20up%20with%20email%2C%20verify%20you%20can%20access%20dashboard%0A-%20%5B%20%5D%20Sign%20up%20with%20OAuth%20(Google%2C%20GitHub)%2C%20verify%20user%20record%20created%0A-%20%5B%20%5D%20Open%20an%20incognito%20window%2C%20try%20to%20access%20%60%2Fdashboard%60%20directly%20%E2%80%94%20should%20redirect%20to%20sign-in%0A-%20%5B%20%5D%20Copy%20a%20project%20URL%2C%20open%20in%20incognito%2C%20sign%20in%20as%20a%20different%20user%20%E2%80%94%20should%20show%20%22not%20found%22%20or%20%22unauthorized%22%0A%0A---%0A%0A%23%23%203.%20Payments%20%26%20Billing%0A*Payment%20bugs%20lose%20you%20money%20and%20trust.%20Test%20thoroughly.*%0A%0A%7C%20Severity%20%7C%20Check%20%7C%20Status%20%7C%0A%7C----------%7C-------%7C--------%7C%0A%7C%20Critical%20%7C%20Stripe%20is%20in%20LIVE%20mode%20(not%20test%20mode)%20for%20production%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Checkout%20flow%20completes%20successfully%20with%20a%20real%20payment%20method%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Webhook%20handler%20verifies%20Stripe%20signature%20before%20processing%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20%60checkout.session.completed%60%20webhook%20updates%20user%20plan%20to%20%22pro%22%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20%60customer.subscription.deleted%60%20webhook%20reverts%20user%20plan%20to%20%22free%22%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20%60invoice.paid%60%20webhook%20handles%20recurring%20payments%20correctly%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20%60invoice.payment_failed%60%20webhook%20handles%20failed%20payments%20(notify%20user)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Customer%20portal%20link%20works%20%E2%80%94%20user%20can%20manage%2Fcancel%20subscription%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Duplicate%20webhook%20events%20are%20handled%20idempotently%20(no%20double-upgrades)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Pricing%20page%20clearly%20shows%20what's%20included%20in%20each%20plan%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Receipt%20emails%20are%20sent%20(Stripe%20handles%20this%20by%20default)%20%7C%20%E2%AC%9C%20%7C%0A%0A%23%23%23%20Payment%20smoke%20test%3A%0A-%20%5B%20%5D%20Complete%20a%20real%20checkout%20(use%20Stripe's%20%240.50%20test%20product%20if%20needed)%0A-%20%5B%20%5D%20Verify%20user%20plan%20updated%20in%20database%20after%20successful%20payment%0A-%20%5B%20%5D%20Cancel%20subscription%20via%20Stripe%20dashboard%2C%20verify%20plan%20reverted%0A-%20%5B%20%5D%20Check%20Stripe%20dashboard%20for%20webhook%20delivery%20%E2%80%94%20any%20failures%3F%0A%0A---%0A%0A%23%23%204.%20Security%0A*One%20security%20bug%20can%20kill%20your%20product.%20Check%20every%20item.*%0A%0A%7C%20Severity%20%7C%20Check%20%7C%20Status%20%7C%0A%7C----------%7C-------%7C--------%7C%0A%7C%20Critical%20%7C%20All%20API%20keys%20and%20secrets%20are%20in%20environment%20variables%2C%20not%20in%20code%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20%60.env%60%20%2F%20%60.env.local%60%20is%20in%20%60.gitignore%60%20(not%20committed%20to%20repo)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Stripe%20webhook%20signatures%20verified%20via%20%60stripe.webhooks.constructEvent()%60%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Clerk%2Fauth%20webhook%20signatures%20verified%20via%20svix%20or%20equivalent%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Every%20mutation%20validates%20input%20types%20with%20framework%20validators%20(Convex%20%60v%60%2C%20Zod%2C%20etc.)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20No%20raw%20user%20input%20passed%20directly%20into%20AI%20system%20prompts%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Rate%20limiting%20on%20AI%2Fexpensive%20operations%3A%20max%20___%20requests%2Fminute%2Fuser%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Rate%20limiting%20on%20auth%20endpoints%3A%20max%20___%20attempts%2Fminute%2FIP%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20CORS%20configured%20correctly%20(only%20allows%20your%20domain)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20No%20sensitive%20data%20logged%20to%20console%20or%20error%20tracking%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20File%20uploads%20(if%20any)%20validated%20for%20type%20and%20size%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Content%20Security%20Policy%20headers%20set%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20HTTPS%20enforced%20(HTTP%20redirects%20to%20HTTPS)%20%7C%20%E2%AC%9C%20%7C%0A%0A%23%23%23%20Security%20smoke%20test%3A%0A-%20%5B%20%5D%20Search%20codebase%20for%20hardcoded%20API%20keys%3A%20%60grep%20-r%20%22sk_%22%20--include%3D%22*.ts%22%60%0A-%20%5B%20%5D%20Verify%20%60.env%60%20is%20not%20in%20git%20history%3A%20%60git%20log%20--all%20--diff-filter%3DA%20--%20.env%60%0A-%20%5B%20%5D%20Open%20browser%20dev%20tools%20%E2%86%92%20Network%20tab%20%E2%86%92%20verify%20no%20API%20keys%20in%20client-side%20requests%0A-%20%5B%20%5D%20Try%20submitting%20a%20form%20with%20malicious%20input%20(XSS%20payload%2C%20SQL%20injection)%20%E2%80%94%20verify%20it's%20sanitized%0A%0A---%0A%0A%23%23%205.%20SEO%20%26%20Performance%0A*If%20search%20engines%20can't%20find%20you%2C%20you%20don't%20exist.*%0A%0A%7C%20Severity%20%7C%20Check%20%7C%20Status%20%7C%0A%7C----------%7C-------%7C--------%7C%0A%7C%20Warning%20%7C%20Every%20page%20has%20a%20unique%20%60%3Ctitle%3E%60%20tag%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Every%20page%20has%20a%20unique%20%60%3Cmeta%20name%3D%22description%22%3E%60%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Open%20Graph%20tags%20set%20for%20social%20sharing%20(%60og%3Atitle%60%2C%20%60og%3Adescription%60%2C%20%60og%3Aimage%60)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Sitemap.xml%20exists%2C%20is%20valid%2C%20and%20lists%20all%20public%20pages%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Robots.txt%20exists%20and%20allows%20search%20engine%20crawling%20of%20public%20pages%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Page%20load%20time%20under%203%20seconds%20on%20mobile%20(test%20with%20PageSpeed%20Insights)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20All%20pages%20are%20mobile%20responsive%20%E2%80%94%20test%20at%20375px%20width%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Images%20have%20descriptive%20%60alt%60%20text%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Structured%20data%20(JSON-LD)%20added%20for%20key%20pages%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Canonical%20URLs%20set%20to%20prevent%20duplicate%20content%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20404%20page%20returns%20proper%20404%20status%20code%20(not%20200)%20%7C%20%E2%AC%9C%20%7C%0A%0A%23%23%23%20SEO%20smoke%20test%3A%0A-%20%5B%20%5D%20Google%20%22site%3Ayourdomain.com%22%20%E2%80%94%20are%20your%20pages%20indexed%3F%0A-%20%5B%20%5D%20Paste%20your%20URL%20into%20Twitter%2FLinkedIn%20%E2%80%94%20does%20the%20preview%20card%20look%20right%3F%0A-%20%5B%20%5D%20Run%20Google%20PageSpeed%20Insights%20on%20your%20landing%20page%20%E2%80%94%20score%20above%2080%3F%0A-%20%5B%20%5D%20Check%20sitemap.xml%20in%20browser%20%E2%80%94%20does%20it%20list%20all%20public%20pages%3F%0A%0A---%0A%0A%23%23%206.%20Error%20Handling%20%26%20UX%0A*Users%20who%20hit%20errors%20and%20get%20no%20feedback%20will%20leave%20and%20never%20come%20back.*%0A%0A%7C%20Severity%20%7C%20Check%20%7C%20Status%20%7C%0A%7C----------%7C-------%7C--------%7C%0A%7C%20Critical%20%7C%20No%20unhandled%20promise%20rejections%20or%20uncaught%20errors%20in%20production%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Loading%20skeletons%20shown%20during%20all%20data%20fetching%20(never%20a%20blank%20screen)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Error%20messages%20are%20user-friendly%20(not%20raw%20stack%20traces%20or%20API%20errors)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Empty%20states%20shown%20when%20no%20data%20exists%20(not%20blank%20white%20space)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Form%20validation%20shows%20clear%20inline%20error%20messages%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Toast%20notifications%20confirm%20async%20actions%20(create%2C%20update%2C%20delete)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20404%20page%20exists%20and%20matches%20the%20app's%20design%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Undo%2Fconfirmation%20for%20destructive%20actions%20(delete%20project%2C%20cancel%20subscription)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Keyboard%20navigation%20works%20for%20core%20workflows%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Error%20monitoring%20service%20configured%20(Sentry%2C%20LogRocket%2C%20etc.)%20%7C%20%E2%AC%9C%20%7C%0A%0A%23%23%23%20UX%20smoke%20test%3A%0A-%20%5B%20%5D%20Disable%20JavaScript%20%E2%80%94%20does%20the%20page%20show%20a%20reasonable%20fallback%3F%0A-%20%5B%20%5D%20Throttle%20network%20to%20%22Slow%203G%22%20in%20dev%20tools%20%E2%80%94%20do%20loading%20states%20appear%3F%0A-%20%5B%20%5D%20Submit%20every%20form%20with%20empty%20fields%20%E2%80%94%20do%20validation%20errors%20appear%3F%0A-%20%5B%20%5D%20Open%20the%20app%20on%20your%20phone%20%E2%80%94%20does%20everything%20fit%3F%0A%0A---%0A%0A%23%23%207.%20Code%20Quality%0A*Technical%20debt%20you%20ship%20on%20day%201%20compounds%20forever.*%0A%0A%7C%20Severity%20%7C%20Check%20%7C%20Status%20%7C%0A%7C----------%7C-------%7C--------%7C%0A%7C%20Critical%20%7C%20%60npm%20run%20build%60%20(or%20equivalent)%20passes%20with%20zero%20errors%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20%60npm%20run%20lint%60%20passes%20with%20zero%20warnings%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20No%20%60console.log%60%20statements%20in%20production%20code%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20No%20hardcoded%20URLs%20%E2%80%94%20all%20URLs%20use%20environment%20variables%20or%20constants%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20No%20%60any%60%20types%20in%20TypeScript%20(use%20%60unknown%60%20and%20narrow)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20No%20%60%40ts-ignore%60%20or%20%60%40ts-expect-error%60%20comments%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Environment%20variables%20documented%20in%20%60.env.example%60%20(without%20values)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20No%20unused%20imports%20or%20variables%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20No%20commented-out%20code%20blocks%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20No%20TODO%2FFIXME%2FHACK%20comments%20left%20in%20production%20code%20%7C%20%E2%AC%9C%20%7C%0A%0A%23%23%23%20Code%20quality%20smoke%20test%3A%0A-%20%5B%20%5D%20Run%20%60npm%20run%20build%20%26%26%20npm%20run%20lint%60%20%E2%80%94%20both%20pass%20clean%3F%0A-%20%5B%20%5D%20Search%20for%20console.log%3A%20%60grep%20-r%20%22console.log%22%20--include%3D%22*.ts%22%20--include%3D%22*.tsx%22%20src%2F%60%0A-%20%5B%20%5D%20Search%20for%20ts-ignore%3A%20%60grep%20-r%20%22ts-ignore%5C%7Cts-expect-error%22%20--include%3D%22*.ts%22%20--include%3D%22*.tsx%22%60%0A-%20%5B%20%5D%20Search%20for%20TODOs%3A%20%60grep%20-r%20%22TODO%5C%7CFIXME%5C%7CHACK%22%20--include%3D%22*.ts%22%20--include%3D%22*.tsx%22%60%0A%0A---%0A%0A%23%23%20Pre-Deploy%20Checklist%0A*Final%20checks%20before%20the%20deploy%20button.*%0A%0A%7C%20Severity%20%7C%20Check%20%7C%20Status%20%7C%0A%7C----------%7C-------%7C--------%7C%0A%7C%20Critical%20%7C%20All%20environment%20variables%20set%20in%20production%20(not%20just%20local)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Domain%20DNS%20configured%20and%20propagated%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20SSL%20certificate%20active%20(HTTPS%20working)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Stripe%20in%20live%20mode%20with%20live%20API%20keys%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Critical%20%7C%20Auth%20provider%20configured%20for%20production%20domain%20(not%20localhost)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Webhook%20URLs%20point%20to%20production%20domain%20(not%20localhost%20or%20ngrok)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Error%20monitoring%20configured%20for%20production%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Warning%20%7C%20Database%20backups%20configured%20(if%20self-hosted)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Analytics%2Ftracking%20installed%20(Plausible%2C%20PostHog%2C%20etc.)%20%7C%20%E2%AC%9C%20%7C%0A%7C%20Info%20%7C%20Custom%20error%20pages%20deployed%20(404%2C%20500)%20%7C%20%E2%AC%9C%20%7C%0A%0A---%0A%0A%23%23%20Action%20Items%0A*Track%20every%20failed%20item%20here.%20Fix%20all%20Critical%20and%20Warning%20items%20before%20launch.*%0A%0A%23%23%23%20Critical%20Fixes%20(must%20fix%20before%20launch)%0A%0A%7C%20Item%20%7C%20Section%20%7C%20Description%20%7C%20Status%20%7C%0A%7C------%7C---------%7C-------------%7C--------%7C%0A%7C%20%7C%20%7C%20%7C%20%E2%AC%9C%20%7C%0A%7C%20%7C%20%7C%20%7C%20%E2%AC%9C%20%7C%0A%7C%20%7C%20%7C%20%7C%20%E2%AC%9C%20%7C%0A%0A%23%23%23%20Warning%20Fixes%20(should%20fix%20before%20launch)%0A%0A%7C%20Item%20%7C%20Section%20%7C%20Description%20%7C%20Status%20%7C%0A%7C------%7C---------%7C-------------%7C--------%7C%0A%7C%20%7C%20%7C%20%7C%20%E2%AC%9C%20%7C%0A%7C%20%7C%20%7C%20%7C%20%E2%AC%9C%20%7C%0A%7C%20%7C%20%7C%20%7C%20%E2%AC%9C%20%7C%0A%0A%23%23%23%20Info%20Fixes%20(fix%20after%20launch)%0A%0A%7C%20Item%20%7C%20Section%20%7C%20Description%20%7C%20Status%20%7C%0A%7C------%7C---------%7C-------------%7C--------%7C%0A%7C%20%7C%20%7C%20%7C%20%E2%AC%9C%20%7C%0A%7C%20%7C%20%7C%20%7C%20%E2%AC%9C%20%7C%0A%7C%20%7C%20%7C%20%7C%20%E2%AC%9C%20%7C%0A%0A---%0A%0A*Want%20an%20AI%20to%20run%20these%20checks%20and%20generate%20a%20fix%20list%3F%20%5BTry%20Build%20a%20Startup%20%E2%86%92%5D(https%3A%2F%2Fbuildastartup.ai)*"])</script><script>self.__next_f.push([1,"1f:[\"$\",\"a\",null,{\"href\":\"$23\",\"download\":\"review-checklist.md\",\"className\":\"inline-flex items-center gap-2 rounded-lg border border-border px-4 py-2 text-sm font-medium transition-colors hover:bg-muted\",\"children\":[\"$L24\",\"Download .md\"]}]\n"])</script><script>self.__next_f.push([1,"20:[\"$\",\"div\",null,{\"className\":\"grid gap-12 xl:grid-cols-[1fr_220px]\",\"children\":[[\"$\",\"article\",null,{\"className\":\"min-w-0\",\"children\":[[\"$\",\"div\",\"1-free-tier-experience\",{\"id\":\"1-free-tier-experience\",\"className\":\"mb-10 scroll-mt-24\",\"children\":[\"$\",\"div\",null,{\"className\":\"mb-6 rounded-xl border p-6 border-border/50 bg-card/30\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-4 flex items-center justify-between gap-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary\",\"children\":1}],[\"$\",\"h2\",null,{\"className\":\"text-xl font-bold tracking-tight\",\"children\":[false,\"Free Tier Experience\"]}]]}],[\"$\",\"$L25\",null,{\"content\":\"## Free Tier Experience\\n*The free tier is your acquisition funnel. If it sucks, nobody upgrades.*\\n\\n| Severity | Check | Status |\\n|----------|-------|--------|\\n| Critical | Free users can sign up and access the core feature without payment | ⬜ |\\n| Critical | Free tier provides real standalone value (not a crippled product) | ⬜ |\\n| Critical | Free tier limits are clearly communicated before the user hits them | ⬜ |\\n| Warning | Upgrade prompts appear at natural friction points, not random popups | ⬜ |\\n| Warning | Free user can understand the value of Pro from the upgrade prompt | ⬜ |\\n| Info | Free users see social proof or testimonials on upgrade page | ⬜ |\\n\\n### Free tier smoke test:\\n- [ ] Create a new account (don't use your admin account)\\n- [ ] Complete the core workflow as a free user\\n- [ ] Hit at least one free tier limit and verify the upgrade prompt\\n- [ ] Verify that free users cannot access Pro-only features via URL manipulation\\n\\n---\\n\"}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground\",\"children\":[[\"$\",\"p\",\"el-0\",{\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cem\u003eThe free tier is your acquisition funnel. If it sucks, nobody upgrades.\u003c/em\u003e\"}}],[\"$\",\"div\",\"el-1\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Severity\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Check\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Free users can sign up and access the core feature without payment\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}],[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[\"$L26\",\"$L27\",\"$L28\"]}],\"$L29\",\"$L2a\",\"$L2b\",\"$L2c\"]}]]}]}],\"$L2d\",\"$L2e\"]}]]}]}],\"$L2f\",\"$L30\",\"$L31\",\"$L32\",\"$L33\",\"$L34\",\"$L35\",\"$L36\"]}],\"$L37\"]}]\n"])</script><script>self.__next_f.push([1,"21:[\"$\",\"div\",null,{\"className\":\"mt-8 rounded-2xl border border-primary/20 bg-gradient-to-br from-primary/10 via-primary/5 to-transparent p-10 text-center md:p-14\",\"children\":[\"$\",\"div\",null,{\"className\":\"mx-auto max-w-lg\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-4 inline-flex items-center gap-2 rounded-full border border-primary/20 bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary\",\"children\":[[\"$\",\"svg\",null,{\"ref\":\"$undefined\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"width\":24,\"height\":24,\"viewBox\":\"0 0 24 24\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":2,\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"className\":\"lucide lucide-rocket size-3.5\",\"aria-hidden\":\"true\",\"children\":[[\"$\",\"path\",\"m3kijz\",{\"d\":\"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z\"}],[\"$\",\"path\",\"1fmvmk\",{\"d\":\"m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z\"}],[\"$\",\"path\",\"1f8sc4\",{\"d\":\"M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0\"}],[\"$\",\"path\",\"qeys4\",{\"d\":\"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5\"}],\"$undefined\"]}],\"Skip the manual work\"]}],[\"$\",\"h2\",null,{\"className\":\"mb-3 text-2xl font-bold md:text-3xl\",\"children\":\"Want the AI to fill this out for you?\"}],[\"$\",\"p\",null,{\"className\":\"mb-8 text-muted-foreground\",\"children\":\"Build a Startup walks you through every step with a conversational AI that asks the right questions and pushes back on bad decisions.\"}],[\"$\",\"$L8\",null,{\"href\":\"/dashboard\",\"className\":\"inline-flex items-center gap-2 rounded-lg bg-primary px-8 py-3.5 text-sm font-semibold text-primary-foreground transition-colors hover:bg-primary/90\",\"children\":[[\"$\",\"svg\",null,{\"ref\":\"$undefined\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"width\":24,\"height\":24,\"viewBox\":\"0 0 24 24\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":2,\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"className\":\"lucide lucide-rocket size-4\",\"aria-hidden\":\"true\",\"children\":[[\"$\",\"path\",\"m3kijz\",{\"d\":\"M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z\"}],[\"$\",\"path\",\"1fmvmk\",{\"d\":\"m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z\"}],[\"$\",\"path\",\"1f8sc4\",{\"d\":\"M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0\"}],[\"$\",\"path\",\"qeys4\",{\"d\":\"M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5\"}],\"$undefined\"]}],\"Try Build a Startup — Free\"]}]]}]}]\n"])</script><script>self.__next_f.push([1,"22:[\"$\",\"div\",null,{\"className\":\"mt-12 grid gap-4 border-t border-border/50 pt-8 sm:grid-cols-2\",\"children\":[[\"$\",\"$L8\",null,{\"href\":\"/templates/build-plan\",\"className\":\"group flex items-center gap-3 rounded-xl border border-border/50 p-4 transition-colors hover:border-border hover:bg-card/50\",\"children\":[[\"$\",\"svg\",null,{\"ref\":\"$undefined\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"width\":24,\"height\":24,\"viewBox\":\"0 0 24 24\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":2,\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"className\":\"lucide lucide-arrow-left size-4 text-muted-foreground transition-transform group-hover:-translate-x-0.5\",\"aria-hidden\":\"true\",\"children\":[[\"$\",\"path\",\"1l729n\",{\"d\":\"m12 19-7-7 7-7\"}],[\"$\",\"path\",\"x3x0zl\",{\"d\":\"M19 12H5\"}],\"$undefined\"]}],[\"$\",\"div\",null,{\"children\":[[\"$\",\"p\",null,{\"className\":\"text-xs text-muted-foreground\",\"children\":[\"Day \",6]}],[\"$\",\"p\",null,{\"className\":\"text-sm font-medium\",\"children\":\"Build Plan Template\"}]]}]]}],[\"$\",\"div\",null,{}]]}]\n"])</script><script>self.__next_f.push([1,"9b:I[95517,[\"/_next/static/chunks/96ab537394c4677f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b3232d64c2c3de43.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b187b5c0f682e0e9.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/71e8f7ee0623bd3f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/f5084ae60443579f.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/b0c49ac9f3ca70ba.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/0e4d7ef867a4338b.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/1d1313ba2a492a97.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/ed415703293dd1f1.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/c44be35721914ebf.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"SectionNav\"]\n24:[\"$\",\"svg\",null,{\"ref\":\"$undefined\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"width\":24,\"height\":24,\"viewBox\":\"0 0 24 24\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":2,\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"className\":\"lucide lucide-download size-4\",\"aria-hidden\":\"true\",\"children\":[[\"$\",\"path\",\"m9g1x1\",{\"d\":\"M12 15V3\"}],[\"$\",\"path\",\"ih7n3h\",{\"d\":\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"}],[\"$\",\"path\",\"brsn70\",{\"d\":\"m7 10 5 5 5-5\"}],\"$undefined\"]}]\n26:[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}]\n27:[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Free tier provides real standalone value (not a crippled product)\"}}]\n28:[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]\n29:[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Free tier limits are clearly communicated before the user hits them\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n2a:[\"$\",\"tr\",\"3\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Upgrade prompts appear at natural friction points, not random popups\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n2b:[\"$\",\"tr\",\"4\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Free user can understand the value of Pro from the upgrade prompt\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n2c:[\"$\",\"tr\",\"5\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Free users see social proof or testimonials on upgrade page\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n2d:[\"$\",\"h3\",\"el-2\",{\"children\":\"Free tier smoke test:\"}]\n"])</script><script>self.__next_f.push([1,"2e:[\"$\",\"ul\",\"el-3\",{\"className\":\"not-prose my-4 space-y-2\",\"children\":[[\"$\",\"li\",\"0\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Create a new account (don't use your admin account)\"}}]]}],[\"$\",\"li\",\"1\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Complete the core workflow as a free user\"}}]]}],[\"$\",\"li\",\"2\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Hit at least one free tier limit and verify the upgrade prompt\"}}]]}],[\"$\",\"li\",\"3\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Verify that free users cannot access Pro-only features via URL manipulation\"}}]]}]]}]\n"])</script><script>self.__next_f.push([1,"38:T509,## Authentication \u0026 Authorization\n*Every unprotected route is a potential data leak.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | Every query and mutation validates authentication via `ctx.auth.getUserIdentity()` or equivalent | ⬜ |\n| Critical | Every resource access verifies ownership: `resource.userId === currentUser._id` | ⬜ |\n| Critical | Unauthenticated users cannot access any `/dashboard`, `/project`, or `/settings` route | ⬜ |\n| Critical | Users cannot access other users' data by modifying IDs in URLs or API calls | ⬜ |\n| Warning | Sign-up flow works end-to-end (email + OAuth providers) | ⬜ |\n| Warning | Sign-in flow works end-to-end | ⬜ |\n| Warning | Password reset / account recovery flow works | ⬜ |\n| Warning | Session expiry redirects to sign-in gracefully (no error page) | ⬜ |\n| Info | User can delete their account and all associated data | ⬜ |\n\n### Auth smoke test:\n- [ ] Sign up with email, verify you can access dashboard\n- [ ] Sign up with OAuth (Google, GitHub), verify user record created\n- [ ] Open an incognito window, try to access `/dashboard` directly — should redirect to sign-in\n- [ ] Copy a project URL, open in incognito, sign in as a different user — should show \"not found\" or \"unauthorized\"\n\n---\n"])</script><script>self.__next_f.push([1,"2f:[\"$\",\"div\",\"2-authentication-authorization\",{\"id\":\"2-authentication-authorization\",\"className\":\"mb-10 scroll-mt-24\",\"children\":[\"$\",\"div\",null,{\"className\":\"mb-6 rounded-xl border p-6 border-border/50 bg-card/30\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-4 flex items-center justify-between gap-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary\",\"children\":2}],[\"$\",\"h2\",null,{\"className\":\"text-xl font-bold tracking-tight\",\"children\":[false,\"Authentication \u0026 Authorization\"]}]]}],[\"$\",\"$L25\",null,{\"content\":\"$38\"}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground\",\"children\":[[\"$\",\"p\",\"el-0\",{\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cem\u003eEvery unprotected route is a potential data leak.\u003c/em\u003e\"}}],[\"$\",\"div\",\"el-1\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Severity\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Check\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Every query and mutation validates authentication via \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003ectx.auth.getUserIdentity()\u003c/code\u003e or equivalent\"}}],\"$L39\"]}],\"$L3a\",\"$L3b\",\"$L3c\",\"$L3d\",\"$L3e\",\"$L3f\",\"$L40\",\"$L41\"]}]]}]}],\"$L42\",\"$L43\"]}]]}]}]\n"])</script><script>self.__next_f.push([1,"44:T552,## Payments \u0026 Billing\n*Payment bugs lose you money and trust. Test thoroughly.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | Stripe is in LIVE mode (not test mode) for production | ⬜ |\n| Critical | Checkout flow completes successfully with a real payment method | ⬜ |\n| Critical | Webhook handler verifies Stripe signature before processing | ⬜ |\n| Critical | `checkout.session.completed` webhook updates user plan to \"pro\" | ⬜ |\n| Critical | `customer.subscription.deleted` webhook reverts user plan to \"free\" | ⬜ |\n| Warning | `invoice.paid` webhook handles recurring payments correctly | ⬜ |\n| Warning | `invoice.payment_failed` webhook handles failed payments (notify user) | ⬜ |\n| Warning | Customer portal link works — user can manage/cancel subscription | ⬜ |\n| Warning | Duplicate webhook events are handled idempotently (no double-upgrades) | ⬜ |\n| Info | Pricing page clearly shows what's included in each plan | ⬜ |\n| Info | Receipt emails are sent (Stripe handles this by default) | ⬜ |\n\n### Payment smoke test:\n- [ ] Complete a real checkout (use Stripe's $0.50 test product if needed)\n- [ ] Verify user plan updated in database after successful payment\n- [ ] Cancel subscription via Stripe dashboard, verify plan reverted\n- [ ] Check Stripe dashboard for webhook delivery — any failures?\n\n---\n"])</script><script>self.__next_f.push([1,"30:[\"$\",\"div\",\"3-payments-billing\",{\"id\":\"3-payments-billing\",\"className\":\"mb-10 scroll-mt-24\",\"children\":[\"$\",\"div\",null,{\"className\":\"mb-6 rounded-xl border p-6 border-border/50 bg-card/30\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-4 flex items-center justify-between gap-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary\",\"children\":3}],[\"$\",\"h2\",null,{\"className\":\"text-xl font-bold tracking-tight\",\"children\":[false,\"Payments \u0026 Billing\"]}]]}],[\"$\",\"$L25\",null,{\"content\":\"$44\"}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground\",\"children\":[[\"$\",\"p\",\"el-0\",{\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cem\u003ePayment bugs lose you money and trust. Test thoroughly.\u003c/em\u003e\"}}],[\"$\",\"div\",\"el-1\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Severity\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Check\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Stripe is in LIVE mode (not test mode) for production\"}}],\"$L45\"]}],\"$L46\",\"$L47\",\"$L48\",\"$L49\",\"$L4a\",\"$L4b\",\"$L4c\",\"$L4d\",\"$L4e\",\"$L4f\"]}]]}]}],\"$L50\",\"$L51\"]}]]}]}]\n"])</script><script>self.__next_f.push([1,"52:T616,"])</script><script>self.__next_f.push([1,"## Security\n*One security bug can kill your product. Check every item.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | All API keys and secrets are in environment variables, not in code | ⬜ |\n| Critical | `.env` / `.env.local` is in `.gitignore` (not committed to repo) | ⬜ |\n| Critical | Stripe webhook signatures verified via `stripe.webhooks.constructEvent()` | ⬜ |\n| Critical | Clerk/auth webhook signatures verified via svix or equivalent | ⬜ |\n| Critical | Every mutation validates input types with framework validators (Convex `v`, Zod, etc.) | ⬜ |\n| Critical | No raw user input passed directly into AI system prompts | ⬜ |\n| Warning | Rate limiting on AI/expensive operations: max ___ requests/minute/user | ⬜ |\n| Warning | Rate limiting on auth endpoints: max ___ attempts/minute/IP | ⬜ |\n| Warning | CORS configured correctly (only allows your domain) | ⬜ |\n| Warning | No sensitive data logged to console or error tracking | ⬜ |\n| Warning | File uploads (if any) validated for type and size | ⬜ |\n| Info | Content Security Policy headers set | ⬜ |\n| Info | HTTPS enforced (HTTP redirects to HTTPS) | ⬜ |\n\n### Security smoke test:\n- [ ] Search codebase for hardcoded API keys: `grep -r \"sk_\" --include=\"*.ts\"`\n- [ ] Verify `.env` is not in git history: `git log --all --diff-filter=A -- .env`\n- [ ] Open browser dev tools → Network tab → verify no API keys in client-side requests\n- [ ] Try submitting a form with malicious input (XSS payload, SQL injection) — verify it's sanitized\n\n---\n"])</script><script>self.__next_f.push([1,"31:[\"$\",\"div\",\"4-security\",{\"id\":\"4-security\",\"className\":\"mb-10 scroll-mt-24\",\"children\":[\"$\",\"div\",null,{\"className\":\"mb-6 rounded-xl border p-6 border-border/50 bg-card/30\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-4 flex items-center justify-between gap-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary\",\"children\":4}],[\"$\",\"h2\",null,{\"className\":\"text-xl font-bold tracking-tight\",\"children\":[false,\"Security\"]}]]}],[\"$\",\"$L25\",null,{\"content\":\"$52\"}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground\",\"children\":[[\"$\",\"p\",\"el-0\",{\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cem\u003eOne security bug can kill your product. Check every item.\u003c/em\u003e\"}}],[\"$\",\"div\",\"el-1\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Severity\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Check\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[\"$L53\",\"$L54\",\"$L55\"]}],\"$L56\",\"$L57\",\"$L58\",\"$L59\",\"$L5a\",\"$L5b\",\"$L5c\",\"$L5d\",\"$L5e\",\"$L5f\",\"$L60\",\"$L61\"]}]]}]}],\"$L62\",\"$L63\"]}]]}]}]\n"])</script><script>self.__next_f.push([1,"64:T4f0,## SEO \u0026 Performance\n*If search engines can't find you, you don't exist.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Warning | Every page has a unique `\u003ctitle\u003e` tag | ⬜ |\n| Warning | Every page has a unique `\u003cmeta name=\"description\"\u003e` | ⬜ |\n| Warning | Open Graph tags set for social sharing (`og:title`, `og:description`, `og:image`) | ⬜ |\n| Warning | Sitemap.xml exists, is valid, and lists all public pages | ⬜ |\n| Warning | Robots.txt exists and allows search engine crawling of public pages | ⬜ |\n| Warning | Page load time under 3 seconds on mobile (test with PageSpeed Insights) | ⬜ |\n| Warning | All pages are mobile responsive — test at 375px width | ⬜ |\n| Info | Images have descriptive `alt` text | ⬜ |\n| Info | Structured data (JSON-LD) added for key pages | ⬜ |\n| Info | Canonical URLs set to prevent duplicate content | ⬜ |\n| Info | 404 page returns proper 404 status code (not 200) | ⬜ |\n\n### SEO smoke test:\n- [ ] Google \"site:yourdomain.com\" — are your pages indexed?\n- [ ] Paste your URL into Twitter/LinkedIn — does the preview card look right?\n- [ ] Run Google PageSpeed Insights on your landing page — score above 80?\n- [ ] Check sitemap.xml in browser — does it list all public pages?\n\n---\n"])</script><script>self.__next_f.push([1,"32:[\"$\",\"div\",\"5-seo-performance\",{\"id\":\"5-seo-performance\",\"className\":\"mb-10 scroll-mt-24\",\"children\":[\"$\",\"div\",null,{\"className\":\"mb-6 rounded-xl border p-6 border-border/50 bg-card/30\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-4 flex items-center justify-between gap-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary\",\"children\":5}],[\"$\",\"h2\",null,{\"className\":\"text-xl font-bold tracking-tight\",\"children\":[false,\"SEO \u0026 Performance\"]}]]}],[\"$\",\"$L25\",null,{\"content\":\"$64\"}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground\",\"children\":[[\"$\",\"p\",\"el-0\",{\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cem\u003eIf search engines can't find you, you don't exist.\u003c/em\u003e\"}}],[\"$\",\"div\",\"el-1\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Severity\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Check\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Every page has a unique \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e\u003ctitle\u003e\u003c/code\u003e tag\"}}],\"$L65\"]}],\"$L66\",\"$L67\",\"$L68\",\"$L69\",\"$L6a\",\"$L6b\",\"$L6c\",\"$L6d\",\"$L6e\",\"$L6f\"]}]]}]}],\"$L70\",\"$L71\"]}]]}]}]\n"])</script><script>self.__next_f.push([1,"72:T4fe,## Error Handling \u0026 UX\n*Users who hit errors and get no feedback will leave and never come back.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | No unhandled promise rejections or uncaught errors in production | ⬜ |\n| Warning | Loading skeletons shown during all data fetching (never a blank screen) | ⬜ |\n| Warning | Error messages are user-friendly (not raw stack traces or API errors) | ⬜ |\n| Warning | Empty states shown when no data exists (not blank white space) | ⬜ |\n| Warning | Form validation shows clear inline error messages | ⬜ |\n| Warning | Toast notifications confirm async actions (create, update, delete) | ⬜ |\n| Warning | 404 page exists and matches the app's design | ⬜ |\n| Info | Undo/confirmation for destructive actions (delete project, cancel subscription) | ⬜ |\n| Info | Keyboard navigation works for core workflows | ⬜ |\n| Info | Error monitoring service configured (Sentry, LogRocket, etc.) | ⬜ |\n\n### UX smoke test:\n- [ ] Disable JavaScript — does the page show a reasonable fallback?\n- [ ] Throttle network to \"Slow 3G\" in dev tools — do loading states appear?\n- [ ] Submit every form with empty fields — do validation errors appear?\n- [ ] Open the app on your phone — does everything fit?\n\n---\n"])</script><script>self.__next_f.push([1,"33:[\"$\",\"div\",\"6-error-handling-ux\",{\"id\":\"6-error-handling-ux\",\"className\":\"mb-10 scroll-mt-24\",\"children\":[\"$\",\"div\",null,{\"className\":\"mb-6 rounded-xl border p-6 border-border/50 bg-card/30\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-4 flex items-center justify-between gap-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary\",\"children\":6}],[\"$\",\"h2\",null,{\"className\":\"text-xl font-bold tracking-tight\",\"children\":[false,\"Error Handling \u0026 UX\"]}]]}],[\"$\",\"$L25\",null,{\"content\":\"$72\"}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground\",\"children\":[[\"$\",\"p\",\"el-0\",{\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cem\u003eUsers who hit errors and get no feedback will leave and never come back.\u003c/em\u003e\"}}],[\"$\",\"div\",\"el-1\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Severity\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Check\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"No unhandled promise rejections or uncaught errors in production\"}}],\"$L73\"]}],\"$L74\",\"$L75\",\"$L76\",\"$L77\",\"$L78\",\"$L79\",\"$L7a\",\"$L7b\",\"$L7c\"]}]]}]}],\"$L7d\",\"$L7e\"]}]]}]}]\n"])</script><script>self.__next_f.push([1,"7f:T4b3,## Code Quality\n*Technical debt you ship on day 1 compounds forever.*\n\n| Severity | Check | Status |\n|----------|-------|--------|\n| Critical | `npm run build` (or equivalent) passes with zero errors | ⬜ |\n| Warning | `npm run lint` passes with zero warnings | ⬜ |\n| Warning | No `console.log` statements in production code | ⬜ |\n| Warning | No hardcoded URLs — all URLs use environment variables or constants | ⬜ |\n| Warning | No `any` types in TypeScript (use `unknown` and narrow) | ⬜ |\n| Warning | No `@ts-ignore` or `@ts-expect-error` comments | ⬜ |\n| Warning | Environment variables documented in `.env.example` (without values) | ⬜ |\n| Info | No unused imports or variables | ⬜ |\n| Info | No commented-out code blocks | ⬜ |\n| Info | No TODO/FIXME/HACK comments left in production code | ⬜ |\n\n### Code quality smoke test:\n- [ ] Run `npm run build \u0026\u0026 npm run lint` — both pass clean?\n- [ ] Search for console.log: `grep -r \"console.log\" --include=\"*.ts\" --include=\"*.tsx\" src/`\n- [ ] Search for ts-ignore: `grep -r \"ts-ignore\\|ts-expect-error\" --include=\"*.ts\" --include=\"*.tsx\"`\n- [ ] Search for TODOs: `grep -r \"TODO\\|FIXME\\|HACK\" --include=\"*.ts\" --include=\"*.tsx\"`\n\n---\n"])</script><script>self.__next_f.push([1,"34:[\"$\",\"div\",\"7-code-quality\",{\"id\":\"7-code-quality\",\"className\":\"mb-10 scroll-mt-24\",\"children\":[\"$\",\"div\",null,{\"className\":\"mb-6 rounded-xl border p-6 border-border/50 bg-card/30\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-4 flex items-center justify-between gap-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary\",\"children\":7}],[\"$\",\"h2\",null,{\"className\":\"text-xl font-bold tracking-tight\",\"children\":[false,\"Code Quality\"]}]]}],[\"$\",\"$L25\",null,{\"content\":\"$7f\"}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground\",\"children\":[[\"$\",\"p\",\"el-0\",{\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cem\u003eTechnical debt you ship on day 1 compounds forever.\u003c/em\u003e\"}}],[\"$\",\"div\",\"el-1\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Severity\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Check\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003enpm run build\u003c/code\u003e (or equivalent) passes with zero errors\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}],\"$L80\",\"$L81\",\"$L82\",\"$L83\",\"$L84\",\"$L85\",\"$L86\",\"$L87\",\"$L88\"]}]]}]}],\"$L89\",\"$L8a\"]}]]}]}]\n"])</script><script>self.__next_f.push([1,"35:[\"$\",\"div\",\"pre-deploy-checklist\",{\"id\":\"pre-deploy-checklist\",\"className\":\"mb-10 scroll-mt-24\",\"children\":[\"$\",\"div\",null,{\"className\":\"mb-6 rounded-xl border p-6 border-destructive/20 bg-destructive/5\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-4 flex items-center justify-between gap-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-destructive/20 text-destructive\",\"children\":8}],[\"$\",\"h2\",null,{\"className\":\"text-xl font-bold tracking-tight\",\"children\":[[\"$\",\"svg\",null,{\"ref\":\"$undefined\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"width\":24,\"height\":24,\"viewBox\":\"0 0 24 24\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":2,\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\",\"className\":\"lucide lucide-triangle-alert mr-2 inline-block size-5 text-destructive\",\"aria-hidden\":\"true\",\"children\":[[\"$\",\"path\",\"wmoenq\",{\"d\":\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"}],[\"$\",\"path\",\"juzpu7\",{\"d\":\"M12 9v4\"}],[\"$\",\"path\",\"p32p05\",{\"d\":\"M12 17h.01\"}],\"$undefined\"]}],\"Pre-Deploy Checklist\"]}]]}],[\"$\",\"$L25\",null,{\"content\":\"## Pre-Deploy Checklist\\n*Final checks before the deploy button.*\\n\\n| Severity | Check | Status |\\n|----------|-------|--------|\\n| Critical | All environment variables set in production (not just local) | ⬜ |\\n| Critical | Domain DNS configured and propagated | ⬜ |\\n| Critical | SSL certificate active (HTTPS working) | ⬜ |\\n| Critical | Stripe in live mode with live API keys | ⬜ |\\n| Critical | Auth provider configured for production domain (not localhost) | ⬜ |\\n| Warning | Webhook URLs point to production domain (not localhost or ngrok) | ⬜ |\\n| Warning | Error monitoring configured for production | ⬜ |\\n| Warning | Database backups configured (if self-hosted) | ⬜ |\\n| Info | Analytics/tracking installed (Plausible, PostHog, etc.) | ⬜ |\\n| Info | Custom error pages deployed (404, 500) | ⬜ |\\n\\n---\\n\"}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground\",\"children\":[[\"$\",\"p\",\"el-0\",{\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cem\u003eFinal checks before the deploy button.\u003c/em\u003e\"}}],[\"$\",\"div\",\"el-1\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Severity\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Check\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"All environment variables set in production (not just local)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}],\"$L8b\",\"$L8c\",\"$L8d\",\"$L8e\",\"$L8f\",\"$L90\",\"$L91\",\"$L92\",\"$L93\"]}]]}]}]]}]]}]}]\n"])</script><script>self.__next_f.push([1,"36:[\"$\",\"div\",\"action-items\",{\"id\":\"action-items\",\"className\":\"mb-10 scroll-mt-24\",\"children\":[\"$\",\"div\",null,{\"className\":\"mb-6 rounded-xl border p-6 border-border/50 bg-card/30\",\"children\":[[\"$\",\"div\",null,{\"className\":\"mb-4 flex items-center justify-between gap-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-sm font-bold bg-primary/10 text-primary\",\"children\":9}],[\"$\",\"h2\",null,{\"className\":\"text-xl font-bold tracking-tight\",\"children\":[false,\"Action Items\"]}]]}],[\"$\",\"$L25\",null,{\"content\":\"## Action Items\\n*Track every failed item here. Fix all Critical and Warning items before launch.*\\n\\n### Critical Fixes (must fix before launch)\\n\\n| Item | Section | Description | Status |\\n|------|---------|-------------|--------|\\n| | | | ⬜ |\\n| | | | ⬜ |\\n| | | | ⬜ |\\n\\n### Warning Fixes (should fix before launch)\\n\\n| Item | Section | Description | Status |\\n|------|---------|-------------|--------|\\n| | | | ⬜ |\\n| | | | ⬜ |\\n| | | | ⬜ |\\n\\n### Info Fixes (fix after launch)\\n\\n| Item | Section | Description | Status |\\n|------|---------|-------------|--------|\\n| | | | ⬜ |\\n| | | | ⬜ |\\n| | | | ⬜ |\\n\\n---\\n\\n*Want an AI to run these checks and generate a fix list? [Try Build a Startup →](https://buildastartup.ai)*\"}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-invert max-w-none prose-headings:font-bold prose-h3:text-base prose-h3:mt-6 prose-h3:mb-2 prose-p:text-muted-foreground prose-p:leading-relaxed prose-strong:text-foreground prose-code:rounded prose-code:bg-muted prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-pre:bg-muted/50 prose-pre:border prose-pre:border-border/50 prose-pre:rounded-lg prose-table:text-sm prose-th:text-left prose-th:font-semibold prose-th:px-3 prose-th:py-2 prose-td:border-t prose-td:border-border/30 prose-td:px-3 prose-td:py-2 prose-li:text-muted-foreground\",\"children\":[[\"$\",\"p\",\"el-0\",{\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cem\u003eTrack every failed item here. Fix all Critical and Warning items before launch.\u003c/em\u003e\"}}],[\"$\",\"h3\",\"el-1\",{\"children\":\"Critical Fixes (must fix before launch)\"}],[\"$\",\"div\",\"el-2\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Item\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Section\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Description\"}],[\"$\",\"th\",\"3\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"3\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}],[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],\"$L94\"]}],\"$L95\"]}]]}]}],\"$L96\",\"$L97\",\"$L98\",\"$L99\",\"$L9a\"]}]]}]}]\n"])</script><script>self.__next_f.push([1,"37:[\"$\",\"aside\",null,{\"className\":\"hidden xl:block\",\"children\":[\"$\",\"$L9b\",null,{\"sections\":[{\"id\":\"1-free-tier-experience\",\"label\":\"Free Tier Experience\",\"number\":1},{\"id\":\"2-authentication-authorization\",\"label\":\"Authentication \u0026 Authorization\",\"number\":2},{\"id\":\"3-payments-billing\",\"label\":\"Payments \u0026 Billing\",\"number\":3},{\"id\":\"4-security\",\"label\":\"Security\",\"number\":4},{\"id\":\"5-seo-performance\",\"label\":\"SEO \u0026 Performance\",\"number\":5},{\"id\":\"6-error-handling-ux\",\"label\":\"Error Handling \u0026 UX\",\"number\":6},{\"id\":\"7-code-quality\",\"label\":\"Code Quality\",\"number\":7},{\"id\":\"pre-deploy-checklist\",\"label\":\"Pre-Deploy Checklist\",\"number\":8},{\"id\":\"action-items\",\"label\":\"Action Items\",\"number\":9}]}]}]\n"])</script><script>self.__next_f.push([1,"39:[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]\n3a:[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Every resource access verifies ownership: \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003eresource.userId === currentUser._id\u003c/code\u003e\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n3b:[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Unauthenticated users cannot access any \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e/dashboard\u003c/code\u003e, \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e/project\u003c/code\u003e, or \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e/settings\u003c/code\u003e route\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n3c:[\"$\",\"tr\",\"3\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Users cannot access other users' data by modifying IDs in URLs or API calls\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n3d:[\"$\",\"tr\",\"4\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Sign-up flow works end-to-end (email + OAuth providers)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n3e:[\"$\",\"tr\",\"5\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Sign-in flow works end-to-end\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n3f:[\"$\",\"tr\",\"6\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Password reset / account recovery flow works\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n40:[\"$\",\"tr\",\"7\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Session expiry redirects to sign-in gracefully (no error page)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n41:[\"$\",\"tr\",\"8\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"User can delete their account and all associated data\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySe"])</script><script>self.__next_f.push([1,"tInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n42:[\"$\",\"h3\",\"el-2\",{\"children\":\"Auth smoke test:\"}]\n"])</script><script>self.__next_f.push([1,"43:[\"$\",\"ul\",\"el-3\",{\"className\":\"not-prose my-4 space-y-2\",\"children\":[[\"$\",\"li\",\"0\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Sign up with email, verify you can access dashboard\"}}]]}],[\"$\",\"li\",\"1\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Sign up with OAuth (Google, GitHub), verify user record created\"}}]]}],[\"$\",\"li\",\"2\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Open an incognito window, try to access \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e/dashboard\u003c/code\u003e directly — should redirect to sign-in\"}}]]}],[\"$\",\"li\",\"3\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Copy a project URL, open in incognito, sign in as a different user — should show \\\"not found\\\" or \\\"unauthorized\\\"\"}}]]}]]}]\n"])</script><script>self.__next_f.push([1,"45:[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]\n46:[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Checkout flow completes successfully with a real payment method\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n47:[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Webhook handler verifies Stripe signature before processing\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n48:[\"$\",\"tr\",\"3\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003echeckout.session.completed\u003c/code\u003e webhook updates user plan to \\\"pro\\\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n49:[\"$\",\"tr\",\"4\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003ecustomer.subscription.deleted\u003c/code\u003e webhook reverts user plan to \\\"free\\\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n4a:[\"$\",\"tr\",\"5\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003einvoice.paid\u003c/code\u003e webhook handles recurring payments correctly\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n4b:[\"$\",\"tr\",\"6\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003einvoice.payment_failed\u003c/code\u003e webhook handles failed payments (notify user)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n4c:[\"$\",\"tr\",\"7\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Customer portal link works — user can manage/cancel subscription\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n4d:[\"$\",\"tr\",\"8\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Duplicate webhook events are handled idempotently (no double-upgrades)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py"])</script><script>self.__next_f.push([1,"-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n4e:[\"$\",\"tr\",\"9\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Pricing page clearly shows what's included in each plan\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n4f:[\"$\",\"tr\",\"10\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Receipt emails are sent (Stripe handles this by default)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n50:[\"$\",\"h3\",\"el-2\",{\"children\":\"Payment smoke test:\"}]\n"])</script><script>self.__next_f.push([1,"51:[\"$\",\"ul\",\"el-3\",{\"className\":\"not-prose my-4 space-y-2\",\"children\":[[\"$\",\"li\",\"0\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Complete a real checkout (use Stripe's $0.50 test product if needed)\"}}]]}],[\"$\",\"li\",\"1\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Verify user plan updated in database after successful payment\"}}]]}],[\"$\",\"li\",\"2\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Cancel subscription via Stripe dashboard, verify plan reverted\"}}]]}],[\"$\",\"li\",\"3\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Check Stripe dashboard for webhook delivery — any failures?\"}}]]}]]}]\n"])</script><script>self.__next_f.push([1,"53:[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}]\n54:[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"All API keys and secrets are in environment variables, not in code\"}}]\n55:[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]\n56:[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e.env\u003c/code\u003e / \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e.env.local\u003c/code\u003e is in \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e.gitignore\u003c/code\u003e (not committed to repo)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n57:[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Stripe webhook signatures verified via \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003estripe.webhooks.constructEvent()\u003c/code\u003e\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n58:[\"$\",\"tr\",\"3\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Clerk/auth webhook signatures verified via svix or equivalent\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n59:[\"$\",\"tr\",\"4\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Every mutation validates input types with framework validators (Convex \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003ev\u003c/code\u003e, Zod, etc.)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n5a:[\"$\",\"tr\",\"5\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"No raw user input passed directly into AI system prompts\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n5b:[\"$\",\"tr\",\"6\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Rate limiting on AI/expensive operations: max ___ requests/minute/user\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n5c:[\"$\",\"tr\",\"7\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Rate limiting on auth endpoints: max ___ attempts/minute/IP\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n5d:[\"$\",\"tr\",\"8\",{\"className\":\"border"])</script><script>self.__next_f.push([1,"-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"CORS configured correctly (only allows your domain)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n5e:[\"$\",\"tr\",\"9\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"No sensitive data logged to console or error tracking\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n5f:[\"$\",\"tr\",\"10\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"File uploads (if any) validated for type and size\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n60:[\"$\",\"tr\",\"11\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Content Security Policy headers set\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n61:[\"$\",\"tr\",\"12\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"HTTPS enforced (HTTP redirects to HTTPS)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n62:[\"$\",\"h3\",\"el-2\",{\"children\":\"Security smoke test:\"}]\n"])</script><script>self.__next_f.push([1,"63:[\"$\",\"ul\",\"el-3\",{\"className\":\"not-prose my-4 space-y-2\",\"children\":[[\"$\",\"li\",\"0\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Search codebase for hardcoded API keys: \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003egrep -r \\\"sk_\\\" --include=\\\"*.ts\\\"\u003c/code\u003e\"}}]]}],[\"$\",\"li\",\"1\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Verify \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e.env\u003c/code\u003e is not in git history: \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003egit log --all --diff-filter=A -- .env\u003c/code\u003e\"}}]]}],[\"$\",\"li\",\"2\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Open browser dev tools → Network tab → verify no API keys in client-side requests\"}}]]}],[\"$\",\"li\",\"3\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Try submitting a form with malicious input (XSS payload, SQL injection) — verify it's sanitized\"}}]]}]]}]\n"])</script><script>self.__next_f.push([1,"65:[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]\n66:[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Every page has a unique \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e\u003cmeta name=\\\"description\\\"\u003e\u003c/code\u003e\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n67:[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Open Graph tags set for social sharing (\u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003eog:title\u003c/code\u003e, \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003eog:description\u003c/code\u003e, \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003eog:image\u003c/code\u003e)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n68:[\"$\",\"tr\",\"3\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Sitemap.xml exists, is valid, and lists all public pages\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n69:[\"$\",\"tr\",\"4\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Robots.txt exists and allows search engine crawling of public pages\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n6a:[\"$\",\"tr\",\"5\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Page load time under 3 seconds on mobile (test with PageSpeed Insights)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n6b:[\"$\",\"tr\",\"6\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"All pages are mobile responsive — test at 375px width\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n6c:[\"$\",\"tr\",\"7\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Images have descriptive \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003ealt\u003c/code\u003e text\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n6d:[\"$\",\"tr\",\"8\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Structured data (JSON-LD) added for key pages\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text"])</script><script>self.__next_f.push([1,"-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n6e:[\"$\",\"tr\",\"9\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Canonical URLs set to prevent duplicate content\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n6f:[\"$\",\"tr\",\"10\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"404 page returns proper 404 status code (not 200)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n70:[\"$\",\"h3\",\"el-2\",{\"children\":\"SEO smoke test:\"}]\n"])</script><script>self.__next_f.push([1,"71:[\"$\",\"ul\",\"el-3\",{\"className\":\"not-prose my-4 space-y-2\",\"children\":[[\"$\",\"li\",\"0\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Google \\\"site:yourdomain.com\\\" — are your pages indexed?\"}}]]}],[\"$\",\"li\",\"1\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Paste your URL into Twitter/LinkedIn — does the preview card look right?\"}}]]}],[\"$\",\"li\",\"2\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Run Google PageSpeed Insights on your landing page — score above 80?\"}}]]}],[\"$\",\"li\",\"3\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Check sitemap.xml in browser — does it list all public pages?\"}}]]}]]}]\n"])</script><script>self.__next_f.push([1,"73:[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]\n74:[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Loading skeletons shown during all data fetching (never a blank screen)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n75:[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Error messages are user-friendly (not raw stack traces or API errors)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n76:[\"$\",\"tr\",\"3\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Empty states shown when no data exists (not blank white space)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n77:[\"$\",\"tr\",\"4\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Form validation shows clear inline error messages\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n78:[\"$\",\"tr\",\"5\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Toast notifications confirm async actions (create, update, delete)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n79:[\"$\",\"tr\",\"6\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"404 page exists and matches the app's design\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n7a:[\"$\",\"tr\",\"7\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Undo/confirmation for destructive actions (delete project, cancel subscription)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n7b:[\"$\",\"tr\",\"8\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Keyboard navigation works for core workflows\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n7c:[\"$\",\"tr\",\"9\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"cl"])</script><script>self.__next_f.push([1,"assName\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Error monitoring service configured (Sentry, LogRocket, etc.)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n7d:[\"$\",\"h3\",\"el-2\",{\"children\":\"UX smoke test:\"}]\n"])</script><script>self.__next_f.push([1,"7e:[\"$\",\"ul\",\"el-3\",{\"className\":\"not-prose my-4 space-y-2\",\"children\":[[\"$\",\"li\",\"0\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Disable JavaScript — does the page show a reasonable fallback?\"}}]]}],[\"$\",\"li\",\"1\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Throttle network to \\\"Slow 3G\\\" in dev tools — do loading states appear?\"}}]]}],[\"$\",\"li\",\"2\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Submit every form with empty fields — do validation errors appear?\"}}]]}],[\"$\",\"li\",\"3\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Open the app on your phone — does everything fit?\"}}]]}]]}]\n"])</script><script>self.__next_f.push([1,"80:[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003enpm run lint\u003c/code\u003e passes with zero warnings\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n81:[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"No \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003econsole.log\u003c/code\u003e statements in production code\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n82:[\"$\",\"tr\",\"3\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"No hardcoded URLs — all URLs use environment variables or constants\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n83:[\"$\",\"tr\",\"4\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"No \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003eany\u003c/code\u003e types in TypeScript (use \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003eunknown\u003c/code\u003e and narrow)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n84:[\"$\",\"tr\",\"5\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"No \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e@ts-ignore\u003c/code\u003e or \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e@ts-expect-error\u003c/code\u003e comments\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n85:[\"$\",\"tr\",\"6\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Environment variables documented in \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003e.env.example\u003c/code\u003e (without values)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n86:[\"$\",\"tr\",\"7\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"No unused imports or variables\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n87:[\"$\",\"tr\",\"8\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"No commented-out code blocks\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n88:[\""])</script><script>self.__next_f.push([1,"$\",\"tr\",\"9\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"No TODO/FIXME/HACK comments left in production code\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n89:[\"$\",\"h3\",\"el-2\",{\"children\":\"Code quality smoke test:\"}]\n"])</script><script>self.__next_f.push([1,"8a:[\"$\",\"ul\",\"el-3\",{\"className\":\"not-prose my-4 space-y-2\",\"children\":[[\"$\",\"li\",\"0\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Run \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003enpm run build \u0026\u0026 npm run lint\u003c/code\u003e — both pass clean?\"}}]]}],[\"$\",\"li\",\"1\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Search for console.log: \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003egrep -r \\\"console.log\\\" --include=\\\"\u003cem\u003e.ts\\\" --include=\\\"\u003c/em\u003e.tsx\\\" src/\u003c/code\u003e\"}}]]}],[\"$\",\"li\",\"2\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Search for ts-ignore: \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003egrep -r \\\"ts-ignore\\\\|ts-expect-error\\\" --include=\\\"\u003cem\u003e.ts\\\" --include=\\\"\u003c/em\u003e.tsx\\\"\u003c/code\u003e\"}}]]}],[\"$\",\"li\",\"3\",{\"className\":\"flex items-start gap-3\",\"children\":[[\"$\",\"span\",null,{\"className\":\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded border text-xs border-border bg-muted text-muted-foreground\",\"children\":\"\"}],[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Search for TODOs: \u003ccode class=\\\"rounded bg-muted px-1.5 py-0.5 text-sm\\\"\u003egrep -r \\\"TODO\\\\|FIXME\\\\|HACK\\\" --include=\\\"\u003cem\u003e.ts\\\" --include=\\\"\u003c/em\u003e.tsx\\\"\u003c/code\u003e\"}}]]}]]}]\n"])</script><script>self.__next_f.push([1,"8b:[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Domain DNS configured and propagated\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n8c:[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"SSL certificate active (HTTPS working)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n8d:[\"$\",\"tr\",\"3\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Stripe in live mode with live API keys\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n8e:[\"$\",\"tr\",\"4\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Critical\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Auth provider configured for production domain (not localhost)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n8f:[\"$\",\"tr\",\"5\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Webhook URLs point to production domain (not localhost or ngrok)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n90:[\"$\",\"tr\",\"6\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Error monitoring configured for production\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n91:[\"$\",\"tr\",\"7\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Warning\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Database backups configured (if self-hosted)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n92:[\"$\",\"tr\",\"8\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Analytics/tracking installed (Plausible, PostHog, etc.)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n93:[\"$\",\"tr\",\"9\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Info\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"Custom error pages deployed (404, 500)\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerH"])</script><script>self.__next_f.push([1,"TML\":{\"__html\":\"⬜\"}}]]}]\n94:[\"$\",\"td\",\"3\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]\n95:[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"3\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]\n96:[\"$\",\"h3\",\"el-3\",{\"children\":\"Warning Fixes (should fix before launch)\"}]\n"])</script><script>self.__next_f.push([1,"97:[\"$\",\"div\",\"el-4\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Item\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Section\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Description\"}],[\"$\",\"th\",\"3\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"3\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}],[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"3\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}],[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"3\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]]}]]}]}]\n"])</script><script>self.__next_f.push([1,"98:[\"$\",\"h3\",\"el-5\",{\"children\":\"Info Fixes (fix after launch)\"}]\n"])</script><script>self.__next_f.push([1,"99:[\"$\",\"div\",\"el-6\",{\"className\":\"not-prose my-4 overflow-x-auto rounded-lg border border-border/50\",\"children\":[\"$\",\"table\",null,{\"className\":\"w-full text-sm\",\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"className\":\"border-b border-border/50 bg-muted/30\",\"children\":[[\"$\",\"th\",\"0\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Item\"}],[\"$\",\"th\",\"1\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Section\"}],[\"$\",\"th\",\"2\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Description\"}],[\"$\",\"th\",\"3\",{\"className\":\"px-4 py-2.5 text-left text-xs font-semibold text-foreground\",\"children\":\"Status\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",\"0\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"3\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}],[\"$\",\"tr\",\"1\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"3\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}],[\"$\",\"tr\",\"2\",{\"className\":\"border-b border-border/30 last:border-0\",\"children\":[[\"$\",\"td\",\"0\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"1\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"2\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\"}}],[\"$\",\"td\",\"3\",{\"className\":\"px-4 py-2.5 text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"⬜\"}}]]}]]}]]}]}]\n"])</script><script>self.__next_f.push([1,"9a:[\"$\",\"p\",\"el-7\",{\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cem\u003eWant an AI to run these checks and generate a fix list? \u003ca href=\\\"https://buildastartup.ai\\\" class=\\\"text-primary underline underline-offset-4 hover:text-primary/80\\\"\u003eTry Build a Startup →\u003c/a\u003e\u003c/em\u003e\"}}]\n"])</script><script>self.__next_f.push([1,"1a:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"9c:I[27201,[\"/_next/static/chunks/208d109d56c94dec.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\",\"/_next/static/chunks/9a13402b4e5995b7.js?dpl=dpl_5hdpwAXcnC5p5Nbvivz9yTV5BbYn\"],\"IconMark\"]\n18:null\n"])</script><script>self.__next_f.push([1,"1c:[[\"$\",\"title\",\"0\",{\"children\":\"Pre-Launch Review Checklist — Build a Startup | Build a Startup\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Free pre-launch review checklist for SaaS apps. Covers free tier experience, auth, payments, security, SEO, error handling, and code quality. Severity ratings, specific checks, and action item tracking. Don't launch without this.\"}],[\"$\",\"meta\",\"2\",{\"property\":\"og:title\",\"content\":\"Pre-Launch Review Checklist — Free Template\"}],[\"$\",\"meta\",\"3\",{\"property\":\"og:description\",\"content\":\"Free pre-launch review checklist for SaaS apps. Covers free tier experience, auth, payments, security, SEO, error handling, and code quality. Severity ratings, specific checks, and action item tracking. Don't launch without this.\"}],[\"$\",\"meta\",\"4\",{\"property\":\"og:type\",\"content\":\"article\"}],[\"$\",\"meta\",\"5\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"6\",{\"name\":\"twitter:title\",\"content\":\"Build a Startup — From Idea to Launch, or Import Your Vibe-Coded Mess\"}],[\"$\",\"meta\",\"7\",{\"name\":\"twitter:description\",\"content\":\"Build from an idea or import existing code. AI-guided workflow that generates PRDs, architecture docs, Claude Code prompts, and tech debt reports.\"}],[\"$\",\"meta\",\"8\",{\"name\":\"twitter:image\",\"content\":\"https://buildastartup.ai/opengraph-image\"}],[\"$\",\"link\",\"9\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0b3bf435.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L9c\",\"10\",{}]]\n"])</script></body></html>