# Front marketing SEO Insight — Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Transformer le template Agenforce en front marketing SEO Insight bilingue (FR/EN), réorganisé en 11 sections selon le brief, avec pricing 4 tiers, en gardant le design du template.

**Architecture:** i18n natif Next.js 16 (`app/[lang]/` + dictionnaires JSON server-only + `proxy.ts`). Tout le copy vit dans `fr.json`/`en.json` (source de vérité unique). Les composants serveur lisent le dict et passent les strings aux composants client via props. Pas de lib i18n.

**Tech Stack:** Next.js 16.2.9 (App Router), React 19.2.7, TypeScript 5, Tailwind v4, motion, shadcn, `next/font`.

**Convention de vérification :** le projet n'a pas de harnais de tests unitaires (et un site marketing présentationnel n'en justifie pas — YAGNI). La vérification de chaque tâche = `bun run build` (typecheck + compile) + `browser_eval` pour le rendu. Les seules logiques testables (détection locale, chargement dict, calcul prix) sont vérifiées par assertion inline ou rendu navigateur.

**Pré-requis utilisateur (bloque la Phase 4 & 5) :**
- Télécharger 5 blocs Aceternity → déposer dans `components/_downloaded/` : `problem.tsx`, `pipeline.tsx`, `mcp.tsx`, `pricing-4col.tsx`, `cta.tsx`.
- Fournir le logo SVG SEO Insight → `public/brands/seo-insight.svg` (+ favicon `app/favicon.ico`).
- Les screenshots/logos sources sont produits plus tard ; placeholders = les 2 webp existants.

**Git :** travail sur la branche `feature/marketing-front-seo-insight` (déjà créée). Commits Conventional Commits après chaque tâche.

---

## File Structure

```
proxy.ts                                   # NEW — détection locale + redirect
app/
  globals.css                              # unchanged (importé via ../globals.css)
  favicon.ico                              # remplacé par le favicon SEO Insight
  [lang]/                                  # NEW segment
    layout.tsx                             # NEW root layout (html lang dynamique, fonts, theme, generateStaticParams)
    dictionaries.ts                        # NEW getDictionary + hasLocale + types
    dictionaries/
      fr.json                              # NEW copy FR (source de vérité)
      en.json                              # NEW copy EN
    sitemap.ts                             # NEW
    robots.ts                              # NEW
    (marketing)/
      layout.tsx                           # MOVED — Navbar+Footer reçoivent dict
      page.tsx                             # MOVED — réordonné 11 sections
      pricing/page.tsx                     # MOVED
    (auth)/
      layout.tsx                           # MOVED
      login/page.tsx                       # MOVED
      signup/page.tsx                      # MOVED
lib/
  pricing.ts                               # NEW data plans + add-ons
  i18n.ts                                  # NEW constantes locales
components/
  logo.tsx                                 # MODIFY — SEO Insight
  navbar.tsx                               # MODIFY — dict + libellés
  footer.tsx                               # MODIFY — rebrand + dict
  hero.tsx                                 # MODIFY — dict
  logo-cloud.tsx                           # MODIFY — logos sources
  features/index.tsx                       # MODIFY — 3 modules
  features/shared.tsx                      # NEW — Card* factorisés
  features-secondary/index.tsx             # MODIFY — multi-source+GSC + import shared
  features-tertiary/index.tsx              # MODIFY — multi-org + import shared
  faqs.tsx                                 # MODIFY — dict
  mode-toggle.tsx                          # MODIFY — retirer console.log
  problem/index.tsx                        # NEW (intègre bloc téléchargé)
  pipeline/index.tsx                       # NEW
  mcp/index.tsx                            # NEW
  pricing.tsx                              # REWRITE — 4 tiers + toggle
  cta/index.tsx                            # NEW
  json-ld.tsx                              # NEW
next.config.ts                             # MODIFY — retirer remotePatterns en fin
```

---

# PHASE 1 — Infrastructure i18n

### Task 1: Constantes locales + dictionnaires

**Files:**
- Create: `lib/i18n.ts`
- Create: `app/[lang]/dictionaries.ts`
- Create: `app/[lang]/dictionaries/fr.json`
- Create: `app/[lang]/dictionaries/en.json`

- [ ] **Step 1: Créer `lib/i18n.ts`**

```ts
export const locales = ["fr", "en"] as const;
export type Locale = (typeof locales)[number];
export const defaultLocale: Locale = "fr";
```

- [ ] **Step 2: Créer `app/[lang]/dictionaries.ts`**

```ts
import "server-only";
import type { Locale } from "@/lib/i18n";

const dictionaries = {
  fr: () => import("./dictionaries/fr.json").then((m) => m.default),
  en: () => import("./dictionaries/en.json").then((m) => m.default),
};

export const hasLocale = (lang: string): lang is Locale => lang in dictionaries;

export const getDictionary = async (lang: Locale) => dictionaries[lang]();

export type Dictionary = Awaited<ReturnType<typeof getDictionary>>;
```

- [ ] **Step 3: Créer `app/[lang]/dictionaries/fr.json`** (source de vérité du copy)

