Skill: alirezarezvani/cs-landing-page-generator (version 1.0.0)

--- name: "landing-page-generator" description: "Generates high-converting landing pages as complete Next.js/React (TSX) components with Tailwind CSS. Creates hero sections, feature grids, pricing tables, FAQ accordions, testimonial blocks, and CTA sections using proven copy frameworks (PAS, AIDA, BAB). Outputs SEO meta tags, structured data, and performance-optimised code targeting Core Web Vitals (LCP < 1s, CLS < 0.1). Use when the user asks to create a landing page, marketing page, homepage, single-page site, lead capture page, campaign page, promo page, or conversion-optimised web page — or when they want to A/B test landing page variants or replace a static page with one designed to convert." --- # Landing Page Generator Generate high-converting landing pages from a product description. Output complete Next.js/React components with multiple section variants, proven copy frameworks, SEO optimization, and performance-first patterns. Not lorem ipsum — actual copy that converts. **Target:** LCP < 1s · CLS < 0.1 · FID < 100ms **Output:** TSX components + Tailwind styles + SEO meta + copy variants --- ## Core Capabilities - 5 hero section variants (centered, split, gradient, video-bg, minimal) - Feature sections (grid, alternating, cards with icons) - Pricing tables (2–4 tiers with feature lists and toggle) - FAQ accordion with schema markup - Testimonials (grid, carousel, single-quote) - CTA sections (banner, full-page, inline) - Footer (simple, mega, minimal) - 4 design styles with Tailwind class sets --- ## Generation Workflow Follow these steps in order for every landing page request: 1. **Gather inputs** — collect product name, tagline, audience, pain point, key benefit, pricing tiers, design style, and copy framework using the trigger format below. Ask only for missing fields. 2. **Analyze brand voice** (recommended) — if the user has existing brand content (website copy, blog posts, marketing materials), run it through `marketing-skill/content-production/scripts/brand_voice_analyzer.py` to get a voice profile (formality, tone, perspective). Use the profile to inform design style and copy framework selection: - formal + professional → **enterprise** style, **AIDA** framework - casual + friendly → **bold-startup** style, **BAB** framework - professional + authoritative → **dark-saas** style, **PAS** framework - casual + conversational → **clean-minimal** style, **BAB** framework 3. **Select design style** — map the user's choice (or infer from brand voice analysis) to one of the four Tailwind class sets in the Design Style Reference. 4. **Apply copy framework** — write all headline and body copy using the chosen framework (PAS / AIDA / BAB) before generating components. Match the voice profile's formality and tone throughout. 5. **Generate sections in order** — Hero → Features → Pricing → FAQ → Testimonials → CTA → Footer. Skip sections not relevant to the product. 6. **Validate against SEO checklist** — run through every item in the SEO Checklist before outputting final code. Fix any gaps inline. 7. **Output final components** — deliver complete, copy-paste-ready TSX files with all Tailwind classes, SEO meta, and structured data included. --- ## Triggering This Skill ``` Product: [name] Tagline: [one sentence value prop] Target audience: [who they are] Key pain point: [what problem you solve] Key benefit: [primary outcome] Pricing tiers: [free/pro/enterprise or describe] Design style: dark-saas | clean-minimal | bold-startup | enterprise Copy framework: PAS | AIDA | BAB ``` --- ## Design Style Reference | Style | Background | Accent | Cards | CTA Button | |---|---|---|---|---| | **Dark SaaS** | `bg-gray-950 text-white` | `violet-500/400` | `bg-gray-900 border border-gray-800` | `bg-violet-600 hover:bg-violet-500` | | **Clean Minimal** | `bg-white text-gray-900` | `blue-600` | `bg-gray-50 border border-gray-200 rounded-2xl` | `bg-blue-600 hover:bg-blue-700` | | **Bold Startup** | `bg-white text-gray-900` | `orange-500` | `shadow-xl rounded-3xl` | `bg-orange-500 hover:bg-orange-600 text-white` | | **Enterprise** | `bg-slate-50 text-slate-900` | `slate-700` | `bg-white border border-slate-200 shadow-sm` | `bg-slate-900 hover:bg-slate-800 text-white` | > **Bold Startup** headings: add `font-black tracking-tight` to all `<h1>`/`<h2>` elements. --- ## Copy Frameworks **PAS (Problem → Agitate → Solution)** - H1: Painful state they're in - Sub: What happens if they don't fix it - CTA: What you offer - *Example — H1:* "Your team wastes 3 hours a day on manual reporting" / *Sub:* "Every hour spent on spreadsheets is an hour not closing deals. Your competitors are already automated." / *CTA:* "Automate your reports in 10 minutes →" **AIDA (Attention → Interest → Desire → Action)** - H1: Bold attention-grabbing statement → Sub: Interesting fact or benefit → Features: Desire-building proof points → CTA: Clear action **BAB (Before → After → Bridge)** - H1: "[Before state] → [After state]" → Sub: "Here's how [product] bridges the gap" → Features: How it works (the bridge) --- ## Representative Component: Hero (Centered Gradient — Dark SaaS) Use this as the structural template for all hero variants. Swap layout classes, gradient direction, and image placement for split, video-bg, and minimal variants. ```tsx export function HeroCentered() { return ( <section className="relative flex min-h-screen flex-col items-center justify-center overflow-hidden bg-gray-950 px-4 text-center"> <div className="absolute inset-0 bg-gradient-to-b from-violet-900/20 to-transparent" /> <div className="pointer-events-none absolute -top-40 left-1/2 h-[600px] w-[600px] -translate-x-1/2 rounded-full bg-violet-600/20 blur-3xl" /> <div className="relative z-10 max-w-4xl"> <div className="mb-6 inline-flex items-center gap-2 rounded-full border border-violet-500/30 bg-violet-500/10 px-4 py-1.5 text-sm text-violet-300"> <span className="h-1.5 w-1.5 rounded-full bg-violet-400" /> Now in public beta </div> <h1 className="mb-6 text-5xl font-bold tracking-tight text-white md:text-7xl"> Ship faster.<br /> <span className="bg-gradient-to-r from-violet-400 to-pink-400 bg-clip-text text-transparent"> Break less. </span> </h1> <p className="mx-auto mb-10 max-w-2xl text-xl text-gray-400"> The deployment platform that catches errors before your users do. Zero config. Instant rollbacks. Real-time monitoring. </p> <div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-center"> <Button size="lg" className="bg-violet-600 text-white hover:bg-violet-500 px-8"> Start free trial </Button> <Button size="lg" variant="outline" className="border-gray-700 text-gray-300"> See how it works → </Button> </div> <p className="mt-4 text-sm text-gray-500">No credit card required · 14-day free trial</p> </div> </section> ) } ``` --- ## Other Section Patterns ### Feature Section (Alternating) Map over a `features` array with `{ title, description, image, badge }`. Toggle layout direction with `i % 2 === 1 ? "lg:flex-row-reverse" : ""`. Use `<Image>` with explicit `width`/`height` and `rounded-2xl shadow-xl`. Wrap in `<section className="py-24">` with `max-w-6xl` container. ### Pricing Table Map over a `plans` array with `{ name, price, description, features[], cta, highlighted }`. Highlighted plan gets `border-2 border-violet-500 bg-violet-950/50 ring-4 ring-violet-500/20`; others get `border border-gray-800 bg-gray-900`. Render `null` price as "Custom". Use `<Check>` icon per feature row. Layout: `grid gap-8 lg:grid-cols-3`. ### FAQ with Schema Markup Inject `FAQPage` JSON-LD via `<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }} />` inside the section. Map FAQs with `{ q, a }` into shadcn `<Accordion>` with `type="single" collapsible`. Container: `max-w-3xl`. ### Testimonials, CTA, Footer - **Testimonials:** Grid (`grid-cols-1 md:grid-cols-3`) or single-quote hero block with avatar, name, role, and quote text. - **CTA Banner:** Full-width section with headline, subhead, and two buttons (primary + ghost). Add trust signals (money-back guarantee, logo strip) immediately below. - **Footer:** Logo + nav columns + social links + legal. Use `border-t border-gray-800` separator. --- ## SEO Checklist - [ ] `<title>` tag: primary keyword + brand (50–60 chars) - [ ] Meta description: benefit + CTA (150–160 chars) - [ ] OG image: 1200×630px with product name and tagline - [ ] H1: one per page, includes primary keyword - [ ] Structured data: FAQPage, Product, or Organization schema - [ ] Canonical URL set - [ ] Image alt text on all `<Image>` components - [ ] robots.txt and sitemap.xml configured - [ ] Core Web Vitals: LCP < 1s, CLS < 0.1 - [ ] Mobile viewport meta tag present - [ ] Internal linking to pricing and docs > **Validation step:** Before outputting final code, verify every checklist item above is satisfied. Fix any gaps inline — do not skip items. --- ## Performance Targets | Metric | Target | Technique | |---|---|---| | LCP | < 1s | Preload hero image, use `priority` on Next/Image | | CLS | < 0.1 | Set explicit width/height on all images | | FID/INP | < 100ms | Defer non-critical JS, use `loading="lazy"` | | TTFB | < 200ms | Use ISR or static generation for landing pages | | Bundle | < 100KB JS | Audit with `@next/bundle-analyzer` | --- ## Common Pitfalls - Hero image not preloaded — add `priority` prop to first `<Image>` - Missing mobile breakpoints — always design mobile-first with `sm:` prefixes - CTA copy too vague — "Get started" beats "Learn more"; "Start free trial" beats "Sign up" - Pricing page missing trust signals — add money-back guarantee and testimonials near CTA - No above-the-fold CTA on mobile — ensure button is visible without scrolling on 375px viewport --- ## Related Skills - **Brand Voice Analyzer** (`marketing-skill/content-production/scripts/brand_voice_analyzer.py`) — Run before generation to establish voice profile and ensure copy consistency - **UI Design System** (`product-team/ui-design-system/`) — Generate design tokens from brand color before building the page - **Competitive Teardown** (`product-team/competitive-teardown/`) — Competitive positioning informs landing page messaging and differentiation

