Audit terminé. Le worktree `/Users/humperdoo/Github/advmfr-next/.claude/worktrees/seo-audit` **existait déjà** quand j'ai démarré (créé par un autre agent de la session, build daté du 25/08 12:49 sur HEAD `a7a66f6`) — je ne l'ai pas supprimé pour ne pas casser les autres auditeurs. À supprimer par le parent en fin de session : `git worktree remove --force /Users/humperdoo/Github/advmfr-next/.claude/worktrees/seo-audit`.

---

# Audit SEO — Home (`/` EN et `/fr`)

Base : HTML prérendu réel (`.next/server/app/en.html`, `fr.html`), 214 Ko / 216 Ko. Tous les constats ci-dessous sont vérifiés dans le HTML ou le code, pas déduits.

## 1. Tableau des constats

| # | Sévérité | Thème | Fichier:ligne | Constat (vérifié) | Correction proposée | Effort | Quick win |
|---|---|---|---|---|---|---|---|
| 1 | **Bloquant** | JSON-LD / canonical | `src/config/seo/structured-data.ts:131` + `src/app/[locale]/page.tsx:77` | Le `WebPage` JSON-LD de la home **FR** déclare `"url": "https://www.advmfr.com"` (URL EN) et non `/fr`. `webPageJsonLd({path:"/"})` fait `${site.url}${path==="/"?"":path}` sans jamais préfixer la locale. Signal contradictoire avec `<link rel="canonical" href=".../fr">`. | Passer le pathname localisé : `path: getPathname({href:"/", locale})`, ou faire prendre `locale` en charge par le builder. | S | ✅ |
| 2 | **Bloquant** | Rendu / indexabilité | `src/app/[locale]/loading.tsx:1-5` (+ `layout.tsx:114`) | **Tout le contenu de la page** (H1, 8 sections, les 3 blocs JSON-LD, les 23 `<img>`, les 30 liens) est émis dans `<div hidden id="S:0">` **en fin de document**, après le `<footer>`, et n'est injecté dans le DOM visible que par le script inline React `$RC("B:0","S:0")`. Le HTML « nu » ne contient qu'un skeleton (`PageSkeleton`) — aucun H1, aucun texte. Vérifié : `<h1>` à l'offset 27066, `<footer>` à 16253, `<div hidden id="S:0">` à 21737. Pattern identique sur toutes les pages (about-us, gallery, contact-us). | Googlebot exécute JS et reconstruira le DOM, mais : (a) coût d'une passe de rendu supplémentaire, (b) les crawlers **explicitement autorisés dans `robots.txt`** (OAI-SearchBot, PerplexityBot, Claude-SearchBot, DuckAssistBot) et les scrapers sociaux ne lisent qu'un skeleton. La route est pourtant 100 % prérendue (`prerender-manifest.json`: `/en`, `/fr`, `initialRevalidateSeconds:false`). → Identifier ce qui suspend l'arbre et l'éliminer, ou supprimer `loading.tsx` au niveau `[locale]` (le descendre sur les seuls segments réellement dynamiques) pour que le contenu soit inliné en place. | M | ❌ |
| 3 | **Bloquant** | Contenu / mots-clés | `messages/en.json` `IndexPage.*` vs `~/Documents/Advmfr/website/pages/home/en/index.md` | Home EN rendue = **433 mots visibles**, **0 occurrence de « jewellery »** (UK), **0 occurrence de « Thailand »**, 5 « jewelry » (US). Le `<title>` et la meta ciblent *Fine **Jewellery** Manufacturer in **Thailand*** : le mot-clé principal n'existe nulle part dans le corps ni dans le H1. La source SEO spécifie 3 840 mots, 10 H2, 27 H3, orthographe UK normalisée. | Aligner l'orthographe sur UK (`jewellery`) partout dans `IndexPage.*` et les alts EN, et intégrer « Thailand »/« Bangkok » dans le H1 ou un H2. La montée en volume rédactionnel est un chantier éditorial distinct. | M (ortho: S) | ✅ (ortho) |
| 4 | **Majeur** | Metas OG/Twitter | `src/config/seo/meta.ts:110-120` vs `src/app/[locale]/layout.tsx:68-81` | `buildPageMetadata` renvoie des objets `openGraph` et `twitter` **partiels** qui écrasent intégralement ceux du layout (merge non profond de Next Metadata). Résultat rendu : `twitter:card = "summary"` (au lieu de `summary_large_image`), **pas de `og:type`**, **pas de `og:site_name`**, **pas de `twitter:site`**. L'image 1200×630 est donc servie en vignette carrée sur X. Affecte toutes les pages passant par `buildPageMetadata`. | Ajouter `type: seo.openGraph.type`, `siteName: seo.openGraph.siteName` dans le bloc `openGraph`, et `card: seo.twitter.card`, `site: seo.twitter.site` dans le bloc `twitter` de `buildPageMetadata`. | S | ✅ |
| 5 | **Majeur** | CWV / LCP | `src/components/home/GallerySlideshow.tsx:68` | Le slide 0 du carrousel (**tout en bas de page**) porte `loading="eager"` → Next émet un `<link rel="preload" as="image">` en `<head>` pour `az4393_r1wq6o.webp` avec `imageSizes="(min-width:1440px) 1312px, 100vw"` et un srcset jusqu'à 2048w. Il **concurrence le préload du hero** (le vrai LCP) sur la même file de priorité. | Supprimer `loading={index === 0 ? "eager" : undefined}` (laisser tous les slides en lazy) ou le remplacer par un préchargement déclenché à l'entrée en viewport. | S | ✅ |
| 6 | **Majeur** | JSON-LD Organization | `src/config/seo/structured-data.ts:44-48` | `"@type": "JewelryStore"` = sous-type LocalBusiness désignant un **magasin de détail vendant des bijoux**. ADVMFR est un fabricant B2B qui, selon sa propre source SEO, « ne vend pas au public ». Type sémantiquement faux, et rend l'entité éligible à un traitement « commerce local ». | Repasser sur `"@type": "Organization"` (ou `["Organization","LocalBusiness"]` si on tient au geo/telephone), en conservant `address`, `geo`, `telephone`. | S | ✅ |
| 7 | **Majeur** | JSON-LD Organization | `structured-data.ts:48` + `src/config/site.ts:17` | `"name": "ADV MFR"` — le nom d'entité déclaré à Google est le wordmark abrégé, pas la raison sociale commerciale « Advance Manufacturers » (qui apparaît pourtant dans les alts, le footer et le `legalName`). Impact direct sur le nom de site / knowledge panel en recherche de marque. | `name: "Advance Manufacturers"`, `alternateName: site.shortName`, `legalName` inchangé. | S | ✅ |
| 8 | **Majeur** | JSON-LD Organization | `structured-data.ts:51` + `site.ts:26-31` | `logo` est une **URL nue** vers un SVG (`/logo-advance-manufacturer.svg`), sans `ImageObject` ni dimensions. Dimensions réelles du fichier : **436×373** — alors que `site.logo` déclare `width: 512, height: 512` (valeurs fausses). Google exige un logo ≥ 112×112 px dans un format crawlable ; un SVG sans dimensions déclarées laisse la contrainte invérifiable. | Émettre `logo: { "@type":"ImageObject", url: <PNG/WebP raster>, width, height }` et corriger `site.logo.width/height`. Les assets `android-chrome-512x512.png` existent déjà. | S | ✅ |
| 9 | **Majeur** | JSON-LD Organization | `structured-data.ts:44-68` | Aucune des preuves E-E-A-T affichées sur la page n'est structurée : pas de `foundingDate` (« Est. 1989 » en badge hero + watermark), pas de `numberOfEmployees` (« 500+ Master Craftsmen » affiché), pas de `description`, pas de `award`/`hasCredential` pour le RJC (COP 0000 7330, LGM 0000 7445 pourtant dans l'alt de l'image), pas de `areaServed` (« 42 countries » dans le body). `sameAs` = 3 réseaux (LinkedIn, Instagram, Facebook) — le compte X `@advmfr` (`site.ts:33`) est utilisé pour `twitter:site` mais absent de `sameAs`. | Ajouter `foundingDate: "1989"`, `numberOfEmployees`, `description`, `areaServed`, `hasCredential` (RJC), et le profil X dans `sameAs`. | M | ❌ |
| 10 | **Majeur** | Sitemap | `src/app/sitemap.ts:30` | `const lastModified = new Date()` au niveau module → **les 30 URLs reçoivent le timestamp du build** (`2026-08-25T05:49:01.974Z`, identique partout). Google déclare ignorer/déprécier les `lastmod` non fiables. Les articles de blog ont bien un vrai `lastmod` (l. 102), les pages statiques non. | Dériver le `lastmod` des pages statiques d'une vraie date de modification (front-matter, `git log -1 --format=%cI <fichier>` au build, ou date manuelle par route), sinon **omettre** `lastmod` sur ces entrées. | M | ❌ |
| 11 | **Majeur** | i18n / mots-clés FR | `messages/fr.json` `IndexPage.seo.metaKeywords` + `LocaleLayout.metaKeywords` | Les 10 keywords FR rendus sont une **traduction littérale du set EN** : « fabricant de joaillerie fine », « fabricant joaillerie **wholesale** » (franglais), « usine joaillerie Thaïlande ». Or **« joaillerie » apparaît 0 fois** dans le corps FR rendu, « Thaïlande » 0 fois. La recherche de mots-clés FR existante (`~/Documents/Advmfr/website/pages/home/fr/index.meta.json`) cible `grossiste bijoux`, `grossiste bijouterie`, `grossiste bijoux argent`, `bijoux en gros`, `fabricant de bijoux`. Corps FR rendu : « grossiste » ×2, « bijoux » ×4, « bijouterie » 0, « en gros » 0. | Remplacer les `metaKeywords` FR par le set issu de la recherche FR (pas par une traduction de l'EN) et injecter le champ lexical `grossiste`/`bijoux en gros`/`argent 925` dans les H2/corps FR. | S | ✅ |
| 12 | **Majeur** | Maillage interne | `GallerySection.tsx`, `ProcessSection.tsx`, `CertificationSection.tsx`, `MaterialsSection.tsx`, `ValuePropSection.tsx` | 5 sections sur 8 ne contiennent **aucun lien**. La section « Gallery » (12 photos) ne pointe pas vers `/gallery` ; « How We Work » (6 étapes) ne pointe pas vers `/production-process` ; « Certification & Sustainability » ne pointe pas vers `/sustainability`. Seuls le hero (2 CTA) et About (1 lien) émettent des liens contextuels. Total home : 30 `<a>`, dont 16 dupliqués (nav desktop + drawer mobile) et 10 en footer. | Ajouter un CTA contextuel en fin de chaque section vers la page profonde correspondante, avec ancre descriptive (« Voir toute la galerie », « Notre processus de production complet », « Notre certification RJC »). | S | ✅ |
| 13 | **Majeur** | Maillage / footer | `src/components/Footer.tsx:14-21` | Le footer ne liste que 6 destinations (Home, About Us, Production Process, Sustainability, Blog, Contact Us). **`/gallery`, `/design-development` et `/news-events` ne sont liées que depuis le header** — donc uniquement via la nav dupliquée, sans ancre contextuelle nulle part sur la home. | Compléter `footerNav` avec les 3 routes manquantes. | S | ✅ |
| 14 | **Majeur** | Outline | `src/components/home/AboutSection.tsx:16-19` | La section About n'a **aucun heading** (commentaire explicite : « The overline is the only heading here — the section has no h2 »). Le bloc le plus riche en preuves (RJC, 1989, 3,5 M pièces, 42 pays, 5 000 m², 500 artisans) est structurellement invisible. | Ajouter un `<h2 className="sr-only">` porteur du mot-clé cible (EN : positionnement « fine jewellery manufacturer in Thailand » ; FR : « grossiste en bijoux »), ou promouvoir l'`<Overline>` en `<h2>` visuellement identique. Le rendu visuel reste inchangé dans les deux cas. | S | ✅ |
| 15 | **Majeur** | H1 vs cible | `messages/{en,fr}.json` `IndexPage.hero.title` | H1 EN rendu = « Manufacturers of Fine Gold & Silver Jewelry » ; `<title>` = « Fine Jewellery Manufacturer in Thailand ». Aucun mot commun en dehors de « Fine ». H1 FR = « Fabricant de bijoux fins en or & argent » ; `<title>` FR = « Grossiste Bijoux : Fournisseur Accessoires Mode » — le H1 ne contient pas « grossiste ». Cf. §5 pour les cibles de la source hors repo. | Aligner chaque H1 sur la cible de **sa propre** locale (voir §5). | S | ✅ |
| 16 | Mineur | Meta description EN | `messages/en.json` `IndexPage.seo.metaDescription` | « 30+ years' experience » (157 car.) alors que la page affiche « over **37** years » (hero subtitle) et « 37 » en compteur animé (`StatsSection`), et « Est. 1989 » en badge. 1989 → 2026 = 37 ans. Incohérence visible en SERP vs page. | Passer à « 37 years » ou « since 1989 » (le second ne périme jamais). Le fichier source hors repo dit aussi « 30+ » — à corriger aux deux endroits. | S | ✅ |
| 17 | Mineur | Meta description FR | `messages/fr.json` `IndexPage.seo.metaDescription` | « Découvrez nos bijoux raffinés, bagues, boucles d'oreilles, pendentifs et accessoires pour boutiques, commerçants et magasins. Achat en gros garanti ! » (149 car.). Registre B2C/promotionnel, ne mentionne ni la marque, ni le statut de fabricant direct, ni le RJC, ni Bangkok — alors que ce sont les arguments de la page. La source FR propose une description contenant « fabricant direct », « or, argent 925 et laiton plaqué », « OEM/ODM », « certifié RJC ». | Réécrire sur la base de la source FR. | S | ✅ |
| 18 | Mineur | OG image | `src/app/[locale]/opengraph-image.tsx:13,35-36` + `messages/*.json` `OpenGraph.tagline` | Image 1200×630 PNG, 33 Ko — techniquement correcte. Contenu : aplat `#101E33` + « ADV MFR » (96 px) + tagline. Tagline EN = « Industrial manufacturing solutions », FR = « Solutions de fabrication industrielle ». **Aucune mention de joaillerie/bijoux, aucun visuel produit, aucun logo.** `og:image:alt` = `"ADV MFR"` (non descriptif, identique EN/FR). | Repartir sur un visuel produit (une des 12 photos de parures) + wordmark + accroche localisée ; `alt` descriptif localisé. | M | ❌ |
| 19 | Mineur | Descriptions génériques | `src/config/site.ts:22`, `messages/{en,fr}.json` `LocaleLayout.description` | `"Industrial manufacturing solutions by ADV MFR."` / `"ADV MFR: industrial manufacturing solutions."` / `"ADV MFR : solutions de fabrication industrielle."` — description de repli du site entier, réutilisée par `manifest.description` et `og:description` du layout. Décrit une usine générique, pas un fabricant de joaillerie fine. | Réécrire sur le positionnement réel (joaillerie fine B2B, Bangkok, RJC). | S | ✅ |
| 20 | Mineur | Manifest | `src/app/manifest.ts:21-24` | `icons` ne déclare que `/favicon.ico` (sizes `any`) et le SVG. **Pas d'icône 192×192 ni 512×512, pas de `purpose:"maskable"`** → critère d'installabilité PWA non rempli. Or `public/android-chrome-192x192.png` (10 Ko) et `public/android-chrome-512x512.png` (49 Ko) **existent déjà et ne sont référencés nulle part**. | Ajouter les 2 entrées PNG avec `sizes`/`type`, plus une entrée `purpose:"maskable"`. | S | ✅ |
| 21 | Mineur | CWV / polices | `src/app/[locale]/layout.tsx:20-25` | 3 familles chargées et **3 woff2 préchargés** en `<head>` (10,0 Ko / 21,4 Ko / 34,7 Ko). Or `font-serif` (= Playfair Display) n'est utilisé **nulle part sur la home** — sa seule utilisation dans tout le repo est `src/components/ui/And.tsx:8`, et le `&` du H1 home est rendu en clair (`font-serif` : 0 occurrence dans `en.html`). Playfair ne sert que de fallback à Tenor Sans dans `--font-display`. | Ne pas charger Playfair sur les routes qui ne l'utilisent pas, ou l'abandonner si `And.tsx` n'est plus employé (à vérifier hors home). Gain immédiat : un `<link rel=preload as=font>` en moins dans le chemin critique. | S | ✅ |
| 22 | Mineur | CWV / images | `src/components/home/StatsSection.tsx:24-31` | Image décorative (`alt=""`, `aria-hidden`) affichée à **`opacity: .16`** derrière du texte, servie via `sizes="(min-width:1024px) 50vw, 100vw"` avec un `src` de repli en **`w=2048&q=75`**, depuis une source 3000×2000 / 279 Ko. Même fichier (`az4948_ggpzcg.webp`) réutilisé comme slide 8 de la galerie avec un alt descriptif → deux alts différents pour le même fichier sur la même page. | Utiliser une variante basse résolution dédiée (ou un `sizes` très réduit, ex. `20vw`) pour un fond à 16 % d'opacité. | S | ✅ |
| 23 | Mineur | Assets | `public/home/advance-manufacture-building.png` | **PNG de 4,60 Mo** (1920×1280), doublon exact du `.webp` de 311 Ko réellement importé par `ProcessSection.tsx:10`. Asset mort qui part au déploiement. Idem `public/home/slider/az4951_wpqknt.webp` et `az5332_i9vpn2.webp` (importés par aucun composant). `public/icon.svg` n'est référencé nulle part. | Supprimer. Viole aussi la règle projet « WebP minimum, jamais PNG/JPG ». | S | ✅ |
| 24 | Mineur | Sitemap | `src/app/sitemap.ts:58-60` | L'entrée `<image:image>` n'est posée que sur `/gallery` **et pointe vers l'image du hero de la home** (`/home/white-gold-set-advance-manufacturer.webp`). La home elle-même, qui rend 23 images dont 12 photos produit, n'a aucune entrée image. | Soit retirer ce bloc (les images sont déjà découvrables via le HTML), soit le rendre correct : images de `/gallery` sur `/gallery`, images produit sur `/`. | S | ✅ |
| 25 | Mineur | i18n / UX | HTML rendu, 0 `<a>` inter-locale | Les `hreflang` EN/FR/x-default sont corrects, mais **aucun sélecteur de langue n'est rendu** : `en.html` ne contient aucun lien vers `/fr` et inversement. Le seul chemin de découverte de l'autre locale est le `hreflang` + la redirection Accept-Language de `next-intl/middleware` (`src/proxy.ts:4`, `localeDetection` activé par défaut). | Ajouter un sélecteur de langue (header ou footer) — bénéfice UX et maillage inter-locale. | M | ❌ |
| 26 | Mineur | Sémantique | `src/app/[locale]/layout.tsx:114` | **Aucun élément `<main>` dans le HTML rendu** (`grep -c '<main' en.html` = 0) : le contenu est dans un `<div className="flex flex-1 flex-col">`. Pas de lien d'évitement (« skip to content ») non plus. 7 `<section>` sur 8 sont sans nom accessible (seul le hero a `aria-labelledby`). | Remplacer le `<div>` par `<main id="main">` + skip link. | S | ✅ |
| 27 | Nit | robots | `src/config/seo/robots.ts:9-10` + `next.config.ts:50-53` | `noai, noimageai` dans le `<meta name="robots">` et `X-Robots-Tag: noimageai` sur `/_next/image`. Ce sont des conventions Spawning/DeviantArt, **pas des directives Google**. Google ignore les jetons inconnus du `X-Robots-Tag` — donc **aucun impact négatif sur Google Images** (ni `noindex` ni `noimageindex` n'est présent, et `max-image-preview:large` est bien conservé). Portée réelle proche de zéro : quasi aucun crawler ne les honore. Le vrai blocage utile est celui de `robots.txt` (GPTBot, Google-Extended, ClaudeBot, CCBot…), qui lui est effectif. | Conserver (inoffensif) ou nettoyer par souci de lisibilité. Noter que `Google-Extended: Disallow: /` exclut le site du grounding Gemini/AI Overviews — choix délibéré à confirmer avec le client. | S | ❌ |
| 28 | Nit | Marque / palette | `src/config/site.ts:24` | `themeColor: "#101E33"` (bleu marine) — valeur **absente du design system** : `--color-brand: #176a60` (teal), `--color-gold: #a6863f`, `--foreground: #1a1c1b`, `--background: #f9f9f7`. Ce `#101E33` pilote à la fois `<meta name="theme-color">`, `manifest.theme_color` et le fond de l'image OG. | Aligner sur `#1a1c1b` (charcoal, cohérent avec le header/footer) ou `#176a60`. | S | ✅ |
| 29 | Nit | Metas | HTML rendu, `<meta name="keywords">` | 10 keywords rendus par locale. Ignorés par Google et Bing depuis 2009/2014. Non nuisibles, mais `assertKeywords` (`meta.ts:65-76`) **fait échouer le build** si un namespace n'en déclare pas → contrainte de maintenance pour un signal nul. Aucun autre consommateur de `metaKeywords` dans le repo. | Garder si un outil interne les exploite ; sinon assouplir l'assertion. | S | ❌ |
| 30 | Nit | Header | HTML rendu, offsets 9234-15046 | Les 8 liens de nav sont rendus **deux fois** (barre desktop + drawer mobile), tous deux dans le HTML. Aucun impact de ranking, mais double la surface d'ancres identiques et le poids DOM du header. | Non bloquant. Une seule liste partagée avec bascule CSS si une refonte du header a lieu. | M | ❌ |

## 2. Outline h1–h3 rendu

**Ordre du DOM final** (après application de `$RC` ; dans l'ordre des octets du HTML brut, le H2 « Join Our Newsletter List » du footer sort **avant** le H1 — cf. constat #2).

**EN — `en.html`**
```
H1  Manufacturers of Fine Gold & Silver Jewelry        (HeroHome)
    — StatsSection : AUCUN heading
    — AboutSection : AUCUN heading  ← trou signalé
H2  Certification & Sustainability                     (CertificationSection)
H2  Why Brands Choose Advance                          (ValuePropSection)
  H3  Complete In-House Production
  H3  Scalable Manufacturing
  H3  Technical Expertise
  H3  Reliable Communication
H2  Materials & Standards                              (MaterialsSection)
  H3  Metals
  H3  Gemstones
  H3  Plating
  H3  Categories
H2  How We Work                                        (ProcessSection)
  H3  Design Review & Feasibility
  H3  CAD Development
  H3  Prototype & Sampling
  H3  Production
  H3  Quality Control
  H3  Worldwide Shipping
H2  Gallery                                            (GallerySection)
H2  Join Our Newsletter List                           (Footer — global)
```

**FR — `fr.html`** (structure strictement identique)
```
H1  Fabricant de bijoux fins en or & argent
H2  Certification & durabilité
H2  Pourquoi les marques choisissent Advance
  H3  Production entièrement intégrée / Fabrication évolutive / Expertise technique / Communication fiable
H2  Matériaux & standards
  H3  Métaux / Gemmes / Placage / Catégories
H2  Comment nous travaillons
  H3  Revue de conception & faisabilité / Développement CAO / Prototype & échantillonnage / Production / Contrôle qualité / Expédition mondiale
H2  Galerie
H2  Rejoignez notre newsletter
```

Bilan : **un seul H1** ✅ — aucun saut de niveau ✅ — mais **14 H3** (et non 8 : 4 ValueProp + 4 Materials + 6 Process), **2 sections sans heading** (Stats, About), et le premier H2 de contenu (`Certification`) arrive après ~180 mots non structurés. Aucun H2 ne contient le mot-clé cible de sa locale : ni « jewellery »/« Thailand » en EN, ni « grossiste »/« en gros » en FR.

## 3. JSON-LD rendus

3 blocs par page, **tous parsent sans erreur** (`json.loads` OK sur EN et FR).

**① `JewelryStore` (`#organization`)** — `name: "ADV MFR"`, `legalName: "Advance Manufacturers Co., Ltd."`, `url`, `logo` (SVG, URL nue), `email`, `telephone`, `faxNumber`, `taxID: "0105532085411"`, `address` (PostalAddress complète Bangkok 10520 TH), `geo` (13.7787 / 100.7880), `sameAs` (LinkedIn, Instagram, Facebook). Identique EN/FR — correct pour la déduplication par `@id`.
→ Erreurs : type inadapté (#6), `name` = abréviation (#7), `logo` sans dimensions/format raster (#8), propriétés E-E-A-T manquantes (#9).

**② `WebSite` (`#website`)** — `name`, `url`, `inLanguage: ["en","fr"]`, `publisher → #organization`. Pas de `potentialAction`/`SearchAction` — **correct**, le site n'a pas de recherche interne ; en ajouter une serait une déclaration fausse. Manque : `description`.

**③ `WebPage`** — `name` (= `<title>` sans le suffixe marque), `description` (= meta description), `url`, `inLanguage`, `isPartOf → #website`.
→ **Erreur bloquante** : en FR, `url` = `https://www.advmfr.com` au lieu de `https://www.advmfr.com/fr` (#1).
→ Manques : `primaryImageOfPage`, `about → #organization`, `dateModified`, `inLanguage` correctement localisé (OK ici : `en` / `fr`).

**`BreadcrumbList`** — absent sur la home : **normal et correct** (un fil d'Ariane à un seul niveau n'apporte rien ; le builder `breadcrumbJsonLd` existe déjà pour les pages profondes).
**Non exploités alors que le contenu s'y prête** : `ImageGallery` pour les 12 parures, `FAQPage` (les 8 questions prévues dans la source hors repo ne sont pas implémentées), `Service`/`Offer` pour OEM/ODM.

## 4. Images de la Home

23 `<img>` rendus, tous en WebP source, tous servis via `/_next/image` (AVIF puis WebP, `qualities: [75]`, `deviceSizes` sans 4K — config `next.config.ts:13-20` correcte).

| # | Fichier source | Dimensions / poids source | `sizes` rendu | Chargement | Alt EN / FR | Verdict |
|---|---|---|---|---|---|---|
| 1 | `home/white-gold-set-advance-manufacturer.webp` | 3000×2000 · 278 Ko | `(min-width:1024px) 50vw, 100vw` | `priority` → **preload en `<head>`** ✅ ; pas de `loading`, pas de `fetchpriority="high"` sur le `<img>` | « A finished white gold and silver jewelry set crafted in the Advance Manufacturers workshop » / « Parure en or blanc et argent fabriquée dans les ateliers Advance Manufacturers » | **LCP correct.** Alt descriptif et naturel, mais en EN il dit *jewelry* (US) alors que la cible est *jewellery* (UK). Nom de fichier ✅ (le seul explicite du lot). |
| 2 | `home/slider/az4948_ggpzcg.webp` (StatsSection) | 3000×2000 · 273 Ko | `(min-width:1024px) 50vw, 100vw`, `src` de repli en **`w=2048`** | lazy | `alt=""` + `aria-hidden` | ⚠️ Décoratif à **opacity .16** mais servi en pleine résolution. Nom Cloudinary. Doublon du slide 8 avec un alt différent. |
| 3 | `production/craftspeople.webp` | 1000×750 · 193 Ko | `(min-width:1024px) 42vw, 100vw` | lazy, `width/height` explicites ✅ | « Advance Manufacturers artisans hand-setting stones at the workbench » / « Artisans d'Advance Manufacturers sertissant des pierres à l'établi » | ✅ Alt et nom de fichier exemplaires. |
| 4 | `home/rjc-certified-member.webp` | 1253×800 · 23 Ko | `(min-width:640px) 400px, 100vw` | lazy, dimensions ✅ | « Responsible Jewelry Council certified member certificate — COP 0000 7330, LGM 0000 7445 » / idem FR | ✅ Excellent (n° de certificat = signal de confiance). Note : « Jewelry » est ici le nom propre de l'organisme — correct de ne pas le franciser ni l'angliciser. |
| 5 | `production/metal-xray-clean.webp` | **800×618** · 30 Ko | `100vw` | lazy, `fill` | « Raw gold and precious metals prepared for jewelry production » / « Or brut et métaux précieux préparés pour la fabrication de bijoux » | ⚠️ **Sous-dimensionnée** : source 800 px de large pour un fond `100vw` en `object-cover` → upscale visible ≥ 800 px de viewport. |
| 6 | `design/predesign.webp` | 1200×800 · 33 Ko | `(min-width:1024px) 33vw, (min-width:640px) 50vw, 100vw` | lazy | « Design team reviewing sketches and assessing production feasibility » / FR équivalent | ✅ `sizes` cohérent avec la grille 3 colonnes. |
| 7 | `design/cadcam.webp` | 1200×800 · 41 Ko | idem #6 | lazy | « Jeweler modeling a piece in CAD software before production » | ✅ |
| 8 | `design/sampling.webp` | 1200×855 · 42 Ko | idem #6 | lazy | « Prototype rings and samples produced from approved CAD models » | ✅ |
| 9 | `production/wax-injection.webp` | 1000×750 · 57 Ko | idem #6 | lazy | « Wax injection during the jewelry casting process on the production floor » | ✅ |
| 10 | `production/polishing.webp` | **1000×1500** (portrait) · 54 Ko | idem #6 | lazy, `fill` dans un `aspect-[4/3]` | « Craftsman polishing a finished piece during quality control » | ⚠️ Source portrait recadrée en paysage 4/3 par `object-cover` → ~55 % de l'image jetée. |
| 11 | `home/advance-manufacture-building.webp` | 1920×1280 · 304 Ko | idem #6 | lazy | « Advance Manufacturers facility preparing finished jewelry for worldwide shipping » | ✅ (mais un **doublon PNG de 4,6 Mo** traîne à côté — cf. #23). |
| 12 | `home/slider/az4393_r1wq6o.webp` | 3000×2000 · 167 Ko | `(min-width:1440px) 1312px, 100vw` | **`loading="eager"` → preload en `<head>`** | « Two-tone gold suite: textured bar pendant, matching ring and huggie hoops, each lined with pave-set diamonds » / FR équivalent | ❌ **Préchargée alors qu'elle est en bas de page** (#5). Alt de très bonne qualité. |
| 13-23 | `az4457`, `az4949`, `az4964`, `az5056`, `az5274`, `az4431`, `az4948`, `az4967`, `az5276`, `az5279`, `az5327` | toutes **3000×2000**, 117-284 Ko | `(min-width:1440px) 1312px, 100vw` | lazy, `placeholder="blur"` | Alts EN et FR très détaillés (métal, technique de sertissage, pierre, pièces de la parure) | ✅ **Alts remarquables** — descriptifs, spécifiques, sans bourrage. ❌ **Noms de fichiers Cloudinary opaques** (`az5274_qlx7ak`) : signal Google Images perdu sur 12 photos produit. |

Transverse :
- **Aucun bourrage de mots-clés** dans les alts — c'est un point fort réel de la page.
- **Cohérence orthographique EN à corriger** : les alts EN utilisent *jewelry* (US) alors que le `<title>`/meta ciblent *jewellery* (UK).
- **Nommage** : 13 fichiers sur 23 portent un identifiant Cloudinary sans sens. Recommandation confirmée : renommer au format `<matiere>-<type>-advance-manufacturers.webp` **au moment de la livraison des nouvelles photos client** (renommer maintenant casserait les imports pour un gain marginal).
- Le blur `placeholder` inline pèse 15,7 Ko de base64 sur 214 Ko de HTML (7 %) — acceptable.

## 5. Écarts source SEO hors repo ↔ page rendue

Comparaison **intra-locale uniquement** — aucune correspondance EN/FR n'est suggérée, chaque locale se juge sur ses propres données de recherche.

### EN — `~/Documents/Advmfr/website/pages/home/en/` (`primary_keyword: "jewellery manufacturing"`, KD 8, vol. 2600)

| Élément | Source | Rendu | Écart |
|---|---|---|---|
| `<title>` | `Fine Jewellery Manufacturer in Thailand \| Advance Mfrs` | `Fine Jewellery Manufacturer in Thailand \| ADV MFR` (49 car.) | ✅ Partie éditoriale identique. Suffixe marque = `ADV MFR` (abréviation) au lieu d'`Advance Mfrs`. À trancher : cohérent avec `site.name`, mais l'abréviation est moins recherchée que le nom complet. |
| Meta description | `…with 30+ years' experience…` | `…Bangkok, 30+ years' experience…` (157 car.) | ✅ Quasi identique. ❌ Le « 30+ » contredit le « 37 » affiché sur la page (#16). |
| **H1** | **`Fine Jewellery Manufacturer in Thailand`** — la source précise explicitement : *« H1 upgraded from 'Manufacturers of Fine Gold & Silver Jewelry' to 'Fine Jewellery Manufacturer in Thailand' »* | **`Manufacturers of Fine Gold & Silver Jewelry`** — c'est-à-dire **exactement l'ancien H1 que la source dit avoir remplacé** | ❌ **Écart majeur.** L'arbitrage SEO n'a pas été appliqué au code. |
| Orthographe | *« spelling normalised to UK per user decision »* | 0 × « jewellery » dans le corps, 5 × « jewelry » | ❌ Décision non appliquée. |
| H2 | 10 H2 attendus (« What a Jewellery Manufacturer Actually Does », « Our Jewellery Manufacturing Capabilities », « Materials We Work With »…) | 5 H2 de contenu, aucun ne reprend le champ lexical cible | ❌ Structure divergente. « Materials & Standards » est le seul cousin de « Materials We Work With ». |
| Volume | 3 840 mots (cible 3 600-4 000, statut « ON TARGET ») | **433 mots** | ❌ ~11 % du volume prévu. |
| Maillage | 9 cibles distinctes / 17 liens, dont `/oem-jewellery-manufacturer/`, `/wholesale-suppliers/`, `/categories/` | 5 cibles internes contextuelles depuis le contenu (hero ×2, about ×1) — et **les 3 routes ci-dessus n'existent pas dans `routing.pathnames`** | ❌ Architecture d'URL divergente : ces 3 pages de la stratégie SEO ne sont pas implémentées. |
| Schema prévu | `Organization + LocalBusiness (geo)`, `WebPage with author`, `FAQPage` (8 questions), `BreadcrumbList` | `JewelryStore`, `WebSite`, `WebPage` (sans `author`) | ⚠️ Partiellement couvert ; `FAQPage` absent faute de FAQ sur la page. |

### FR — `~/Documents/Advmfr/website/pages/home/fr/` (`primary_keyword: "grossiste bijoux"`)

| Élément | Source | Rendu | Écart |
|---|---|---|---|
| `<title>` | `Grossiste en bijoux & fabricant direct \| Advance Mfrs` | `Grossiste Bijoux : Fournisseur Accessoires Mode \| ADV MFR` (57 car.) | ⚠️ Les deux ciblent « grossiste bijoux ». Le rendu ajoute « Fournisseur Accessoires Mode » et **perd « fabricant direct »** — or c'est précisément le pivot stratégique documenté dans la source (`intent_note` : positionner ADVMFR en *fabricant-grossiste* pour réconcilier l'intention SERP « bijoux fantaisie » avec une offre or/argent). |
| Meta description | `Grossiste en bijoux et fabricant direct depuis 30 ans : or, argent 925 et laiton plaqué au vrai prix d'usine. OEM/ODM, certifié RJC. Contactez-nous.` | `Découvrez nos bijoux raffinés, bagues, boucles d'oreilles, pendentifs et accessoires pour boutiques, commerçants et magasins. Achat en gros garanti !` | ❌ Aucun élément commun hors « bijoux ». Le rendu perd : fabricant direct, or/argent 925/laiton, prix d'usine, OEM/ODM, RJC. |
| **H1** | `Grossiste en bijoux & fabricant direct depuis plus de 30 ans` | `Fabricant de bijoux fins en or & argent` | ❌ Le H1 rendu **ne contient pas « grossiste »**, le mot-clé principal FR. |
| H2 | 9 H2 (« Qu'est-ce qu'un grossiste en bijoux ? », « Grossiste, fabricant ou marketplace »…) | 5 H2, aucun ne contient « grossiste » ni « en gros » | ❌ |
| Volume | 2 057 mots (statut « slightly under », bat tous les concurrents FR) | **505 mots** | ❌ ~25 % du volume prévu. |
| Champ lexical | `grossiste bijoux`, `grossiste bijouterie`, `bijoux en gros`, `argent 925` | « grossiste » ×2, « bijouterie » 0, « en gros » 0, « argent 925 » 0, « joaillerie » 0 | ❌ |
| `metaKeywords` | Set FR issu de la recherche (`grossiste bijoux`, `grossiste bijoux argent`…) | Set traduit de l'EN, tout en « joaillerie » (mot absent de la page) | ❌ Cf. #11. |

**Écart transverse** : la source FR note que l'architecture d'URL FR n'était pas arrêtée (« *only /fr/contact/ used* ») — elle l'est désormais (`routing.pathnames`, 9 routes FR localisées, sitemap correct). La source SEO FR est donc en retard sur le code sur ce point précis, et à mettre à jour côté source.

## 6. Top 5 des actions

1. **Réparer les deux bugs qui envoient de mauvais signaux à Google** — le `WebPage` JSON-LD FR qui déclare l'URL EN (#1), et l'écrasement des metas `openGraph`/`twitter` par `buildPageMetadata` qui fait perdre `og:type`, `og:site_name`, `twitter:site` et dégrade la carte X en `summary` (#4). Deux correctifs de quelques lignes, effet immédiat, portée site entier. **Effort S.**

2. **Faire coïncider le H1 avec le `<title>` dans chaque locale** (#15, #3) — EN : le H1 rendu est littéralement l'ancien titre que la source SEO documente avoir remplacé ; FR : le H1 ne contient pas « grossiste ». Dans la foulée, normaliser l'orthographe EN sur *jewellery* (0 occurrence actuellement, contre 5 *jewelry*) et introduire « Thailand » dans le corps. Le H1 est le seul heading que Google lit avant tout le reste. **Effort S.**

3. **Rendre le contenu visible sans exécution JS** (#2) — aujourd'hui le HTML brut de la home ne contient qu'un skeleton ; H1, JSON-LD, images et liens vivent dans un `<div hidden>` réactivé par script. Googlebot s'en sortira, mais les crawlers de citation que le `robots.txt` autorise explicitement (Perplexity, OAI-SearchBot, Claude-SearchBot) et les scrapers sociaux, non. La route est pourtant 100 % prérendue : le `loading.tsx` au niveau `[locale]` est à descendre sur les segments réellement dynamiques. **Effort M, gros levier GEO.**

4. **Ouvrir le maillage depuis les sections de la home** (#12, #13) — 5 sections sur 8 n'émettent aucun lien : « Gallery » ne pointe pas vers `/gallery`, « How We Work » pas vers `/production-process`, « Certification » pas vers `/sustainability`, et le footer omet `/gallery`, `/design-development`, `/news-events`. Ajouter un CTA contextuel par section + compléter le footer transmet du PageRank interne avec des ancres descriptives, là où il n'y a aujourd'hui que la nav dupliquée. **Effort S.**

5. **Lot de quick wins techniques** (#5, #21, #20, #14, #26) — retirer le `loading="eager"` du slide 0 qui fait précharger une image de bas de page en concurrence du LCP ; cesser de précharger Playfair Display, jamais utilisé sur la home ; déclarer dans le manifest les icônes 192/512 qui existent déjà dans `public/` ; ajouter un `h2 sr-only` à la section About (aucun heading actuellement) ; remplacer le wrapper `div` par un `<main>`. Cinq modifications d'une à trois lignes chacune, aucun impact visuel. **Effort S global.**