```json
{
  "nav": {
    "features": "Fonctionnalités",
    "modules": "Modules",
    "pricing": "Tarifs",
    "faqs": "FAQ",
    "login": "Connexion",
    "signup": "Inscription"
  },
  "hero": {
    "title_line1": "Tout votre SEO, du mot-clé à la publication",
    "title_line2": "et prêt pour vos agents IA.",
    "subtitle": "Réservoir de mots-clés multi-source, backlinks, pipeline de contenu et serveur MCP intégré. Une seule plateforme, multi-sites, multi-équipes.",
    "cta_primary": "Démarrer l'essai gratuit",
    "cta_secondary": "Voir les forfaits",
    "trial_note": "14 jours, sans carte bancaire"
  },
  "logocloud": {
    "title": "Importez vos données depuis vos outils SEO",
    "subtitle": "Semrush, Ahrefs, Mangools, SE Ranking, Google Search Console."
  },
  "problem": {
    "title": "Votre stack SEO est éclatée en 5 outils.",
    "subtitle": "Jongler entre des SaaS qui ne se parlent pas vous coûte du temps et de la donnée propre.",
    "points": [
      { "title": "5 abonnements, 0 cohérence", "description": "Mots-clés ici, backlinks là, contenu ailleurs — rien n'est relié." },
      { "title": "Données écrasées", "description": "Vos imports s'écrasent sans garder ni la source ni la date de mise à jour." },
      { "title": "Invisibles pour vos agents IA", "description": "Aucun de ces outils n'expose sa donnée à Claude ou ChatGPT." },
      { "title": "Reporting fragmenté", "description": "Recoller les performances entre outils est un travail manuel sans fin." }
    ]
  },
  "pipeline": {
    "title": "Un seul pipeline, du mot-clé à l'article publié.",
    "subtitle": "SEO Insight couvre toute la chaîne dans un seul produit, multi-sites et multi-organisations.",
    "steps": [
      { "title": "Réservoir", "description": "Tous vos mots-clés, multi-source, par date de MAJ." },
      { "title": "Recherche", "description": "Position GSC, quick-wins, couverture par pays." },
      { "title": "Planning", "description": "Chambre de briefing : SERP, on-page, briefs." },
      { "title": "Dispatch", "description": "Validation avant production, par pays." },
      { "title": "Contenu", "description": "Pipeline Kanban Idée → Brief → Rédaction → Publié." },
      { "title": "En ligne", "description": "Perf GSC : clics, impressions, position, CTR." }
    ]
  },
  "modules": {
    "title": "Tout le pipeline SEO, un seul outil",
    "subtitle": "Mots-clés, backlinks et contenu reliés — plus de jonglage entre 5 SaaS.",
    "keywords": {
      "title": "Keywords — Réservoir multi-source",
      "description": "Volume, KD, CPC, tendances 12 mois, intent, quick-wins GSC (positions 11–20)."
    },
    "backlink": {
      "title": "Backlink — Sources curées",
      "description": "~380 sources curées, profil entrant, DA, dofollow, import par fournisseur."
    },
    "content": {
      "title": "Contenu — Pipeline éditorial",
      "description": "Kanban Idée → Brief → Rédaction → Relecture → Publié, briefs par pays."
    }
  },
  "mcp": {
    "title": "Branchez Claude et vos agents sur votre donnée SEO.",
    "subtitle": "Serveur MCP intégré : votre donnée SEO accessible à Claude, ChatGPT et tout client MCP. Quota mensuel par forfait, packs de crédits, clés API.",
    "bullets": [
      "Serveur MCP intégré, natif",
      "Quota de requêtes MCP par forfait",
      "Packs de crédits à l'achat",
      "Clés API (Paramètres → API Keys)"
    ]
  },
  "multisource": {
    "card_source_title": "Multi-source par date de MAJ",
    "card_source_description": "Chaque métrique garde sa source et sa date de mise à jour — pas d'écrasement aveugle. Bascule de vue par source.",
    "card_gsc_title": "Google Search Console intégré",
    "card_gsc_description": "Connexion par propriété, quick-wins, matrice de couverture par pays.",
    "items": [
      { "title": "Import multi-fournisseurs", "description": "Semrush, Ahrefs, Mangools, SE Ranking, GSC." },
      { "title": "Performance Rust", "description": "Parsers CSV/HTML Rust→WASM, import lourd Bun + DuckDB." },
      { "title": "Sparklines par source", "description": "Tendances 12 mois conservées par fournisseur." }
    ]
  },
  "team": {
    "items": [
      { "title": "Multi-organisations", "description": "Gérez plusieurs orgs depuis un seul compte." },
      { "title": "Rôles", "description": "Owner, admin, membre — accès contrôlé." },
      { "title": "Invitations d'équipe", "description": "Invitez vos collègues et clients en quelques clics." },
      { "title": "Sièges extensibles", "description": "Ajoutez des sièges sur Pro et Agency à 19 $/mo." }
    ]
  },
  "pricing": {
    "eyebrow": "Tarifs",
    "title": "Tarification simple et transparente",
    "subtitle": "Essai 14 jours, sans carte bancaire. Annuel : −20 % minimum vs mensuel.",
    "toggle_monthly": "Mensuel",
    "toggle_yearly": "Annuel",
    "yearly_badge": "−20 %",
    "per_month": "/mo",
    "popular": "Le plus populaire",
    "cta": "Démarrer l'essai gratuit",
    "feature_labels": {
      "keywords": "Mots-clés",
      "projects": "Projets (sites)",
      "seats": "Sièges inclus",
      "integrations": "Intégrations",
      "backlinks": "Lignes backlink",
      "mcp": "Requêtes MCP / mois"
    },
    "addons_title": "Add-ons",
    "addons": [
      "Siège supplémentaire (Pro & Agency) : 19 $/mois (≈ 15,20 $/mo en annuel).",
      "Pack de crédits MCP : 25 $ pour 25 000 requêtes (ponctuel, cumulable)."
    ],
    "unlimited": "illimité"
  },
  "faqs": {
    "title": "Questions fréquentes",
    "items": [
      { "question": "Y a-t-il un essai gratuit ?", "answer": "Oui, 14 jours gratuits sans carte bancaire. Les limites de l'essai correspondent à Pro divisé par deux (750 mots-clés, 50 000 requêtes MCP)." },
      { "question": "Puis-je ajouter des sièges ?", "answer": "Oui, sur les forfaits Pro et Agency : 19 $/mois par siège supplémentaire (≈ 15,20 $/mo en annuel)." },
      { "question": "Comment fonctionnent les packs de crédits MCP ?", "answer": "Chaque forfait inclut un quota mensuel de requêtes MCP. Vous pouvez acheter des packs ponctuels et cumulables : 25 $ pour 25 000 requêtes." },
      { "question": "Quelles intégrations sont disponibles ?", "answer": "Import multi-fournisseurs : Semrush, Ahrefs, Mangools, SE Ranking et Google Search Console. Le nombre d'intégrations dépend du forfait (illimité en Agency)." },
      { "question": "Puis-je annuler à tout moment ?", "answer": "Oui, l'abonnement est sans engagement et résiliable depuis le portail client Stripe." }
    ]
  },
  "cta": {
    "title": "Démarrez votre essai gratuit",
    "subtitle": "Tout votre SEO, du mot-clé à la publication — et prêt pour vos agents IA.",
    "button": "Démarrer l'essai gratuit",
    "note": "14 jours, sans carte bancaire"
  },
  "footer": {
    "tagline": "La plateforme SEO tout-en-un, multi-source et prête pour les agents IA.",
    "cta": "Démarrer l'essai gratuit",
    "product": "Produit",
    "company": "Société",
    "newsletter": "Newsletter",
    "newsletter_note": "Les nouveautés produit et les coulisses, sans spam.",
    "email_placeholder": "Votre email",
    "rights": "Tous droits réservés.",
    "privacy": "Confidentialité",
    "terms": "Conditions"
  }
}
```

- [ ] **Step 4: Créer `app/[lang]/dictionaries/en.json`** (même structure, traduction EN)