path: references/conversion-patterns.md
content: # High-Converting Landing Page Patterns

## Overview

This reference catalogs proven landing page design patterns that drive higher conversion rates. Each pattern includes placement guidance, implementation notes, and A/B testing priorities.

## Hero Section Layouts

### Pattern 1: Left Copy + Right Product Screenshot
- **Best for:** SaaS products with a strong visual UI
- **Structure:** Headline, subheadline, CTA on left (60%); product screenshot on right (40%)
- **Why it works:** F-pattern reading leads with copy, product image provides proof
- **Conversion lift:** Baseline pattern, strong performer across industries

### Pattern 2: Centered Copy + Full-Width Background
- **Best for:** Brand-driven products, consumer apps
- **Structure:** Centered headline, subheadline, CTA over background image/gradient
- **Why it works:** Focuses attention on single message, high visual impact
- **Note:** Ensure text contrast against background for readability

### Pattern 3: Video Hero
- **Best for:** Complex products requiring demonstration
- **Structure:** Short headline + embedded video (60-90 seconds) + CTA below
- **Why it works:** Video explains what text cannot, increases time on page
- **Note:** Always include thumbnail; autoplay is often counterproductive

### Pattern 4: Interactive Demo
- **Best for:** Developer tools, data products, design tools
- **Structure:** Minimal copy + embedded interactive product experience
- **Why it works:** Hands-on experience converts better than description
- **Note:** Keep demo focused on one "aha moment" workflow

## Social Proof Placement

### Logo Bar
- **Position:** Immediately below hero section
- **Count:** 5-7 logos for credibility without clutter
- **Label:** "Trusted by" or "Used by teams at"
- **Selection:** Mix recognizable brands with relevant industry logos

### Testimonial Cards
- **Position:** After feature explanation sections
- **Format:** Photo + name + title + company + specific quote
- **Best quotes:** Include measurable outcomes ("Saved 10 hours/week")
- **Layout:** 2-3 testimonials in a row, carousel for more

### Case Study Callouts
- **Position:** Mid-page, before pricing
- **Format:** Company logo + headline metric + "Read the story" link
- **Example:** "Acme Corp reduced onboarding time by 60%"

### Social Proof Numbers
- **Position:** Near CTA or in dedicated trust section
- **Format:** Large number + descriptor (e.g., "50,000+ teams", "4.8/5 rating")
- **Selection:** Choose 3-4 most impressive metrics

## Pricing Table Designs

### Good/Better/Best (3-Tier)
- Most effective for SaaS with clear feature tiers
- Highlight recommended plan with visual emphasis
- Show annual discount prominently
- Include feature comparison matrix below

### Simple Two-Tier
- Free/Pro or Starter/Professional
- Best for PLG products with clear upgrade trigger
- Minimize decision fatigue

### Enterprise Custom
- Replace price with "Contact Sales" for high-ACV products
- List enterprise-specific features (SSO, SLA, dedicated support)
- Include a "Talk to Sales" CTA, not just a form

### Pricing Psychology
- Anchor with highest-priced plan first (or in the middle with visual highlight)
- Use monthly price with annual billing toggle
- Show savings percentage for annual plans
- Round prices ending in 9 (e.g., $49/mo, $99/mo)

## Trust Signals

### Security Badges
- SOC 2, ISO 27001, GDPR compliance badges
- SSL certificate indicator
- Place near forms and payment sections

### Guarantees
- Money-back guarantee with specific timeframe
- Free trial with no credit card requirement
- SLA uptime commitments

### Awards & Recognition
- Industry awards (best of, top rated)
- Analyst recognition (Gartner, Forrester, G2 Leader)
- Media mentions (as seen in logos)

### Real-Time Activity
- "X people signed up today" (use real data only)
- Recent activity feed
- Live user count

## Urgency Elements

### Ethical Urgency
- Limited-time pricing (with real deadline)
- Early adopter benefits (extra features, lower price)
- Cohort-based enrollment (actual capacity limits)

### Avoid
- Fake countdown timers that reset
- False scarcity ("only 3 left" when unlimited)
- Pressure tactics that erode trust

