Milestone 15: Marketing site
Purpose: Marketing and legal pages live; contact form stores submissions and sends email; SEO and analytics in place.
Exit state: Landing, pricing, contact (with DB + Postmark); legal pages (terms, privacy, impressum); sitemap, metadata, Plausible optional; blog stub.
Spec reference: §2.13 Marketing Website, §3.13 Internationalisation (i18n), §5.21b ContactSubmission, §6.1 Marketing routes, §9.12 Marketing env.
Prerequisites: M14. ContactSubmission table in M01; Postmark in M08.
15.1 Landing and pricing
Tasks
-
Landing page
- Route /. Section 2.13: Hero (headline, subhead, CTA "Start free trial", "See pricing"); Problem/Solution; Key features (3–4); How it works (3 steps); Pricing teaser; Social proof placeholder; Final CTA; Footer (Pricing, Contact, Legal, theme toggle). Static or SSG. Section 6.1. All copy via next-intl from
messages/en/marketing.json(Section 3.13, 2.13).
- Route /. Section 2.13: Hero (headline, subhead, CTA "Start free trial", "See pricing"); Problem/Solution; Key features (3–4); How it works (3 steps); Pricing teaser; Social proof placeholder; Final CTA; Footer (Pricing, Contact, Legal, theme toggle). Static or SSG. Section 6.1. All copy via next-intl from
-
Pricing page
- Route /pricing. Section 2.13: Plan comparison (Starter, Growth, Business, Enterprise); inboxes, features per plan; monthly/annual toggle; CTA per plan ("Start free trial" → /signup; Enterprise → /contact). FAQ section. Pricing content from config (TypeScript or JSON) for plan data; all user-facing labels and copy via next-intl (
messages/en/marketing.json). Section 6.10, 3.13.
- Route /pricing. Section 2.13: Plan comparison (Starter, Growth, Business, Enterprise); inboxes, features per plan; monthly/annual toggle; CTA per plan ("Start free trial" → /signup; Enterprise → /contact). FAQ section. Pricing content from config (TypeScript or JSON) for plan data; all user-facing labels and copy via next-intl (
-
Rendering
- Marketing route group (marketing); force static where possible (export const dynamic = 'force-static'). Section 2.13. Uses same next-intl setup as app (Section 2.13 table).
Acceptance criteria
| Criterion | Status |
|---|---|
| Landing and pricing render; CTAs link to /signup and /contact; no auth required. | Pending |
| Pricing content manageable (config file); annual/monthly toggle if applicable. | Pending |
Marketing copy (landing, pricing, contact) uses next-intl; strings in messages/en/marketing.json per Section 3.13. | Pending |
15.2 Contact form
Tasks
-
Form
- Route /contact. Fields: name, email, company (optional), subject (dropdown: General, Sales, Support, Partnership), message. Section 2.13. Validation: client (react-hook-form + Zod) and server (Route Handler or tRPC). Honeypot + rate limit (e.g. 3 per IP per hour). Section 2.13.
-
Submit
- Insert contact_submissions row (id, name, email, company, subject, message, ip_address hashed/truncated, created_at). Section 5.21b. Send email to CONTACT_EMAIL via Postmark (template or simple text). Success: inline confirmation; no redirect. Section 2.13.
-
Env
- CONTACT_EMAIL required; document in Section 9.12.
Acceptance criteria
| Criterion | Status |
|---|---|
| Form validates and submits; row in DB; email received at CONTACT_EMAIL. | Pending |
| Rate limit and honeypot prevent abuse; success message shown. | Pending |
| No enumeration (same message for success). | Pending |
15.3 Legal pages
Tasks
-
Routes
- /legal/terms (AGB), /legal/privacy (Datenschutz), /legal/impressum (Imprint). Section 2.13. Content from MDX in content/legal/ (or similar). Shared LegalLayout: typography, ToC for long pages, last-updated. Section 2.13.
-
Content
- Placeholder or lawyer-reviewed copy; mark [LEGAL REVIEW REQUIRED] if placeholder. For DE/AT/CH: Impressum and Datenschutz in German; use next-intl with default locale or locale-based routes so
/legal/impressumcan serve German content (Section 2.13). Document chosen approach (single locale vs locale-based legal pages).
- Placeholder or lawyer-reviewed copy; mark [LEGAL REVIEW REQUIRED] if placeholder. For DE/AT/CH: Impressum and Datenschutz in German; use next-intl with default locale or locale-based routes so
-
404
- Custom not-found page with link back to home; Section 2.13.
Acceptance criteria
| Criterion | Status |
|---|---|
| Legal routes render; content editable via MDX; layout consistent. | Pending |
| 404 page exists and is linked from marketing. | Pending |
15.4 SEO and analytics
Tasks
-
Metadata
- generateMetadata per page (title, description); OpenGraph where applicable. Section 2.13.
-
Sitemap
- /sitemap.xml auto-generated (Next.js or custom); include /, /pricing, /contact, /legal/*. Section 2.13.
-
robots.txt
- Allow all except /app/** and /api/** (or appropriate). Section 2.13.
-
Plausible
- Optional: NEXT_PUBLIC_PLAUSIBLE_DOMAIN; inject script in marketing layout only; no cookie banner for strict necessity. Section 2.13, 9.12.
Acceptance criteria
| Criterion | Status |
|---|---|
| Metadata and sitemap present; robots.txt disallows app and API. | Pending |
| Plausible loads when env set; no cookie banner (or document if needed). | Pending |
15.5 Blog stub
Tasks
- Route
- /blog: "Coming soon" or placeholder. Section 2.13. Structure in place for future articles.
Acceptance criteria
| Criterion | Status |
|---|---|
| /blog returns 200 with coming soon content. | Pending |
| Ready for M16 (Polish and observability). | Pending |
Milestone 15 sign-off
| Criterion | Status |
|---|---|
| All tasks in 15.1–15.5 complete. | Pending |
| All acceptance criteria met. | Pending |
| Marketing site and contact form production-ready; legal and SEO in place. | Pending |
| E2E: Landing, pricing, contact form, legal pages (see INDEX — Testing strategy). | Pending |
| Ready for M16 (Polish and observability). | Pending |