```json
{
  "nav": {
    "features": "Features",
    "modules": "Modules",
    "pricing": "Pricing",
    "faqs": "FAQ",
    "login": "Login",
    "signup": "Sign up"
  },
  "hero": {
    "title_line1": "All your SEO, from keyword to publish",
    "title_line2": "and ready for your AI agents.",
    "subtitle": "Multi-source keyword reservoir, backlinks, content pipeline and a built-in MCP server. One platform, multi-site, multi-team.",
    "cta_primary": "Start your free trial",
    "cta_secondary": "See pricing",
    "trial_note": "14 days, no credit card"
  },
  "logocloud": {
    "title": "Import your data from your SEO tools",
    "subtitle": "Semrush, Ahrefs, Mangools, SE Ranking, Google Search Console."
  },
  "problem": {
    "title": "Your SEO stack is scattered across 5 tools.",
    "subtitle": "Juggling SaaS that don't talk to each other costs you time and clean data.",
    "points": [
      { "title": "5 subscriptions, 0 cohesion", "description": "Keywords here, backlinks there, content elsewhere — nothing is connected." },
      { "title": "Overwritten data", "description": "Your imports overwrite each other without keeping source or update date." },
      { "title": "Invisible to your AI agents", "description": "None of these tools expose their data to Claude or ChatGPT." },
      { "title": "Fragmented reporting", "description": "Stitching performance across tools is endless manual work." }
    ]
  },
  "pipeline": {
    "title": "One pipeline, from keyword to published article.",
    "subtitle": "SEO Insight covers the whole chain in a single product, multi-site and multi-organization.",
    "steps": [
      { "title": "Reservoir", "description": "All your keywords, multi-source, by update date." },
      { "title": "Research", "description": "GSC position, quick-wins, coverage by country." },
      { "title": "Planning", "description": "Briefing room: SERP, on-page, briefs." },
      { "title": "Dispatch", "description": "Validation before production, by country." },
      { "title": "Content", "description": "Kanban pipeline Idea → Brief → Writing → Published." },
      { "title": "Online", "description": "GSC perf: clicks, impressions, position, CTR." }
    ]
  },
  "modules": {
    "title": "The whole SEO pipeline, one tool",
    "subtitle": "Keywords, backlinks and content connected — no more juggling 5 SaaS.",
    "keywords": {
      "title": "Keywords — Multi-source reservoir",
      "description": "Volume, KD, CPC, 12-month trends, intent, GSC quick-wins (positions 11–20)."
    },
    "backlink": {
      "title": "Backlink — Curated sources",
      "description": "~380 curated sources, inbound profile, DA, dofollow, import per provider."
    },
    "content": {
      "title": "Content — Editorial pipeline",
      "description": "Kanban Idea → Brief → Writing → Review → Published, briefs per country."
    }
  },
  "mcp": {
    "title": "Plug Claude and your agents into your SEO data.",
    "subtitle": "Built-in MCP server: your SEO data accessible to Claude, ChatGPT and any MCP client. Monthly quota per plan, credit packs, API keys.",
    "bullets": [
      "Built-in, native MCP server",
      "MCP request quota per plan",
      "Credit packs available to purchase",
      "API keys (Settings → API Keys)"
    ]
  },
  "multisource": {
    "card_source_title": "Multi-source by update date",
    "card_source_description": "Every metric keeps its source and update date — no blind overwrite. Switch view by source.",
    "card_gsc_title": "Google Search Console built-in",
    "card_gsc_description": "Connect per property, quick-wins, coverage matrix by country.",
    "items": [
      { "title": "Multi-provider import", "description": "Semrush, Ahrefs, Mangools, SE Ranking, GSC." },
      { "title": "Rust performance", "description": "Rust→WASM CSV/HTML parsers, heavy import with Bun + DuckDB." },
      { "title": "Sparklines per source", "description": "12-month trends kept per provider." }
    ]
  },
  "team": {
    "items": [
      { "title": "Multi-organization", "description": "Manage several orgs from a single account." },
      { "title": "Roles", "description": "Owner, admin, member — controlled access." },
      { "title": "Team invitations", "description": "Invite teammates and clients in a few clicks." },
      { "title": "Extensible seats", "description": "Add seats on Pro and Agency at $19/mo." }
    ]
  },
  "pricing": {
    "eyebrow": "Pricing",
    "title": "Simple, transparent pricing",
    "subtitle": "14-day free trial, no credit card. Annual: at least −20% vs monthly.",
    "toggle_monthly": "Monthly",
    "toggle_yearly": "Annual",
    "yearly_badge": "−20%",
    "per_month": "/mo",
    "popular": "Most popular",
    "cta": "Start your free trial",
    "feature_labels": {
      "keywords": "Keywords",
      "projects": "Projects (sites)",
      "seats": "Included seats",
      "integrations": "Integrations",
      "backlinks": "Backlink rows",
      "mcp": "MCP requests / month"
    },
    "addons_title": "Add-ons",
    "addons": [
      "Extra seat (Pro & Agency): $19/month (≈ $15.20/mo annually).",
      "MCP credit pack: $25 for 25,000 requests (one-time, stackable)."
    ],
    "unlimited": "unlimited"
  },
  "faqs": {
    "title": "Frequently asked questions",
    "items": [
      { "question": "Is there a free trial?", "answer": "Yes, 14 days free with no credit card. Trial limits match Pro divided by two (750 keywords, 50,000 MCP requests)." },
      { "question": "Can I add seats?", "answer": "Yes, on Pro and Agency plans: $19/month per extra seat (≈ $15.20/mo annually)." },
      { "question": "How do MCP credit packs work?", "answer": "Each plan includes a monthly MCP request quota. You can buy one-time, stackable packs: $25 for 25,000 requests." },
      { "question": "Which integrations are available?", "answer": "Multi-provider import: Semrush, Ahrefs, Mangools, SE Ranking and Google Search Console. The number of integrations depends on your plan (unlimited on Agency)." },
      { "question": "Can I cancel anytime?", "answer": "Yes, the subscription is commitment-free and can be cancelled from the Stripe customer portal." }
    ]
  },
  "cta": {
    "title": "Start your free trial",
    "subtitle": "All your SEO, from keyword to publish — and ready for your AI agents.",
    "button": "Start your free trial",
    "note": "14 days, no credit card"
  },
  "footer": {
    "tagline": "The all-in-one SEO platform, multi-source and AI-agent ready.",
    "cta": "Start your free trial",
    "product": "Product",
    "company": "Company",
    "newsletter": "Newsletter",
    "newsletter_note": "Product news and behind the scenes, no spam.",
    "email_placeholder": "Your email",
    "rights": "All rights reserved.",
    "privacy": "Privacy",
    "terms": "Terms"
  }
}
```

- [ ] **Step 5: Commit**

```bash
git add lib/i18n.ts "app/[lang]/dictionaries.ts" "app/[lang]/dictionaries/fr.json" "app/[lang]/dictionaries/en.json"
git commit -m "feat(i18n): add locale constants and FR/EN dictionaries"
```

---

### Task 2: Restructurer les routes sous `[lang]`

**Files:**
- Move: `app/(marketing)/*` → `app/[lang]/(marketing)/*`
- Move: `app/(auth)/*` → `app/[lang]/(auth)/*`
- Delete: `app/layout.tsx`
- Create: `app/[lang]/layout.tsx`

- [ ] **Step 1: Déplacer les dossiers de routes (git mv)**

```bash
mkdir -p "app/[lang]"
git mv "app/(marketing)" "app/[lang]/(marketing)"
git mv "app/(auth)" "app/[lang]/(auth)"
```