## Form Optimization

### Field Reduction
- Every additional field reduces conversion ~10%
- Start with email only, progressive profiling later
- Use single-column layouts for forms

### Smart Defaults
- Pre-fill country based on IP
- Auto-detect company from email domain
- Default to most popular plan

### Inline Validation
- Validate fields on blur, not on submit
- Show success states (green checkmark)
- Provide helpful error messages

### Multi-Step Forms
- Break long forms into 2-3 steps with progress indicator
- Put easiest questions first to build commitment
- Allow saving progress for complex forms

## Mobile-First Patterns

### Thumb-Friendly Design
- CTAs in thumb zone (bottom 40% of screen)
- Minimum tap target: 44x44px
- Adequate spacing between interactive elements

### Content Priority
- Lead with most compelling content (no scrolling to find CTA)
- Collapse secondary information into accordions
- Use sticky CTA bar on scroll

### Performance
- Target <3s load time on 3G
- Lazy-load images below fold
- Minimize JavaScript execution

## A/B Testing Priority Matrix

Test these elements in order of expected impact:

| Priority | Element | Expected Impact | Effort |
|----------|---------|----------------|--------|
| 1 | Headline | High | Low |
| 2 | CTA text and color | High | Low |
| 3 | Hero image/video | High | Medium |
| 4 | Social proof placement | Medium | Low |
| 5 | Form fields (fewer) | Medium | Low |
| 6 | Pricing presentation | Medium | Medium |
| 7 | Page length | Medium | High |
| 8 | Testimonial selection | Low | Low |
| 9 | Color scheme | Low | Medium |
| 10 | Font choices | Low | Low |

### Testing Best Practices
- Test one variable at a time for clear attribution
- Run tests for minimum 2 weeks or 1,000 visitors per variant
- Use 95% statistical significance threshold
- Document all test results for institutional knowledge
- Winner becomes new control for next test iteration


path: references/copy-frameworks.md
content: # Landing Page Copywriting Frameworks

## Overview

Four copy frameworks with worked SaaS examples you can adapt. Each framework includes a complete before/after example plus specific guidelines for each section.

## 1. AIDA Framework (Attention - Interest - Desire - Action)

The classic direct response formula, ideal for product landing pages.

**Example — Project management SaaS:**

> **Attention:** "Your Team Loses 12 Hours Every Sprint to Status Meetings"
>
> **Interest:** "Engineering teams at Series A-C startups spend 23% of their week in sync meetings — not writing code. We tracked 847 teams over 6 months. The pattern was clear: the more people in a standup, the less code shipped that day."
>
> **Desire:** "Teams using AsyncStand ship 31% more story points per sprint. No more 15-person standups where 13 people zone out. Replace your daily sync with a 2-minute async check-in that your engineers actually complete (94% response rate vs 67% attendance for live standups)."
>
> **Action:** "Start Your Free 14-Day Trial — No Credit Card Required"

### Attention
- Lead with a specific, quantified pain point (not vague claims)
- Weak: "Save time on meetings" → Strong: "Your Team Loses 12 Hours Every Sprint to Status Meetings"
- Keep headlines under 10 words for maximum impact

### Interest
- Back up the headline with specific data or a relatable scenario
- Weak: "Meetings waste time" → Strong: "We tracked 847 teams — the more people in standup, the less code shipped that day"
- Use their language: mirror words from customer reviews, support tickets, and G2 feedback

### Desire
- Stack measurable outcomes, not features
- Weak: "AI-powered async updates" → Strong: "31% more story points per sprint, 94% response rate"
- Compare directly to the status quo they already endure

### Action
- Single, clear CTA with action-oriented verb
- Reduce friction: "No credit card required," "Set up in 2 minutes"
- Repeat CTA after each major content block

## 2. PAS Framework (Problem - Agitate - Solution)

Best for pain-point-driven products where the problem is well understood.

**Example — Expense management tool:**

> **Problem:** "Your finance team is still chasing receipts in Slack DMs."
>
> **Agitate:** "Last quarter, your team spent 46 hours manually reconciling expenses across email threads, shared drives, and 'I'll submit it later' promises. That's $4,200 in payroll — spent on data entry. And when audit season hits? Good luck finding that client dinner receipt from February."
>
> **Solution:** "Snap a photo of the receipt. ExpenseFlow auto-extracts vendor, amount, and category in 3 seconds. Your monthly close drops from 5 days to 1. 2,400 finance teams already made the switch."

### Problem
- Name the exact scenario (not the abstract category)
- Weak: "Expense tracking is hard" → Strong: "Your finance team is still chasing receipts in Slack DMs"
- Mirror language from reviews and support tickets

### Agitate
- Quantify the cost in dollars, hours, or missed opportunities
- Weak: "This costs you money" → Strong: "46 hours last quarter, $4,200 in payroll — on data entry"
- Acknowledge the workarounds they've tried and why those fail too

### Solution
- Lead with the user action, not the technology: "Snap a photo" not "AI-powered OCR"
- Include one proof point: number of customers, time saved, or before/after metric
- Make the mechanism clear in one sentence: what happens when they use it

## 3. BAB Framework (Before - After - Bridge)

Ideal for aspirational products and lifestyle-oriented landing pages.

**Example — Sales enablement platform:**

> **Before:** "It's 9 PM. You're rebuilding a deck for tomorrow's demo because the prospect is in healthcare, not fintech. You copy-paste from three old decks, pray the logos are right, and rehearse the new talk track in the shower."
>
> **After:** "It's 9 AM. You type 'healthcare, 200-bed hospital, HIPAA-concerned CTO.' DeckGen builds your slides in 40 seconds — case studies, compliance badges, ROI calculator pre-loaded. You walk into the call with the best deck your prospect has ever seen."
>
> **Bridge:** "DeckGen connects to your CRM, learns your win patterns, and generates prospect-specific decks in under a minute. 340 AEs at companies like Stripe and Notion already use it. Start free — your first 5 decks are on us."

### Before
- Describe a specific, lived moment — not an abstract pain category
- Weak: "Sales decks take too long" → Strong: "It's 9 PM. You're rebuilding a deck for tomorrow's demo..."
- Use second person and present tense to make it feel immediate

### After
- Same level of specificity — show the transformed version of that exact moment
- Include a measurable outcome: "40 seconds," "best deck your prospect has ever seen"
- The after state should feel effortless compared to the before

### Bridge
- Name the product explicitly and explain the mechanism in one sentence
- Include one social proof data point
- End with a low-friction CTA that connects to the after state

## 4. 4Ps Framework (Promise - Picture - Proof - Push)

Strong for SaaS and B2B landing pages with measurable outcomes.

### Promise
- Make a clear, specific, believable promise
- Tie it to a measurable outcome
- Example: "Reduce customer churn by 25% in 90 days"

### Picture
- Help the reader visualize success
- Use scenarios they can relate to
- Show the product in context (screenshots, demos)

### Proof
- Back the promise with evidence
- Customer testimonials with specific results
- Case studies with before/after metrics
- Third-party validation (awards, analyst reports)