- [ ] **Step 2: Créer le nouveau root layout `app/[lang]/layout.tsx`** (déplace fonts + theme depuis l'ancien `app/layout.tsx`, rend `lang` dynamique)

```tsx
import type { Metadata } from "next";
import { Inter, Manrope } from "next/font/google";
import "../globals.css";
import { ThemeProvider } from "@/providers/theme-provider";
import { locales, type Locale } from "@/lib/i18n";
import { hasLocale } from "./dictionaries";
import { notFound } from "next/navigation";

const manrope = Manrope({
  variable: "--font-manrope",
  subsets: ["latin"],
  weight: ["400", "500", "600", "700"],
});

const inter = Inter({
  variable: "--font-inter",
  subsets: ["latin"],
  weight: ["400", "500", "600", "700"],
});

export const metadata: Metadata = {
  title: "SEO Insight — La plateforme SEO tout-en-un, prête pour vos agents IA",
  description:
    "Réservoir de mots-clés multi-source, backlinks, pipeline de contenu et serveur MCP intégré. Une seule plateforme, multi-sites, multi-équipes.",
};

export function generateStaticParams() {
  return locales.map((lang) => ({ lang }));
}

export default async function RootLayout({
  children,
  params,
}: LayoutProps<"/[lang]">) {
  const { lang } = await params;
  if (!hasLocale(lang)) notFound();

  return (
    <html lang={lang} suppressHydrationWarning className="scroll-smooth">
      <body className={`${manrope.variable} ${inter.variable} antialiased`}>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          <main className="bg-background text-foreground">{children}</main>
        </ThemeProvider>
      </body>
    </html>
  );
}
```

- [ ] **Step 3: Supprimer l'ancien root layout**

```bash
git rm "app/layout.tsx"
```

- [ ] **Step 4: Vérifier la compilation**

Run: `bun run build`
Expected: build PASS. Les routes affichées doivent être `/[lang]`, `/[lang]/pricing`, `/[lang]/login`, `/[lang]/signup`. Si erreur "params should be awaited", vérifier le `await params`. (`favicon.ico` et `globals.css` restent dans `app/`.)

- [ ] **Step 5: Commit**

```bash
git add -A
git commit -m "feat(i18n): move routes under [lang] segment with dynamic root layout"
```

---

### Task 3: Proxy de détection de locale

**Files:**
- Create: `proxy.ts` (racine du projet — ex-`middleware.ts`, renommé en Next 16)

- [ ] **Step 1: Créer `proxy.ts`**

```ts
import { NextResponse, type NextRequest } from "next/server";
import { locales, defaultLocale } from "@/lib/i18n";

function getLocale(request: NextRequest): string {
  const accept = request.headers.get("accept-language") ?? "";
  const preferred = accept
    .split(",")
    .map((part) => part.split(";")[0].trim().slice(0, 2).toLowerCase());
  const match = preferred.find((code) =>
    (locales as readonly string[]).includes(code),
  );
  return match ?? defaultLocale;
}

export function proxy(request: NextRequest) {
  const { pathname } = request.nextUrl;
  const hasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
  );
  if (hasLocale) return;

  const locale = getLocale(request);
  request.nextUrl.pathname = `/${locale}${pathname}`;
  return NextResponse.redirect(request.nextUrl);
}

export const config = {
  matcher: ["/((?!_next|.*\\..*).*)"],
};
```

- [ ] **Step 2: Vérifier le build**

Run: `bun run build`
Expected: PASS, et le terminal liste `proxy` comme compilé (Next 16 reconnaît `proxy.ts`).

- [ ] **Step 3: Vérifier le redirect au runtime**

```bash
bun run dev
```
Puis avec `browser_eval` : `navigate` vers `http://localhost:3000/` → l'URL finale doit être `/fr` (ou `/en` selon Accept-Language). `navigate` vers `/pricing` → redirige vers `/fr/pricing`.
Expected: redirection effective, page rendue sans erreur console.

- [ ] **Step 4: Commit**

```bash
git add proxy.ts
git commit -m "feat(i18n): add proxy for Accept-Language locale detection"
```

---

# PHASE 2 — Branding

### Task 4: Logo SEO Insight

**Files:**
- Modify: `components/logo.tsx`
- Asset attendu: `public/brands/seo-insight.svg` (fourni par l'utilisateur)

- [ ] **Step 1: Réécrire `components/logo.tsx`** (wordmark SEO Insight ; utilise le SVG fourni s'il existe, sinon fallback `LogoIcon` actuel conservé)

```tsx
import { cn } from "@/lib/utils";
import Link from "next/link";
import React from "react";

export const Logo = ({
  className,
  lang = "fr",
}: {
  className?: string;
  lang?: string;
}) => {
  return (
    <Link href={`/${lang}`} className={cn("flex items-center gap-2", className)}>
      <LogoIcon />
      <span className="text-sm font-semibold text-neutral-700 dark:text-neutral-200">
        SEO Insight
      </span>
    </Link>
  );
};

export const LogoIcon = (props: React.SVGAttributes<SVGSVGElement>) => {
  return (
    <svg
      width="20"
      height="20"
      viewBox="0 0 20 20"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
      {...props}
    >
      <circle cx="9" cy="9" r="6" stroke="currentColor" strokeWidth="2" />
      <path
        d="M13.5 13.5L18 18"
        stroke="currentColor"
        strokeWidth="2"
        strokeLinecap="round"
      />
    </svg>
  );
};
```

> Note : si l'utilisateur fournit `public/brands/seo-insight.svg`, remplacer le `<svg>` inline par `<Image src="/brands/seo-insight.svg" alt="SEO Insight" width={20} height={20} />`. Le placeholder loupe ci-dessus reste valide pour le build.

- [ ] **Step 2: Build**

Run: `bun run build`
Expected: PASS.

- [ ] **Step 3: Commit**

```bash
git add components/logo.tsx
git commit -m "feat(branding): replace Agenforce logo with SEO Insight"
```

---

### Task 5: Navbar (dict + libellés localisés)

**Files:**
- Modify: `components/navbar.tsx`

- [ ] **Step 1: Réécrire `components/navbar.tsx`** pour recevoir `dict` + `lang` et préfixer les liens par la locale

```tsx
"use client";
import React, { useState } from "react";
import { Logo } from "./logo";
import { Container } from "./container";
import Link from "next/link";
import { Button } from "./ui/button";
import { IconLayoutSidebar, IconX } from "@tabler/icons-react";
import { AnimatePresence, motion } from "motion/react";
import type { Dictionary } from "@/app/[lang]/dictionaries";

type NavProps = { dict: Dictionary["nav"]; lang: string };

const useNavlinks = (dict: Dictionary["nav"], lang: string) => [
  { title: dict.features, href: `/${lang}#modules` },
  { title: dict.modules, href: `/${lang}#product` },
  { title: dict.faqs, href: `/${lang}#faqs` },
  { title: dict.pricing, href: `/${lang}/pricing` },
];

export const Navbar = ({ dict, lang }: NavProps) => {
  return (
    <div className="border-b border-neutral-200 dark:border-neutral-800">
      <DesktopNavbar dict={dict} lang={lang} />
      <MobileNavbar dict={dict} lang={lang} />
    </div>
  );
};

const MobileNavbar = ({ dict, lang }: NavProps) => {
  const [open, setOpen] = useState(false);
  const navlinks = useNavlinks(dict, lang);
  return (
    <div className="flex md:hidden px-4 py-2 justify-between relative">
      <Logo lang={lang} />
      <button onClick={() => setOpen(!open)} aria-label="Open menu">
        <IconLayoutSidebar className="size-4" />
      </button>
      <AnimatePresence>
        {open && (
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1, backdropFilter: "blur(15px)" }}
            exit={{ opacity: 0, backdropFilter: "blur(0px)" }}
            transition={{ duration: 0.2 }}
            className="fixed inset-0 h-full w-full z-50 px-4 py-1.5 flex flex-col justify-between"
          >
            <div>
              <div className="flex justify-between">
                <Logo lang={lang} />
                <button onClick={() => setOpen(false)} aria-label="Close menu">
                  <IconX />
                </button>
              </div>
              <div className="flex flex-col gap-6 my-10">
                {navlinks.map((item, index) => (
                  <motion.div
                    initial={{ opacity: 0, x: -4 }}
                    animate={{ opacity: 1, x: 0 }}
                    transition={{ duration: 0.2, delay: index * 0.1 }}
                    key={item.title}
                  >
                    <Link
                      href={item.href}
                      onClick={() => setOpen(false)}
                      className="text-2xl text-neutral-600 dark:text-neutral-400 font-medium"
                    >
                      {item.title}
                    </Link>
                  </motion.div>
                ))}
              </div>
            </div>
            <div className="flex items-center justify-end gap-4">
              <Link
                href={`/${lang}/login`}
                className="text-sm px-4 inline-block py-2 rounded-md text-neutral-600 dark:text-neutral-400 font-medium"
              >
                {dict.login}
              </Link>
              <Button asChild>
                <Link href={`/${lang}/signup`}>{dict.signup}</Link>
              </Button>
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
};

const DesktopNavbar = ({ dict, lang }: NavProps) => {
  const navlinks = useNavlinks(dict, lang);
  return (
    <Container className="py-4 items-center justify-between hidden lg:flex">
      <Logo lang={lang} />
      <div className="flex items-center gap-10">
        {navlinks.map((item) => (
          <Link
            key={item.title}
            href={item.href}
            className="text-sm text-neutral-600 dark:text-neutral-400 font-medium"
          >
            {item.title}
          </Link>
        ))}
      </div>
      <div className="flex items-center gap-4">
        <Link
          href={`/${lang}/login`}
          className="text-sm px-4 inline-block py-2 rounded-md text-neutral-600 dark:text-neutral-400 font-medium"
        >
          {dict.login}
        </Link>
        <Button asChild>
          <Link href={`/${lang}/signup`}>{dict.signup}</Link>
        </Button>
      </div>
    </Container>
  );
};
```

- [ ] **Step 2: Build** — `bun run build` (échouera tant que le layout marketing ne passe pas `dict`/`lang` ; corrigé en Task 14. Vérifier seulement l'absence d'erreur de type dans `navbar.tsx` via** `bunx tsc --noEmit`** si besoin, sinon committer et continuer.)

- [ ] **Step 3: Commit**

```bash
git add components/navbar.tsx
git commit -m "feat(branding): localize navbar via dictionary"
```

---

### Task 6: Footer (rebrand + dict)

**Files:**
- Modify: `components/footer.tsx`

- [ ] **Step 1: Modifier `components/footer.tsx`** — signature `({ dict, lang }: { dict: Dictionary["footer"]; lang: string })`, remplacer :
  - `import type { Dictionary } from "@/app/[lang]/dictionaries";` en tête.
  - `<Subheading>Safe, observable, outcome-driven AI</Subheading>` → `{dict.tagline}`
  - `<Button className="shadow-brand">Start a 30-day trial</Button>` → `{dict.cta}`
  - les titres de colonnes `Product`/`Company`/`Newsletter` → `{dict.product}` / `{dict.company}` / `{dict.newsletter}`
  - le copyright `Agenforce AI` → `SEO Insight` et `All rights reserved.` → `{dict.rights}`
  - placeholder email → `{dict.email_placeholder}`, note newsletter → `{dict.newsletter_note}`
  - liens `Privacy Policy`/`Terms of Service` → `{dict.privacy}` / `{dict.terms}` et `href={`/${lang}/privacy`}` / `href={`/${lang}/terms`}`
  - `<Logo />` → `<Logo lang={lang} />`
  - garder les listes `product`/`company` (liens `#` inchangés pour l'instant).

- [ ] **Step 2: Commit**

```bash
git add components/footer.tsx
git commit -m "feat(branding): rebrand footer to SEO Insight with dictionary"
```

---

# PHASE 3 — Sections réutilisées (refill depuis le dict)

### Task 7: Hero

**Files:**
- Modify: `components/hero.tsx`

- [ ] **Step 1: Réécrire `components/hero.tsx`** (reçoit `dict`/`lang`)

```tsx
import React from "react";
import { Container } from "./container";
import { Heading } from "./heading";
import { Subheading } from "./subheading";
import { Button } from "./ui/button";
import Link from "next/link";
import { LandingImages } from "./landing-images";
import { GradientDivider } from "./gradient-divider";
import type { Dictionary } from "@/app/[lang]/dictionaries";

export const Hero = ({
  dict,
  lang,
}: {
  dict: Dictionary["hero"];
  lang: string;
}) => {
  return (
    <section className="pt-10 md:pt-20 lg:pt-32 relative overflow-hidden">
      <Container>
        <Heading as="h1">
          {dict.title_line1} <br /> {dict.title_line2}
        </Heading>
        <Subheading className="py-8">{dict.subtitle}</Subheading>
        <div className="flex items-center gap-6">
          <Button asChild className="shadow-brand">
            <Link href={`/${lang}/signup`}>{dict.cta_primary}</Link>
          </Button>
          <Button asChild variant="outline">
            <Link href={`/${lang}/pricing`}>{dict.cta_secondary}</Link>
          </Button>
        </div>
        <p className="mt-4 text-sm text-neutral-500">{dict.trial_note}</p>
        <LandingImages />
      </Container>
      <GradientDivider />
    </section>
  );
};
```

- [ ] **Step 2: Commit**

```bash
git add components/hero.tsx
git commit -m "feat(home): refill hero with SEO Insight copy via dict"
```

---

### Task 8: LogoCloud (logos sources)

**Files:**
- Modify: `components/logo-cloud.tsx`

- [ ] **Step 1: Réécrire `components/logo-cloud.tsx`** — titre depuis dict, liste de logos = sources SEO. Utiliser des placeholders locaux qui pointeront vers `public/brands/*` (créés en Task 18). Tant que les fichiers n'existent pas, garder les URLs aceternity en commentaire et afficher les **noms** en attendant pour ne pas casser le build.

```tsx
"use client";
import Image from "next/image";
import React from "react";
import { motion } from "motion/react";
import type { Dictionary } from "@/app/[lang]/dictionaries";

const sources = [
  { title: "Semrush", src: "/brands/semrush.webp" },
  { title: "Ahrefs", src: "/brands/ahrefs.webp" },
  { title: "Mangools", src: "/brands/mangools.webp" },
  { title: "SE Ranking", src: "/brands/se-ranking.webp" },
  { title: "Google Search Console", src: "/brands/gsc.webp" },
];

export const LogoCloud = ({ dict }: { dict: Dictionary["logocloud"] }) => {
  return (
    <section className="pb-10 md:pb-10">
      <h2 className="text-neutral-600 font-medium dark:text-neutral-400 text-lg text-center max-w-xl mx-auto">
        {dict.title}
        <br className="hidden md:block" />
        <span className="text-neutral-400"> {dict.subtitle}</span>
      </h2>
      <div className="grid grid-cols-2 md:grid-cols-5 max-w-3xl mx-auto mt-10 items-center">
        {sources.map((logo, index) => (
          <motion.div
            initial={{ y: -10, opacity: 0, filter: "blur(10px)" }}
            whileInView={{ y: 0, opacity: 1, filter: "blur(0px)" }}
            transition={{ duration: 0.5, ease: "easeOut", delay: index * 0.1 }}
            viewport={{ once: true }}
            key={logo.title}
          >
            <Image
              src={logo.src}
              width={100}
              height={100}
              alt={logo.title}
              className="h-12 w-auto object-contain mx-auto opacity-70 grayscale hover:grayscale-0 transition"
            />
          </motion.div>
        ))}
      </div>
    </section>
  );
};
```

> Dépendance : les fichiers `public/brands/*.webp` sont ajoutés en Task 18. Si absent au moment du build, le build passe (next/image ne vérifie pas l'existence à la compilation) mais l'image sera cassée au runtime → c'est attendu jusqu'à Task 18.

- [ ] **Step 2: Commit**

```bash
git add components/logo-cloud.tsx
git commit -m "feat(home): logo cloud shows SEO data sources"
```

---

### Task 9: Features — 3 modules

**Files:**
- Modify: `components/features/index.tsx`

- [ ] **Step 1: Modifier `components/features/index.tsx`** — signature `({ dict }: { dict: Dictionary["modules"] })`. Remplacer le `Heading`/`Subheading` par `dict.title`/`dict.subtitle`, et les 3 `CardTitle` par `dict.keywords.title`, `dict.backlink.title`, `dict.content.title`. (Garder les 3 `SkeletonOne/Two/Three` et la structure des cartes inchangées.) Ajouter `import type { Dictionary } from "@/app/[lang]/dictionaries";`.

- [ ] **Step 2: Commit**

```bash
git add components/features/index.tsx
git commit -m "feat(home): map feature bento to Keywords/Backlink/Content modules"
```

---

### Task 10: Factoriser les Card* partagés

**Files:**
- Create: `components/features/shared.tsx`
- Modify: `components/features-secondary/index.tsx`
- Modify: `components/features-tertiary/index.tsx`

- [ ] **Step 1: Créer `components/features/shared.tsx`** (extrait les 3 helpers dupliqués à l'identique entre secondary & tertiary)

```tsx
import React from "react";
import { cn } from "@/lib/utils";

export const CardContent = ({ children }: { children: React.ReactNode }) => {
  return <div className="p-4 md:p-8">{children}</div>;
};

export const CardDescription = ({
  children,
}: {
  children: React.ReactNode;
}) => {
  return (
    <p className="text-neutral-600 dark:text-neutral-400 mt-2 max-w-md text-balance">
      {children}
    </p>
  );
};

export const CardSkeleton = ({
  className,
  children,
}: {
  className?: string;
  children?: React.ReactNode;
}) => {
  return (
    <div
      className={cn(
        "relative h-80 sm:h-60 flex flex-col md:h-80 overflow-hidden perspective-distant",
        className,
      )}
    >
      {children}
    </div>
  );
};
```

- [ ] **Step 2: Build**

Run: `bun run build`
Expected: PASS.

- [ ] **Step 3: Commit**

```bash
git add components/features/shared.tsx
git commit -m "refactor(home): extract shared Card helpers"
```

---

### Task 11: FeaturesSecondary — Multi-source & GSC

**Files:**
- Modify: `components/features-secondary/index.tsx`

- [ ] **Step 1: Modifier `components/features-secondary/index.tsx`** :
  - Remplacer les helpers locaux `CardContent`/`CardDescription`/`CardSkeleton` par l'import `import { CardContent, CardDescription, CardSkeleton } from "@/components/features/shared";` (supprimer les définitions en bas de fichier).
  - Signature : `({ dict }: { dict: Dictionary["multisource"] })`.
  - Carte 1 titre → `dict.card_source_title`, desc → `dict.card_source_description`.
  - Carte 2 titre → `dict.card_gsc_title`, desc → `dict.card_gsc_description`.
  - Les 3 items du bas (Workflow/Integration/Human icons) : titres/desc → `dict.items[0..2]` (garder les icônes `WorkflowIcon`/`IntegrationIcon`/`HumanIcon`).
  - Ajouter `import type { Dictionary } from "@/app/[lang]/dictionaries";`.

- [ ] **Step 2: Commit**

```bash
git add components/features-secondary/index.tsx
git commit -m "feat(home): map secondary features to Multi-source & GSC"
```

---

### Task 12: FeaturesTertiary — Multi-org

**Files:**
- Modify: `components/features-tertiary/index.tsx`

- [ ] **Step 1: Modifier `components/features-tertiary/index.tsx`** :
  - Remplacer les helpers locaux par l'import depuis `@/components/features/shared` (supprimer définitions en bas).
  - Signature : `({ dict }: { dict: Dictionary["team"] })`.
  - 4 cartes : titres → `dict.items[0..3].title`, desc → `dict.items[0..3].description` (Orgs / Rôles / Invitations / Sièges). Garder `SkeletonOne..Four` et les bordures.
  - Ajouter l'import `Dictionary`.

- [ ] **Step 2: Commit**

```bash
git add components/features-tertiary/index.tsx
git commit -m "feat(home): map tertiary features to multi-org & teams"
```

---

### Task 13: FAQs

**Files:**
- Modify: `components/faqs.tsx`

- [ ] **Step 1: Modifier `components/faqs.tsx`** :
  - Signature : `({ dict }: { dict: Dictionary["faqs"] })`.
  - Supprimer le tableau `questions` hardcodé, utiliser `dict.items`.
  - Titre `Frequently Asked Questions` → `{dict.title}`.
  - Ajouter import `Dictionary`. (Garder le sous-composant `Question` et `UserChatIcon`.)

- [ ] **Step 2: Commit**

```bash
git add components/faqs.tsx
git commit -m "feat(home): refill FAQs from dictionary"
```

---

### Task 14: Marketing layout + home page (câblage dict)

**Files:**
- Modify: `app/[lang]/(marketing)/layout.tsx`
- Modify: `app/[lang]/(marketing)/page.tsx`
- Modify: `components/mode-toggle.tsx` (retirer `console.log`)

- [ ] **Step 1: Réécrire `app/[lang]/(marketing)/layout.tsx`** (récupère dict, passe à Navbar/Footer)

```tsx
import { Navbar } from "@/components/navbar";
import { Footer } from "@/components/footer";
import { getDictionary, hasLocale } from "@/app/[lang]/dictionaries";
import { notFound } from "next/navigation";

export default async function MarketingLayout({
  children,
  params,
}: LayoutProps<"/[lang]">) {
  const { lang } = await params;
  if (!hasLocale(lang)) notFound();
  const dict = await getDictionary(lang);

  return (
    <>
      <Navbar dict={dict.nav} lang={lang} />
      {children}
      <Footer dict={dict.footer} lang={lang} />
    </>
  );
}
```

- [ ] **Step 2: Réécrire `app/[lang]/(marketing)/page.tsx`** (ordre des 11 sections — les sections Phase 4 sont importées mais commentées tant que les blocs ne sont pas intégrés)

```tsx
import { getDictionary, hasLocale } from "@/app/[lang]/dictionaries";
import { notFound } from "next/navigation";
import { Hero } from "@/components/hero";
import { LogoCloud } from "@/components/logo-cloud";
import { Features } from "@/components/features";
import { FeaturesSecondary } from "@/components/features-secondary";
import { FeaturesTertiary } from "@/components/features-tertiary";
import { FAQs } from "@/components/faqs";
// Phase 4 (à décommenter après intégration des blocs téléchargés) :
// import { Problem } from "@/components/problem";
// import { Pipeline } from "@/components/pipeline";
// import { MCP } from "@/components/mcp";
// import { Pricing } from "@/components/pricing";
// import { CTA } from "@/components/cta";

export default async function Home({ params }: PageProps<"/[lang]">) {
  const { lang } = await params;
  if (!hasLocale(lang)) notFound();
  const dict = await getDictionary(lang);

  return (
    <div className="min-h-screen">
      <Hero dict={dict.hero} lang={lang} />
      <LogoCloud dict={dict.logocloud} />
      {/* <Problem dict={dict.problem} /> */}
      {/* <Pipeline dict={dict.pipeline} /> */}
      <Features dict={dict.modules} />
      {/* <MCP dict={dict.mcp} /> */}
      <FeaturesSecondary dict={dict.multisource} />
      <FeaturesTertiary dict={dict.team} />
      {/* <Pricing dict={dict.pricing} lang={lang} /> */}
      <FAQs dict={dict.faqs} />
      {/* <CTA dict={dict.cta} lang={lang} /> */}
    </div>
  );
}
```

- [ ] **Step 3: Nettoyer `components/mode-toggle.tsx`** — supprimer la ligne `console.log("switch working");`.

- [ ] **Step 4: Build + vérif navigateur**

Run: `bun run build`
Expected: PASS. Puis `bun run dev` + `browser_eval` sur `/fr` et `/en` : Hero, LogoCloud (textes OK, logos cassés = normal), Features, FeaturesSecondary, FeaturesTertiary, FAQs s'affichent. `console_messages errorsOnly=true` → 0 erreur.

- [ ] **Step 5: Commit**

```bash
git add "app/[lang]/(marketing)/layout.tsx" "app/[lang]/(marketing)/page.tsx" components/mode-toggle.tsx
git commit -m "feat(home): wire dictionary into marketing layout and reorder sections"
```

---

# PHASE 4 — Blocs téléchargés (bloqué sur les téléchargements utilisateur)

> Chaque tâche suppose le bloc déposé dans `components/_downloaded/<name>.tsx`. L'intégration consiste à : créer un wrapper `components/<section>/index.tsx` qui adapte le bloc au design system et le câble au dict (interface ci-dessous), puis décommenter l'import dans `page.tsx` (Task 14 Step 2).

### Task 15: Section Problème

**Files:**
- Create: `components/problem/index.tsx`
- Modify: `app/[lang]/(marketing)/page.tsx` (décommenter `Problem`)

**Interface attendue :** `export const Problem = ({ dict }: { dict: Dictionary["problem"] }) => ...`
Consomme `dict.title`, `dict.subtitle`, `dict.points[]` (`{title, description}`, 4 items). Rendu dans un `<Container>`, style cohérent (cartes/bento neutres `bg-neutral-100 dark:bg-neutral-800`, `Heading`/`Subheading`).

- [ ] **Step 1:** Intégrer le bloc téléchargé dans `components/problem/index.tsx` en mappant les 4 `points` du dict.
- [ ] **Step 2:** Décommenter `import { Problem }` + `<Problem dict={dict.problem} />` (avant `Pipeline`) dans `page.tsx`.
- [ ] **Step 3:** `bun run build` → PASS ; `browser_eval` `/fr` → section visible.
- [ ] **Step 4:** Commit `feat(home): add problem section`.

---

### Task 16: Section Pipeline

**Files:**
- Create: `components/pipeline/index.tsx`
- Modify: `page.tsx` (décommenter `Pipeline`)

**Interface :** `export const Pipeline = ({ dict }: { dict: Dictionary["pipeline"] }) => ...`
Consomme `dict.title`, `dict.subtitle`, `dict.steps[]` (6 items `{title, description}`). Rendu en process horizontal/stepper, icônes au choix depuis `@/icons`.

- [ ] **Step 1:** Intégrer le bloc en mappant les 6 `steps`.
- [ ] **Step 2:** Décommenter dans `page.tsx` (entre `Problem` et `Features`).
- [ ] **Step 3:** `bun run build` → PASS ; vérif navigateur.
- [ ] **Step 4:** Commit `feat(home): add pipeline section`.

---

### Task 17: Section MCP (spotlight)

**Files:**
- Create: `components/mcp/index.tsx`
- Modify: `page.tsx` (décommenter `MCP`)

**Interface :** `export const MCP = ({ dict }: { dict: Dictionary["mcp"] }) => ...`
Consomme `dict.title`, `dict.subtitle`, `dict.bullets[]` (4 strings). Bloc code/terminal du téléchargement, accent fort (c'est le différenciateur ⭐).

- [ ] **Step 1:** Intégrer le bloc, câbler titre/subtitle/bullets.
- [ ] **Step 2:** Décommenter dans `page.tsx` (entre `Features` et `FeaturesSecondary`).
- [ ] **Step 3:** `bun run build` → PASS ; vérif navigateur.
- [ ] **Step 4:** Commit `feat(home): add MCP agents-ready spotlight section`.

---

### Task 18: Pricing 4 tiers + toggle

**Files:**
- Create: `lib/pricing.ts`
- Rewrite: `components/pricing.tsx`
- Modify: `app/[lang]/(marketing)/page.tsx` (décommenter `Pricing`)
- Modify: `app/[lang]/(marketing)/pricing/page.tsx`

- [ ] **Step 1: Créer `lib/pricing.ts`** (données figées du brief §4)

```ts
export type Plan = {
  id: "solo" | "starter" | "pro" | "agency";
  name: string;
  monthly: number;
  yearly: number; // équivalent /mois en annuel
  popular?: boolean;
  features: {
    keywords: string;
    projects: string;
    seats: string;
    integrations: string;
    backlinks: string;
    mcp: string;
  };
};

export const plans: Plan[] = [
  {
    id: "solo",
    name: "Solo",
    monthly: 9.99,
    yearly: 7.99,
    features: {
      keywords: "100",
      projects: "1",
      seats: "1",
      integrations: "1",
      backlinks: "10 000",
      mcp: "1 000",
    },
  },
  {
    id: "starter",
    name: "Starter",
    monthly: 49,
    yearly: 39,
    features: {
      keywords: "500",
      projects: "5",
      seats: "1",
      integrations: "2",
      backlinks: "100 000",
      mcp: "10 000",
    },
  },
  {
    id: "pro",
    name: "Pro",
    monthly: 129,
    yearly: 99,
    popular: true,
    features: {
      keywords: "1 500",
      projects: "15",
      seats: "3",
      integrations: "5",
      backlinks: "500 000",
      mcp: "100 000",
    },
  },
  {
    id: "agency",
    name: "Agency",
    monthly: 299,
    yearly: 239,
    features: {
      keywords: "5 000",
      projects: "40",
      seats: "10",
      integrations: "__UNLIMITED__",
      backlinks: "2 000 000",
      mcp: "500 000",
    },
  },
];
```

> `__UNLIMITED__` est remplacé à l'affichage par `dict.pricing.unlimited` ("illimité"/"unlimited").

- [ ] **Step 2: Réécrire `components/pricing.tsx`** — composant client avec toggle. Adapter le bloc téléchargé 4 colonnes ; câbler `plans` + `dict.pricing`. Squelette attendu :

```tsx
"use client";
import React, { useState } from "react";
import { Container } from "./container";
import { Heading } from "./heading";
import { Subheading } from "./subheading";
import { Button } from "./ui/button";
import { IconCircleCheckFilled } from "@tabler/icons-react";
import Link from "next/link";
import { plans } from "@/lib/pricing";
import type { Dictionary } from "@/app/[lang]/dictionaries";

export const Pricing = ({
  dict,
  lang,
}: {
  dict: Dictionary["pricing"];
  lang: string;
}) => {
  const [yearly, setYearly] = useState(false);
  return (
    <section className="py-10 md:py-20 lg:py-32 relative overflow-hidden">
      <Container>
        <Subheading className="mx-auto text-center">{dict.eyebrow}</Subheading>
        <Heading className="text-center mt-4">{dict.title}</Heading>
        <Subheading className="mt-4 mx-auto text-center max-w-2xl">
          {dict.subtitle}
        </Subheading>

        {/* Toggle mensuel/annuel */}
        <div className="flex items-center justify-center gap-3 my-8">
          <span>{dict.toggle_monthly}</span>
          <button
            onClick={() => setYearly((v) => !v)}
            aria-label="toggle billing period"
            className="relative h-6 w-11 rounded-full bg-neutral-300 dark:bg-neutral-700"
          >
            <span
              className={`absolute top-0.5 size-5 rounded-full bg-white transition-all ${
                yearly ? "left-[22px]" : "left-0.5"
              }`}
            />
          </button>
          <span>{dict.toggle_yearly}</span>
          <span className="text-xs font-semibold text-green-600">
            {dict.yearly_badge}
          </span>
        </div>

        {/* 4 cartes — markup issu du bloc téléchargé */}
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
          {plans.map((plan) => (
            <div
              key={plan.id}
              className={`p-6 rounded-2xl bg-neutral-100 dark:bg-neutral-800 flex flex-col gap-4 ${
                plan.popular ? "ring-2 ring-black dark:ring-white" : ""
              }`}
            >
              {plan.popular && (
                <span className="text-xs font-semibold">{dict.popular}</span>
              )}
              <h3 className="text-lg font-bold">{plan.name}</h3>
              <div className="text-3xl font-bold">
                ${yearly ? plan.yearly : plan.monthly}
                <span className="text-sm text-neutral-400">
                  {dict.per_month}
                </span>
              </div>
              <Button asChild>
                <Link href={`/${lang}/signup`}>{dict.cta}</Link>
              </Button>
              <ul className="flex flex-col gap-2 mt-2 text-sm">
                {(
                  Object.keys(dict.feature_labels) as (keyof typeof plan.features)[]
                ).map((key) => (
                  <li key={key} className="flex items-center gap-2">
                    <IconCircleCheckFilled className="size-4 text-neutral-500" />
                    <span>
                      {dict.feature_labels[key]} :{" "}
                      {plan.features[key] === "__UNLIMITED__"
                        ? dict.unlimited
                        : plan.features[key]}
                    </span>
                  </li>
                ))}
              </ul>
            </div>
          ))}
        </div>

        {/* Add-ons */}
        <div className="mt-10">
          <h4 className="font-semibold">{dict.addons_title}</h4>
          <ul className="mt-2 text-sm text-neutral-500 flex flex-col gap-1">
            {dict.addons.map((a) => (
              <li key={a}>{a}</li>
            ))}
          </ul>
        </div>
      </Container>
    </section>
  );
};
```

- [ ] **Step 3: Mettre à jour `app/[lang]/(marketing)/pricing/page.tsx`** — passer dict + lang :

```tsx
import { getDictionary, hasLocale } from "@/app/[lang]/dictionaries";
import { notFound } from "next/navigation";
import { Pricing } from "@/components/pricing";
import { FAQs } from "@/components/faqs";

export default async function PricingPage({ params }: PageProps<"/[lang]"> ) {
  const { lang } = await params;
  if (!hasLocale(lang)) notFound();
  const dict = await getDictionary(lang);
  return (
    <div className="min-h-screen">
      <Pricing dict={dict.pricing} lang={lang} />
      <FAQs dict={dict.faqs} />
    </div>
  );
}
```

- [ ] **Step 4:** Décommenter `Pricing` dans la home `page.tsx`.
- [ ] **Step 5:** `bun run build` → PASS ; `browser_eval` `/fr/pricing` & `/en/pricing` : 4 cartes, toggle bascule les prix, Pro mis en avant, add-ons visibles.
- [ ] **Step 6:** Commit `feat(pricing): 4-tier pricing with monthly/yearly toggle`.

---

### Task 19: Band CTA final

**Files:**
- Create: `components/cta/index.tsx`
- Modify: `page.tsx` (décommenter `CTA`)

**Interface :** `export const CTA = ({ dict, lang }: { dict: Dictionary["cta"]; lang: string }) => ...`
Consomme `dict.title`, `dict.subtitle`, `dict.button`, `dict.note`. Bouton → `/${lang}/signup`. Band pleine largeur avant le footer.

- [ ] **Step 1:** Intégrer le bloc, câbler dict.
- [ ] **Step 2:** Décommenter dans `page.tsx` (dernier élément avant `</div>`).
- [ ] **Step 3:** `bun run build` → PASS ; vérif navigateur.
- [ ] **Step 4:** Commit `feat(home): add final CTA band`.

---

# PHASE 5 — Assets

### Task 20: Logos sources + screenshots placeholders

**Files:**
- Add: `public/brands/{semrush,ahrefs,mangools,se-ranking,gsc}.webp` (+ `stripe.webp`, `claude.webp` si utilisés par le bloc MCP)
- Add: `public/home/dashboard-backlinks.webp`, `public/home/dashboard-content.webp`

- [ ] **Step 1:** Déposer les logos sources fournis dans `public/brands/`. Tant que les vrais logos ne sont pas disponibles, créer des placeholders en copiant un SVG neutre, OU laisser l'utilisateur fournir. (Le `LogoCloud` Task 8 pointe déjà dessus.)
- [ ] **Step 2:** Pour les screenshots modules manquants, créer des placeholders en copiant les webp existants :

```bash
cp public/home/dashboard-overview.webp public/home/dashboard-backlinks.webp
cp public/home/dashboard-keywords.webp public/home/dashboard-content.webp
```

- [ ] **Step 3:** Brancher les screenshots modules dans les cartes `Features` (Task 9) via `LandingImages`/`Image` si le design des cartes le permet (sinon laisser les skeletons animés actuels — décision à confirmer à l'intégration).
- [ ] **Step 4:** `bun run build` → PASS ; vérif navigateur : logos sources affichés.
- [ ] **Step 5:** Commit `feat(assets): add source logos and module screenshot placeholders`.

---

### Task 21: Rapatrier les images distantes

**Files:**
- Modify: `components/landing-images.tsx` (déjà local par défaut — vérifier qu'aucune section ne passe d'URL aceternity)
- Modify: `next.config.ts`

- [ ] **Step 1:** Vérifier qu'aucun composant restant ne référence `assets.aceternity.com` :

```bash
grep -rn "assets.aceternity.com" components/ app/
```
Expected: aucune occurrence (les sections `Speed`/`Outcomes` qui les utilisaient ne sont plus dans la page). Si présence, remplacer par un webp local de `public/home/`.

- [ ] **Step 2:** Nettoyer `next.config.ts` — retirer `remotePatterns` devenu inutile :

```ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {};

export default nextConfig;
```

- [ ] **Step 3:** `bun run build` → PASS.
- [ ] **Step 4:** Commit `perf(images): drop remote image patterns, all images local`.

---

# PHASE 6 — SEO / meta

### Task 22: JSON-LD

**Files:**
- Create: `components/json-ld.tsx`
- Modify: `app/[lang]/(marketing)/page.tsx`

- [ ] **Step 1: Créer `components/json-ld.tsx`** (SoftwareApplication + Offers depuis `plans` + Organization)

```tsx
import { plans } from "@/lib/pricing";

export const JsonLd = () => {
  const data = {
    "@context": "https://schema.org",
    "@graph": [
      {
        "@type": "Organization",
        name: "SEO Insight",
        url: "https://seo-insight.com",
      },
      {
        "@type": "SoftwareApplication",
        name: "SEO Insight",
        applicationCategory: "BusinessApplication",
        operatingSystem: "Web",
        offers: plans.map((p) => ({
          "@type": "Offer",
          name: p.name,
          price: p.monthly,
          priceCurrency: "USD",
        })),
      },
    ],
  };
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
    />
  );
};
```

- [ ] **Step 2:** Ajouter `<JsonLd />` en tête du `return` de la home `page.tsx` (import inclus).
- [ ] **Step 3:** `bun run build` → PASS.
- [ ] **Step 4:** Commit `feat(seo): add SoftwareApplication/Offer/Organization JSON-LD`.

---

### Task 23: generateMetadata localisé + sitemap + robots

**Files:**
- Modify: `app/[lang]/layout.tsx` (remplacer `metadata` statique par `generateMetadata`)
- Create: `app/[lang]/sitemap.ts` *(ou `app/sitemap.ts`)*
- Create: `app/robots.ts`

- [ ] **Step 1:** Dans `app/[lang]/layout.tsx`, remplacer l'export `metadata` par :

```tsx
export async function generateMetadata({
  params,
}: LayoutProps<"/[lang]">): Promise<Metadata> {
  const { lang } = await params;
  const dict = await getDictionary(hasLocale(lang) ? lang : "fr");
  return {
    title: `SEO Insight — ${dict.hero.title_line1}`,
    description: dict.hero.subtitle,
    openGraph: {
      title: "SEO Insight",
      description: dict.hero.subtitle,
      images: ["/home/dashboard-overview.webp"],
      locale: lang,
    },
  };
}
```
(ajouter l'import `getDictionary` ; garder `generateStaticParams`.)

- [ ] **Step 2: Créer `app/robots.ts`**

```ts
import type { MetadataRoute } from "next";

export default function robots(): MetadataRoute.Robots {
  return {
    rules: { userAgent: "*", allow: "/" },
    sitemap: "https://seo-insight.com/sitemap.xml",
  };
}
```

- [ ] **Step 3: Créer `app/sitemap.ts`**

```ts
import type { MetadataRoute } from "next";
import { locales } from "@/lib/i18n";

const base = "https://seo-insight.com";
const paths = ["", "/pricing", "/login", "/signup"];

export default function sitemap(): MetadataRoute.Sitemap {
  return locales.flatMap((lang) =>
    paths.map((p) => ({ url: `${base}/${lang}${p}` })),
  );
}
```

- [ ] **Step 4:** `bun run build` → PASS (sitemap/robots listés dans les routes).
- [ ] **Step 5:** Commit `feat(seo): localized metadata, sitemap and robots`.

---

# PHASE 7 — Vérification finale

### Task 24: Build + vérification navigateur complète

- [ ] **Step 1:** `bun run build` → PASS, sans warning bloquant.
- [ ] **Step 2:** `bun run dev` puis `browser_eval` :
  - `navigate` `/` → redirige `/fr`
  - `/fr`, `/en` (home) : 11 sections rendues, images OK
  - `/fr/pricing`, `/en/pricing` : toggle fonctionne
  - `/fr/login`, `/fr/signup`
  - `console_messages errorsOnly=true` sur chaque → 0 erreur
  - `close`
- [ ] **Step 3:** `knip` (règle globale, avant PR sensible) pour détecter exports/fichiers morts :

```bash
bunx knip
```
Traiter les morts-vivants éventuels (anciens skeletons inutilisés des sections supprimées, etc.).

- [ ] **Step 4:** Commit final si nettoyage `chore: remove dead code after marketing front rebuild`.

---

## Notes de fin

- **Merge** : une fois validé, suivre le git flow — `git checkout dev && git merge feature/marketing-front-seo-insight --ff-only && git push && git branch -d feature/marketing-front-seo-insight`. Merge `dev → main` seulement sur demande explicite.
- **Suites possibles** (hors périmètre) : skill `nextjs-oxc` (oxlint/oxfmt/knip en CI), skill `seo-geo` après rédaction du contenu, vrais screenshots produit, pages légales.