### Push
- Give a compelling reason to act now
- Limited-time offer, bonus, or guarantee
- Risk reversal (money-back guarantee, free trial)

## Headline Formulas

### Benefit-Driven
- "Get [Desired Outcome] Without [Common Objection]"
- "[Specific Result] in [Timeframe]"
- "The [Adjective] Way to [Achieve Goal]"

### Problem-Driven
- "Stop [Painful Activity]. Start [Better Alternative]."
- "Tired of [Problem]? There's a Better Way."
- "[Problem]? Not Anymore."

### Social Proof-Driven
- "[Number] Teams Trust [Product] to [Outcome]"
- "Why [Notable Company] Switched to [Product]"
- "Rated #1 for [Category] by [Authority]"

### Question-Driven
- "What If You Could [Desirable Outcome]?"
- "Ready to [Transformation]?"
- "Still [Painful Status Quo]?"

## CTA Best Practices

### Language
- Use first-person: "Start My Free Trial" > "Start Your Free Trial"
- Be specific: "Get My Report" > "Submit"
- Include benefit: "Start Saving Time" > "Sign Up"
- Add urgency naturally: "Start Free Today" > "Sign Up Now!!!"

### Placement
- Primary CTA above the fold
- Repeat after each major content section
- Sticky CTA on scroll (mobile especially)
- Exit-intent as last chance

### Design
- High contrast color (stands out from page palette)
- Sufficient whitespace around the button
- Large enough to tap on mobile (min 44x44px)
- Micro-copy below button to reduce anxiety ("No credit card required")

## Above-the-Fold Principles

The first viewport must accomplish these goals within 5 seconds:
1. **Communicate what you do** - Clear, jargon-free headline
2. **Show who it's for** - Audience identification
3. **Demonstrate value** - Primary benefit or outcome
4. **Provide next step** - Visible CTA button
5. **Build credibility** - One trust signal (logo bar, metric, badge)

### Above-the-Fold Checklist
- [ ] Headline states primary benefit (under 10 words)
- [ ] Subheadline adds specificity or addresses objection
- [ ] Hero image/video shows product in use
- [ ] CTA button is visible without scrolling
- [ ] At least one trust signal present
- [ ] No jargon or ambiguity in messaging


path: references/landing-page-patterns.md
content: # Landing Page Patterns

This reference captures high-converting page patterns and copy structures.

## Hero Section Patterns

### Pattern 1: Problem-Solution Hero
- Headline names the painful problem.
- Subheadline states the clear outcome.
- Primary CTA starts immediately.
- Optional supporting visual demonstrates product in context.

### Pattern 2: Outcome-First Hero
- Headline leads with measurable value.
- Subheadline clarifies who the page is for.
- CTA is action-oriented and specific.

### Pattern 3: Authority Hero
- Headline + trust indicator (logos, testimonial snippet, proof metric).
- Useful when category skepticism is high.

## Social Proof Layouts

### Logo Strip + Proof Metric
- Keep to recognizable logos.
- Add one proof metric (e.g., active users, revenue saved, hours reduced).

### Testimonial Grid
- 3-6 testimonials across segments.
- Include role/company where possible.
- Prefer concrete outcomes over generic praise.

### Case Study Snapshot
- Mini blocks: challenge -> approach -> measurable result.

## CTA Best Practices

- Use one dominant CTA per section.
- Match CTA verb to user intent ("Start trial", "Get demo", "Run audit").
- Keep CTA copy specific; avoid vague labels like "Submit".
- Reduce friction near CTA (short form, trust indicators, no surprise commitments).

## Above-the-Fold Checklist

- [ ] Clear value proposition in first viewport
- [ ] Audience clarity (who this is for)
- [ ] One primary CTA visible without scrolling
- [ ] Proof element (logos, stat, quote)
- [ ] Visual hierarchy emphasizes headline + CTA
- [ ] Mobile layout keeps CTA accessible

## Conversion-Optimized Templates

### SaaS Demo Page
1. Hero with problem-solution framing
2. Product walkthrough section
3. Social proof strip
4. Benefits by persona
5. Objection handling FAQ
6. Final CTA

### Lead Magnet Page
1. Promise + asset preview
2. Bullet outcomes
3. Short form
4. Trust/privacy note

### Product Launch Page
1. Outcome-first hero
2. Why now / differentiation
3. Feature blocks
4. Testimonials / beta feedback
5. Pricing or waitlist CTA

## Headline Formulas

### PAS (Problem-Agitate-Solution)
- Problem: identify the pain
- Agitate: show consequences of inaction
- Solution: position the offer as relief

Example structure:
"Still [problem]? Stop [negative consequence] and start [desired outcome]."

### AIDA (Attention-Interest-Desire-Action)
- Attention: pattern interrupt headline
- Interest: relevant context and stakes
- Desire: proof and benefits
- Action: concrete next step

### 4U Formula
- Useful: clear practical value
- Urgent: reason to act now
- Unique: differentiated promise
- Ultra-specific: concrete outcome and scope

Example structure:
"Get [specific result] in [timeframe] without [common pain]."


path: references/seo-checklist.md
content: # Landing Page SEO Checklist

## Overview

This checklist ensures landing pages are optimized for search engine visibility while maintaining conversion focus. Apply these checks before launching any landing page.

## Meta Tags

- [ ] **Title tag**: Under 60 characters, includes primary keyword, ends with brand name
- [ ] **Meta description**: 150-160 characters, includes CTA language, unique per page
- [ ] **Canonical URL**: Set to prevent duplicate content issues
- [ ] **Robots meta**: Ensure page is indexable (`index, follow`) unless intentionally noindex
- [ ] **Open Graph tags**: og:title, og:description, og:image, og:url for social sharing
- [ ] **Twitter Card tags**: twitter:card, twitter:title, twitter:description, twitter:image
- [ ] **Viewport meta**: `<meta name="viewport" content="width=device-width, initial-scale=1">`

## Structured Data

- [ ] **Organization schema**: Company name, logo, social profiles
- [ ] **Product schema**: Name, description, price, availability (for product pages)
- [ ] **FAQ schema**: For pages with FAQ sections (rich snippet opportunity)
- [ ] **Breadcrumb schema**: Navigation path for deep pages
- [ ] **Review schema**: Aggregate rating if testimonials present (use carefully per guidelines)
- [ ] **Validate**: Test all structured data with Google Rich Results Test

## Core Web Vitals Targets

### Largest Contentful Paint (LCP) - Target: < 2.5s
- [ ] Optimize hero image (WebP format, proper dimensions)
- [ ] Preload critical resources (`<link rel="preload">`)
- [ ] Use CDN for static assets
- [ ] Minimize render-blocking CSS and JavaScript

### First Input Delay (FID) / Interaction to Next Paint (INP) - Target: < 200ms
- [ ] Defer non-critical JavaScript
- [ ] Break up long tasks (>50ms)
- [ ] Minimize third-party script impact
- [ ] Use `requestAnimationFrame` for visual updates

### Cumulative Layout Shift (CLS) - Target: < 0.1
- [ ] Set explicit width/height on images and videos
- [ ] Reserve space for dynamic content (ads, embeds)
- [ ] Use `font-display: swap` for web fonts
- [ ] Avoid inserting content above existing content

## Keyword Placement

- [ ] **H1 tag**: Contains primary keyword, one per page only
- [ ] **H2 tags**: Include secondary keywords naturally
- [ ] **First paragraph**: Primary keyword appears in first 100 words
- [ ] **Body copy**: Natural keyword density (1-2%), no stuffing
- [ ] **Image alt text**: Descriptive, includes keyword where relevant
- [ ] **URL slug**: Short, keyword-rich, hyphen-separated
- [ ] **CTA text**: Consider keyword inclusion where natural

## Internal Linking

- [ ] Link to relevant product/feature pages
- [ ] Link to blog content that supports the page topic
- [ ] Use descriptive anchor text (not "click here")
- [ ] Ensure landing page is linked from main navigation or sitemap
- [ ] Link to pricing page if applicable
- [ ] Limit links to avoid diluting page authority (15-20 max)

## Image Optimization

- [ ] **Format**: Use WebP with JPEG/PNG fallback
- [ ] **Compression**: Lossy compression for photos, lossless for graphics
- [ ] **Dimensions**: Serve at exact display size (no CSS resizing)
- [ ] **Alt text**: Descriptive, 125 characters max, natural keyword inclusion
- [ ] **File names**: Descriptive, hyphenated (e.g., `product-dashboard-screenshot.webp`)
- [ ] **Lazy loading**: Apply to images below the fold (`loading="lazy"`)
- [ ] **Responsive images**: Use `srcset` for different viewport sizes

## Canonical URLs

- [ ] Self-referencing canonical on every page
- [ ] Consistent protocol (https) and trailing slash usage
- [ ] Canonical points to preferred URL version (www vs non-www)
- [ ] UTM parameters excluded from canonical URL
- [ ] Pagination handled with rel="next"/"prev" or single-page canonical

## Mobile Responsiveness

- [ ] **Mobile-friendly test**: Pass Google Mobile-Friendly Test
- [ ] **Touch targets**: Minimum 44x44px, 8px spacing between targets
- [ ] **Font size**: Minimum 16px base font, no pinch-to-zoom needed
- [ ] **Content parity**: All critical content accessible on mobile
- [ ] **Horizontal scroll**: None present at any viewport width
- [ ] **Form usability**: Appropriate input types (email, tel), autocomplete attributes
- [ ] **Media queries**: Breakpoints at 480px, 768px, 1024px, 1200px minimum

## Technical SEO

- [ ] **HTTPS**: SSL certificate valid and active
- [ ] **Page speed**: < 3s load time on mobile (test with PageSpeed Insights)
- [ ] **XML sitemap**: Page included in sitemap.xml
- [ ] **Robots.txt**: Page not blocked by robots.txt
- [ ] **404 handling**: Custom 404 page with navigation
- [ ] **Redirect chains**: No more than 1 redirect hop
- [ ] **Hreflang**: Set for multi-language landing pages

## Content Quality Signals

- [ ] **Unique content**: No duplicate content from other pages
- [ ] **Content depth**: Sufficient content for topic coverage (500+ words for SEO pages)
- [ ] **Readability**: Grade level 6-8 for broad audiences
- [ ] **Freshness**: Last modified date reflects recent updates
- [ ] **E-E-A-T signals**: Author expertise, company authority, trust indicators


path: scripts/landing_page_scaffolder.py
content: #!/usr/bin/env python3
"""Landing Page Scaffolder — Generate landing pages as HTML or Next.js TSX from config.

Creates production-ready landing pages with hero sections, features,
testimonials, pricing, CTAs, and responsive design.

Usage:
    python landing_page_scaffolder.py config.json --format html --output page.html
    python landing_page_scaffolder.py config.json --format tsx --output LandingPage.tsx
    python landing_page_scaffolder.py config.json --format json
"""

import argparse
import json
import sys
from typing import Dict, List, Any, Optional
from datetime import datetime
import html as html_module


def escape(text: str) -> str:
    """HTML-escape text."""
    return html_module.escape(str(text))


# ---------------------------------------------------------------------------
# Tailwind style mappings for TSX output
# ---------------------------------------------------------------------------

DESIGN_STYLES = {
    "dark-saas": {
        "bg": "bg-gray-950", "text": "text-white",
        "accent": "violet", "card_bg": "bg-gray-900 border border-gray-800",
        "btn": "bg-violet-600 hover:bg-violet-500 text-white",
        "btn_secondary": "border border-gray-700 text-gray-300 hover:bg-gray-800",
        "section_alt": "bg-gray-900/50", "muted": "text-gray-400",
        "border": "border-gray-800",
    },
    "clean-minimal": {
        "bg": "bg-white", "text": "text-gray-900",
        "accent": "blue", "card_bg": "bg-gray-50 border border-gray-200 rounded-2xl",
        "btn": "bg-blue-600 hover:bg-blue-700 text-white",
        "btn_secondary": "border border-gray-300 text-gray-700 hover:bg-gray-50",
        "section_alt": "bg-gray-50", "muted": "text-gray-500",
        "border": "border-gray-200",
    },
    "bold-startup": {
        "bg": "bg-white", "text": "text-gray-900",
        "accent": "orange", "card_bg": "shadow-xl rounded-3xl bg-white",
        "btn": "bg-orange-500 hover:bg-orange-600 text-white",
        "btn_secondary": "border-2 border-orange-500 text-orange-600 hover:bg-orange-50",
        "section_alt": "bg-orange-50/30", "muted": "text-gray-500",
        "border": "border-gray-200",
    },
    "enterprise": {
        "bg": "bg-slate-50", "text": "text-slate-900",
        "accent": "slate", "card_bg": "bg-white border border-slate-200 shadow-sm",
        "btn": "bg-slate-900 hover:bg-slate-800 text-white",
        "btn_secondary": "border border-slate-300 text-slate-700 hover:bg-slate-100",
        "section_alt": "bg-white", "muted": "text-slate-500",
        "border": "border-slate-200",
    },
}


# ---------------------------------------------------------------------------
# TSX generators
# ---------------------------------------------------------------------------

def tsx_nav(config: Dict[str, Any], style: Dict[str, str]) -> str:
    brand = config.get("brand", "Brand")
    nav_links = config.get("nav_links", [])
    cta = config.get("nav_cta", {"text": "Get Started", "url": "#"})
    links_jsx = "\n          ".join(
        f'<a href="{l.get("url", "#")}" className="{style["muted"]} hover:{style["text"]} font-medium transition-colors">{l.get("text", "")}</a>'
        for l in nav_links
    )
    return f'''function Navbar() {{
  return (
    <nav className="sticky top-0 z-50 {style["bg"]} border-b {style["border"]} backdrop-blur-sm">
      <div className="mx-auto flex max-w-7xl items-center justify-between px-6 py-4">
        <a href="#" className="text-xl font-bold {style["text"]}">{brand}</a>
        <div className="hidden items-center gap-8 md:flex">
          {links_jsx}
          <a href="{cta.get("url", "#")}" className="rounded-lg {style["btn"]} px-5 py-2.5 text-sm font-semibold transition-colors">
            {cta.get("text", "Get Started")}
          </a>
        </div>
      </div>
    </nav>
  );
}}'''


def tsx_hero(hero: Dict[str, Any], style: Dict[str, str]) -> str:
    h1 = hero.get("headline", "Your Headline Here")
    sub = hero.get("subheadline", "")
    primary_cta = hero.get("primary_cta", {"text": "Get Started", "url": "#"})
    secondary_cta = hero.get("secondary_cta", None)
    secondary_jsx = ""
    if secondary_cta:
        secondary_jsx = f'''
          <a href="{secondary_cta.get("url", "#")}" className="rounded-lg {style["btn_secondary"]} px-8 py-3 text-lg font-semibold transition-colors">
            {secondary_cta.get("text", "Learn More")}
          </a>'''
    return f'''function Hero() {{
  return (
    <section className="flex min-h-[80vh] flex-col items-center justify-center px-6 py-24 text-center {style["bg"]}">
      <div className="mx-auto max-w-4xl">
        <h1 className="mb-6 text-5xl font-bold tracking-tight {style["text"]} md:text-7xl">
          {h1}
        </h1>
        <p className="mx-auto mb-10 max-w-2xl text-xl {style["muted"]}">
          {sub}
        </p>
        <div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-center">
          <a href="{primary_cta.get("url", "#")}" className="rounded-lg {style["btn"]} px-8 py-3 text-lg font-semibold transition-colors">
            {primary_cta.get("text", "Get Started")}
          </a>{secondary_jsx}
        </div>
      </div>
    </section>
  );
}}'''


def tsx_features(features: Dict[str, Any], style: Dict[str, str]) -> str:
    title = features.get("title", "Features")
    subtitle = features.get("subtitle", "")
    items = features.get("items", [])
    cards_jsx = "\n        ".join(
        f'''<div className="{style["card_bg"]} rounded-xl p-8">
          <div className="mb-4 text-3xl">{f.get("icon", "")}</div>
          <h3 className="mb-3 text-xl font-semibold {style["text"]}">{f.get("title", "")}</h3>
          <p className="{style["muted"]}">{f.get("description", "")}</p>
        </div>'''
        for f in items
    )
    return f'''function Features() {{
  return (
    <section className="{style["section_alt"]} px-6 py-24">
      <div className="mx-auto max-w-7xl">
        <h2 className="mb-4 text-center text-4xl font-bold {style["text"]}">{title}</h2>
        <p className="mx-auto mb-16 max-w-2xl text-center text-lg {style["muted"]}">{subtitle}</p>
        <div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
        {cards_jsx}
        </div>
      </div>
    </section>
  );
}}'''


def tsx_testimonials(testimonials: Dict[str, Any], style: Dict[str, str]) -> str:
    title = testimonials.get("title", "What Our Customers Say")
    items = testimonials.get("items", [])
    if not items:
        return ""
    cards_jsx = "\n        ".join(
        f'''<div className="rounded-xl border {style["border"]} p-8">
          <p className="mb-6 text-lg italic {style["muted"]}">"{t.get("quote", "")}"</p>
          <div>
            <p className="font-semibold {style["text"]}">{t.get("name", "")}</p>
            <p className="text-sm {style["muted"]}">{t.get("title", "")}, {t.get("company", "")}</p>
          </div>
        </div>'''
        for t in items
    )
    return f'''function Testimonials() {{
  return (
    <section className="px-6 py-24 {style["bg"]}">
      <div className="mx-auto max-w-7xl">
        <h2 className="mb-16 text-center text-4xl font-bold {style["text"]}">{title}</h2>
        <div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
        {cards_jsx}
        </div>
      </div>
    </section>
  );
}}'''


def tsx_pricing(pricing: Dict[str, Any], style: Dict[str, str]) -> str:
    title = pricing.get("title", "Pricing")
    plans = pricing.get("plans", [])
    if not plans:
        return ""
    accent = style["accent"]
    cards = []
    for p in plans:
        featured = p.get("featured", False)
        border_cls = f"border-2 border-{accent}-500 ring-4 ring-{accent}-500/20" if featured else f"border {style['border']}"
        badge = f'\n            <div className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-{accent}-600 px-4 py-1 text-xs font-semibold text-white">Most Popular</div>' if featured else ""
        features_jsx = "\n              ".join(
            f'<li className="flex items-center gap-2 py-2"><span className="text-{accent}-500 font-bold">&#10003;</span> {feat}</li>'
            for feat in p.get("features", [])
        )
        cards.append(f'''<div className="relative rounded-2xl {border_cls} {style["card_bg"]} p-8 text-center">{badge}
            <h3 className="mb-2 text-xl font-semibold {style["text"]}">{p.get("name", "")}</h3>
            <div className="my-6 text-5xl font-extrabold {style["text"]}">${p.get("price", "0")}<span className="text-base font-normal {style["muted"]}">/mo</span></div>
            <p className="{style["muted"]} mb-6">{p.get("description", "")}</p>
            <ul className="mb-8 space-y-1 text-left {style["muted"]}">
              {features_jsx}
            </ul>
            <a href="{p.get("cta_url", "#")}" className="block w-full rounded-lg {style["btn"]} py-3 text-center font-semibold transition-colors">
              {p.get("cta_text", "Choose Plan")}
            </a>
          </div>''')
    cards_jsx = "\n        ".join(cards)
    return f'''function Pricing() {{
  return (
    <section className="{style["section_alt"]} px-6 py-24">
      <div className="mx-auto max-w-5xl">
        <h2 className="mb-16 text-center text-4xl font-bold {style["text"]}">{title}</h2>
        <div className="grid gap-8 lg:grid-cols-{min(len(plans), 3)}">
        {cards_jsx}
        </div>
      </div>
    </section>
  );
}}'''


def tsx_cta(cta: Dict[str, Any], style: Dict[str, str]) -> str:
    accent = style["accent"]
    return f'''function CTASection() {{
  return (
    <section className="bg-{accent}-600 px-6 py-24 text-center text-white">
      <div className="mx-auto max-w-3xl">
        <h2 className="mb-4 text-4xl font-bold">{cta.get("headline", "Ready to get started?")}</h2>
        <p className="mb-10 text-xl opacity-90">{cta.get("subheadline", "")}</p>
        <a href="{cta.get("url", "#")}" className="rounded-lg bg-white px-8 py-3 text-lg font-semibold text-{accent}-600 transition-colors hover:bg-gray-100">
          {cta.get("text", "Start Free Trial")}
        </a>
      </div>
    </section>
  );
}}'''


def tsx_footer(config: Dict[str, Any], style: Dict[str, str]) -> str:
    brand = config.get("brand", "Company")
    year = datetime.now().year
    footer_text = config.get("footer_text", f"{year} {brand}. All rights reserved.")
    return f'''function Footer() {{
  return (
    <footer className="border-t {style["border"]} {style["bg"]} px-6 py-10 text-center {style["muted"]}">
      <p>&copy; {footer_text}</p>
    </footer>
  );
}}'''


def generate_tsx(config: Dict[str, Any]) -> str:
    """Generate complete Next.js/React TSX landing page with Tailwind CSS."""
    style_name = config.get("design_style", "clean-minimal")
    style = DESIGN_STYLES.get(style_name, DESIGN_STYLES["clean-minimal"])

    components = []
    component_names = []

    components.append(tsx_nav(config, style))
    component_names.append("Navbar")

    if config.get("hero"):
        components.append(tsx_hero(config["hero"], style))
        component_names.append("Hero")

    if config.get("features"):
        components.append(tsx_features(config["features"], style))
        component_names.append("Features")

    if config.get("testimonials") and config["testimonials"].get("items"):
        components.append(tsx_testimonials(config["testimonials"], style))
        component_names.append("Testimonials")

    if config.get("pricing") and config["pricing"].get("plans"):
        components.append(tsx_pricing(config["pricing"], style))
        component_names.append("Pricing")

    if config.get("cta"):
        components.append(tsx_cta(config["cta"], style))
        component_names.append("CTASection")

    components.append(tsx_footer(config, style))
    component_names.append("Footer")

    title = config.get("title", "Landing Page")
    meta_desc = config.get("meta_description", "")

    page_body = "\n      ".join(f"<{name} />" for name in component_names)
    all_components = "\n\n".join(components)

    return f'''// Generated by Landing Page Scaffolder — {datetime.now().strftime("%Y-%m-%d")}
// Stack: Next.js 14+ App Router, React, Tailwind CSS
// Design style: {style_name}

import type {{ Metadata }} from "next";

export const metadata: Metadata = {{
  title: "{title}",
  description: "{meta_desc}",
  openGraph: {{
    title: "{title}",
    description: "{meta_desc}",
    type: "website",
  }},
}};

{all_components}

export default function LandingPage() {{
  return (
    <main>
      {page_body}
    </main>
  );
}}
'''


# ---------------------------------------------------------------------------
# HTML generators (existing)
# ---------------------------------------------------------------------------

def generate_css(config: Dict[str, Any]) -> str:
    """Generate responsive CSS from config theme."""
    theme = config.get("theme", {})
    primary = theme.get("primary_color", "#2563eb")
    secondary = theme.get("secondary_color", "#1e40af")
    bg = theme.get("background", "#ffffff")
    text_color = theme.get("text_color", "#1f2937")
    font = theme.get("font", "Inter, system-ui, -apple-system, sans-serif")

    return f"""
    * {{ margin: 0; padding: 0; box-sizing: border-box; }}
    body {{ font-family: {font}; color: {text_color}; background: {bg}; line-height: 1.6; }}
    .container {{ max-width: 1200px; margin: 0 auto; padding: 0 24px; }}
    nav {{ padding: 16px 0; border-bottom: 1px solid #e5e7eb; position: sticky; top: 0; background: {bg}; z-index: 100; }}
    nav .container {{ display: flex; justify-content: space-between; align-items: center; }}
    .nav-logo {{ font-size: 1.5rem; font-weight: 700; color: {primary}; text-decoration: none; }}
    .nav-links {{ display: flex; gap: 24px; list-style: none; }}
    .nav-links a {{ text-decoration: none; color: {text_color}; font-weight: 500; }}
    .nav-cta {{ background: {primary}; color: white; padding: 8px 20px; border-radius: 6px; text-decoration: none; font-weight: 600; }}
    .hero {{ padding: 80px 0; text-align: center; }}
    .hero h1 {{ font-size: 3.5rem; font-weight: 800; line-height: 1.1; margin-bottom: 24px; max-width: 800px; margin-left: auto; margin-right: auto; }}
    .hero p {{ font-size: 1.25rem; color: #6b7280; max-width: 600px; margin: 0 auto 32px; }}
    .hero-cta {{ display: inline-flex; gap: 16px; }}
    .btn-primary {{ background: {primary}; color: white; padding: 14px 32px; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 1.1rem; }}
    .btn-secondary {{ background: transparent; color: {primary}; padding: 14px 32px; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 1.1rem; border: 2px solid {primary}; }}
    .features {{ padding: 80px 0; background: #f9fafb; }}
    .section-title {{ text-align: center; font-size: 2.5rem; font-weight: 700; margin-bottom: 16px; }}
    .section-subtitle {{ text-align: center; color: #6b7280; font-size: 1.1rem; margin-bottom: 48px; max-width: 600px; margin-left: auto; margin-right: auto; }}
    .features-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px; }}
    .feature-card {{ background: white; padding: 32px; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }}
    .feature-icon {{ font-size: 2rem; margin-bottom: 16px; }}
    .feature-card h3 {{ font-size: 1.25rem; margin-bottom: 12px; }}
    .feature-card p {{ color: #6b7280; }}
    .testimonials {{ padding: 80px 0; }}
    .testimonials-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 24px; }}
    .testimonial-card {{ padding: 32px; border: 1px solid #e5e7eb; border-radius: 12px; }}
    .testimonial-text {{ font-size: 1.1rem; font-style: italic; margin-bottom: 20px; }}
    .testimonial-author {{ display: flex; align-items: center; gap: 12px; }}
    .author-info strong {{ display: block; }}
    .author-info span {{ color: #6b7280; font-size: 0.9rem; }}
    .pricing {{ padding: 80px 0; background: #f9fafb; }}
    .pricing-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px; max-width: 900px; margin: 0 auto; }}
    .pricing-card {{ background: white; padding: 32px; border-radius: 12px; border: 2px solid #e5e7eb; text-align: center; }}
    .pricing-card.featured {{ border-color: {primary}; position: relative; }}
    .pricing-card.featured::before {{ content: "Most Popular"; position: absolute; top: -12px; left: 50%; transform: translateX(-50%); background: {primary}; color: white; padding: 4px 16px; border-radius: 20px; font-size: 0.8rem; font-weight: 600; }}
    .pricing-name {{ font-size: 1.25rem; font-weight: 600; margin-bottom: 8px; }}
    .pricing-price {{ font-size: 3rem; font-weight: 800; margin: 16px 0; }}
    .pricing-price span {{ font-size: 1rem; font-weight: 400; color: #6b7280; }}
    .pricing-features {{ list-style: none; text-align: left; margin: 24px 0; }}
    .pricing-features li {{ padding: 8px 0; border-bottom: 1px solid #f3f4f6; }}
    .pricing-features li::before {{ content: "\\2713 "; color: {primary}; font-weight: 700; }}
    .cta-section {{ padding: 80px 0; text-align: center; background: {primary}; color: white; }}
    .cta-section h2 {{ font-size: 2.5rem; margin-bottom: 16px; }}
    .cta-section p {{ font-size: 1.1rem; opacity: 0.9; margin-bottom: 32px; }}
    .btn-white {{ background: white; color: {primary}; padding: 14px 32px; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 1.1rem; }}
    footer {{ padding: 40px 0; border-top: 1px solid #e5e7eb; color: #6b7280; text-align: center; }}
    @media (max-width: 768px) {{
        .hero h1 {{ font-size: 2.25rem; }}
        .hero-cta {{ flex-direction: column; align-items: center; }}
        .nav-links {{ display: none; }}
        .features-grid {{ grid-template-columns: 1fr; }}
        .pricing-grid {{ grid-template-columns: 1fr; }}
    }}
    """


def render_nav(config: Dict[str, Any]) -> str:
    brand = escape(config.get("brand", "Brand"))
    nav_links = config.get("nav_links", [])
    cta = config.get("nav_cta", {"text": "Get Started", "url": "#"})
    links = "\n".join(
        f'<li><a href="{escape(l.get("url", "#"))}">{escape(l.get("text", ""))}</a></li>'
        for l in nav_links
    )
    return f"""
    <nav><div class="container">
        <a href="#" class="nav-logo">{brand}</a>
        <ul class="nav-links">{links}</ul>
        <a href="{escape(cta.get('url', '#'))}" class="nav-cta">{escape(cta.get('text', 'Get Started'))}</a>
    </div></nav>"""


def render_hero(hero: Dict[str, Any]) -> str:
    h1 = escape(hero.get("headline", "Your Headline Here"))
    sub = escape(hero.get("subheadline", ""))
    primary_cta = hero.get("primary_cta", {"text": "Get Started", "url": "#"})
    secondary_cta = hero.get("secondary_cta", None)
    cta_html = f'<a href="{escape(primary_cta.get("url", "#"))}" class="btn-primary">{escape(primary_cta.get("text", "Get Started"))}</a>'
    if secondary_cta:
        cta_html += f'\n<a href="{escape(secondary_cta.get("url", "#"))}" class="btn-secondary">{escape(secondary_cta.get("text", "Learn More"))}</a>'
    return f"""
    <section class="hero"><div class="container">
        <h1>{h1}</h1>
        <p>{sub}</p>
        <div class="hero-cta">{cta_html}</div>
    </div></section>"""


def render_features(features: Dict[str, Any]) -> str:
    title = escape(features.get("title", "Features"))
    subtitle = escape(features.get("subtitle", ""))
    items = features.get("items", [])
    cards = "\n".join(f"""
        <div class="feature-card">
            <div class="feature-icon">{escape(f.get('icon', ''))}</div>
            <h3>{escape(f.get('title', ''))}</h3>
            <p>{escape(f.get('description', ''))}</p>
        </div>""" for f in items)
    return f"""
    <section class="features"><div class="container">
        <h2 class="section-title">{title}</h2>
        <p class="section-subtitle">{subtitle}</p>
        <div class="features-grid">{cards}</div>
    </div></section>"""


def render_testimonials(testimonials: Dict[str, Any]) -> str:
    title = escape(testimonials.get("title", "What Our Customers Say"))
    items = testimonials.get("items", [])
    if not items:
        return ""
    cards = "\n".join(f"""
        <div class="testimonial-card">
            <p class="testimonial-text">"{escape(t.get('quote', ''))}"</p>
            <div class="testimonial-author">
                <div class="author-info">
                    <strong>{escape(t.get('name', ''))}</strong>
                    <span>{escape(t.get('title', ''))}, {escape(t.get('company', ''))}</span>
                </div>
            </div>
        </div>""" for t in items)
    return f"""
    <section class="testimonials"><div class="container">
        <h2 class="section-title">{title}</h2>
        <div class="testimonials-grid">{cards}</div>
    </div></section>"""


def render_pricing(pricing: Dict[str, Any]) -> str:
    title = escape(pricing.get("title", "Pricing"))
    plans = pricing.get("plans", [])
    if not plans:
        return ""
    cards = "\n".join(f"""
        <div class="pricing-card {'featured' if p.get('featured') else ''}">
            <div class="pricing-name">{escape(p.get('name', ''))}</div>
            <div class="pricing-price">${escape(str(p.get('price', '0')))}<span>/mo</span></div>
            <p>{escape(p.get('description', ''))}</p>
            <ul class="pricing-features">
                {"".join(f'<li>{escape(f)}</li>' for f in p.get('features', []))}
            </ul>
            <a href="{escape(p.get('cta_url', '#'))}" class="btn-primary">{escape(p.get('cta_text', 'Choose Plan'))}</a>
        </div>""" for p in plans)
    return f"""
    <section class="pricing"><div class="container">
        <h2 class="section-title">{title}</h2>
        <div class="pricing-grid">{cards}</div>
    </div></section>"""


def render_cta(cta: Dict[str, Any]) -> str:
    return f"""
    <section class="cta-section"><div class="container">
        <h2>{escape(cta.get('headline', 'Ready to get started?'))}</h2>
        <p>{escape(cta.get('subheadline', ''))}</p>
        <a href="{escape(cta.get('url', '#'))}" class="btn-white">{escape(cta.get('text', 'Start Free Trial'))}</a>
    </div></section>"""


def generate_html(config: Dict[str, Any]) -> str:
    """Generate complete HTML landing page."""
    title = escape(config.get("title", "Landing Page"))
    css = generate_css(config)
    sections = []
    sections.append(render_nav(config))
    if config.get("hero"):
        sections.append(render_hero(config["hero"]))
    if config.get("features"):
        sections.append(render_features(config["features"]))
    if config.get("testimonials"):
        sections.append(render_testimonials(config["testimonials"]))
    if config.get("pricing"):
        sections.append(render_pricing(config["pricing"]))
    if config.get("cta"):
        sections.append(render_cta(config["cta"]))
    sections.append(f"""
    <footer><div class="container">
        <p>{escape(config.get('footer_text', f'{datetime.now().year} {config.get("brand", "Company")}. All rights reserved.'))}</p>
    </div></footer>""")
    return f"""<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{title}</title>
    <meta name="description" content="{escape(config.get('meta_description', ''))}">
    <style>{css}</style>
</head>
<body>
{"".join(sections)}
</body>
</html>"""


def main():
    parser = argparse.ArgumentParser(
        description="Generate landing pages as HTML or Next.js TSX with Tailwind CSS"
    )
    parser.add_argument("input", help="Path to page config JSON")
    parser.add_argument(
        "--format", choices=["html", "tsx", "json"], default="tsx",
        help="Output format: tsx (Next.js + Tailwind), html (standalone), json (metadata)"
    )
    parser.add_argument("--output", type=str, default=None, help="Output file path")

    args = parser.parse_args()

    with open(args.input) as f:
        config = json.load(f)

    if args.format == "json":
        output = json.dumps({
            "generated_at": datetime.now().isoformat(),
            "config": config,
            "formats_available": ["html", "tsx"],
            "sections": [k for k in ["nav", "hero", "features", "testimonials", "pricing", "cta", "footer"]
                         if config.get(k) or k in ("nav", "footer")]
        }, indent=2)
    elif args.format == "tsx":
        output = generate_tsx(config)
    else:
        output = generate_html(config)

    if args.output:
        with open(args.output, "w") as f:
            f.write(output)
        print(f"Landing page written to {args.output}")
    else:
        print(output)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)