Audits · septembre 2026Pages agents : passe de finition (pixel, mouvement, copy, avis)

Pages agents : passe de finition (pixel, mouvement, copy, avis)

2026-09-26, pilier growth-web. Périmètre : les six pages /agents/[slug] (atlas, maya, marco, otis, faye, sam) dans les six langues. Gabarit src/components/patterns/marketing/agent-page/, chargeur…

2026-09-26, pilier growth-web. Périmètre : les six pages /agents/[slug] (atlas, maya, marco, otis, faye, sam) dans les six langues. Gabarit src/components/patterns/marketing/agent-page/**, chargeur src/app/(marketing)/agents/[slug]/_components/**, copy messages/*.json → marketingPages.agents.page, vérité produit src/features/ai/agents/identity-registry.ts et src/features/ai/agents/agents/specialists.ts.

Référence : la page Atlas approuvée (standalone.html du paquet atlas-page, hors dépôt), mesurée par le harnais de comparaison (texte par texte, boîte par boîte, puis diff pixel par section). Aucun code ni asset du paquet n'est repris : les valeurs relevées (durées, courbes, rayons, bordures) sont réécrites dans nos modules CSS.

1. Ce qui manquait, page par page et section par section

Constat avant la passe, sur les six pages :

SectionManque ou écart constatéPagesCorrection
HeroDeux des quatre promesses portaient une pastille « En développement » qui ajoutait une ligne à la grille 2 × 2 (+29 px à 1440, contenu recentré, CTA décalés de 14,6 px)atlas (b, c), maya (a, b)Le hero ne promet plus que ce qui est livré : aucune pastille par construction, hauteur identique à la référence
HeroPromesses vagues (« Analyse votre marché en continu », « Alimente vos insights et systèmes »)toutesVerbes d'action + objet concret (« Délègue au bon spécialiste », « Chiffre la marge de chaque prix »)
Cas d'usageUne ou deux cartes seulement : otis 1, sam 1, maya 2, marco 2 (le carrousel de trois cartes était à moitié vide)4 spécialistesTrois scénarios par agent, écrits dans le catalogue, légende « exemples illustratifs, boutiques et chiffres fictifs »
Cas d'usageRéponses sans chiffre ni exemple de boutique, sans prochaine étapetoutesChiffres illustratifs, exemple de boutique, fin sur une étape à valider (« Je la confie à @Marco pour vendredi ? »)
CapacitésItems « quoi » sans « avec quel outil » ni « pour quel bénéfice »toutesFormule QUOI + OUTIL + BÉNÉFICE, chaque outil vérifié dans le toolset du runtime
CapacitésLa pastille de la carte signée faisait grandir la carte (+22 px à 1440)atlasPastille qui ne grandit plus la ligne (§3)
MurAucune branche « vrais avis »toutesBranche avis réels + repli sur les cartes signées par l'agent (§5)
FAQAbsentes : « @X ou ChatGPT : quelle différence ? », « Puis-je voir ce que @X a fait ? », « Puis-je personnaliser @X ? », « Faut-il configurer les intégrations soi-même ? »toutesLes quatre ajoutées pour les six agents (onglet Fonctionnement : 3 → 4 questions)
FAQRéponses de remplissage (« Où connaître les offres disponibles ? » renvoyait à une démo)atlasQuestion prix : crédits inclus dans les plans payants, renvoi à la page Tarifs
ClôtureLe lien « Rencontrer toute l'équipe » ajoutait 24 px (1440) à 32 px (1920) au bandeautoutesLien sorti du flux, dans le padding bas du bandeau : zéro hauteur ajoutée
IntégrationsPas de bandeau d'intégrationstoutesVolontairement non ajouté (§6)

Exceptions acceptées et documentées (décision de gouvernance, 2026-09-26), gardées en minimisant leur impact :

  • Pastilles de statut : toujours visibles, jamais cachées. Elles ne vivent plus dans le hero ni dans les scénarios (un scénario non livré est un item de roadmap, copy.md §7), et elles ne font plus grandir la ligne qui les porte (marges verticales négatives, §3). Elles restent sur une ligne de capacité, une carte du mur et une réponse de FAQ par agent, là où le produit n'est pas encore livré, avec la phrase qui le dit.
  • Lien vers l'équipe : conservé sous les CTA, sans hauteur ajoutée.
  • Ligne Trustpilot du hero : rendue seulement depuis un résumé réel (readTrustpilotSummary), jamais par défaut. En son absence le harnais masque aussi la ligne côté référence.

2. Écarts pixel, par largeur

Mesure : compare-uw (notre /fr/agents/atlas rendu à la largeur V, référence rendue à la largeur du panneau P, puisque la page vit dans le panneau du shell marketing). Boîtes en px relatifs à la racine [data-agent-page] ; « Δh » = hauteur de section, nous moins référence ; « diff » = part des pixels qui diffèrent (seuil 48 sur la somme RGB).

Lecture importante. Le texte d'Atlas a été réécrit dans cette passe : à partir de là, le diff pixel mesure aussi un texte différent, et les hauteurs d'une section qui suit le contenu (cartes de scénario, mur, FAQ) suivent la longueur du texte. Les réponses de scénario et le corps de la carte signée ont été resserrés à la longueur de la référence (cinq lignes, environ 170 caractères) pour que la grille reste celle qui a été approuvée. Le fond du hero de la référence charge deux assets externes (anneau SVG, masque de points PNG) que le harnais bloque : notre anneau et notre trame, dessinés en CSS/SVG, comptent donc comme des différences alors que la référence rend un fond vide à cet endroit.

Avant (copy approuvée, gabarit d'avant la passe)

Largeur (panneau)herocas d'usagecapacitésmurFAQclôture
1920 (1482)Δh 0 · 8,21 %Δh +1,8 · 4,14 %Δh 0 · 4,68 %Δh 0 · 2,68 %Δh 0 · 2,91 %Δh +31,6 · 4,38 %
1440 (1002)Δh 0 · 9,92 %Δh +18,5 · 11,35 %Δh +22,4 · 10,82 %Δh 0 · 2,52 %Δh 0 · 4,00 %Δh +23,7 · 5,80 %
1024, 768, 390la première passe du harnais a planté sur la référence à ces largeurs (§2.1), pas de mesure « avant »

À 1440, 92 textes sur 97 appariés étaient décalés de plus de 2 px, tous en aval de la pastille du hero (Δy −14,5 / +14,6), puis de la pastille de la carte (+22,3), puis du lien d'équipe (+64,6 sur les CTA de clôture).

Après (copy réécrite, gabarit corrigé)

Largeur (panneau)herocas d'usagecapacitésmurFAQclôture
1920 (1482)Δh 0 · 2,60 %Δh 0 · 6,88 %Δh 0 · 4,19 %Δh 0 · 6,31 %Δh 0 · 2,39 %Δh 0 · 3,44 %
1440 (1002)Δh 0 · 3,29 %Δh 0 · 8,66 %Δh 0 · 6,24 %Δh 0 · 6,30 %Δh 0 · 2,91 %Δh 0 · 4,02 %
1024 (586)Δh −41,8 · 12,76 %Δh +17,9 · 10,66 %Δh 0 · 8,21 %Δh 0 · 8,51 %Δh 0 · 7,08 %Δh 0 · 6,65 %
768 (750)Δh −26,7 · 8,70 %Δh +22,9 · 9,24 %Δh 0 · 8,83 %Δh −36,0 · 9,85 %Δh 0 · 6,81 %Δh 0 · 6,59 %
390 (372)Δh −1,4 · 7,20 %Δh −4,6 · 12,18 %Δh −21,4 · 12,98 %Δh 0 · 12,19 %Δh −19,1 · 13,53 %Δh 0 · 9,26 %

À 1920 et 1440, les six sections ont exactement la hauteur de la référence, et seulement 19 textes appariés sur 43 bougent de plus de 2 px, tous à cause de leur propre texte (signatures du mur qui suivent la longueur d'une carte, largeur du mot « Découvrez » et du libellé « Scénario », déjà présentes avant la passe). Les écarts restants sous 991 px ont tous été vérifiés côte à côte (cmp-final5/sbs-<largeur>-<section>.png) et viennent tous du texte, jamais de la grille : les promesses du hero de la référence passent sur deux lignes à 586 et 750 px, pas les nôtres (hero plus court) ; une réponse de scénario prend une ligne de plus dans le carrousel à 1,1 carte (+18 / +23) ; le titre du mur tient sur une ligne au lieu de deux à 750 px (−36) ; le titre de la carte signée et une question de FAQ tiennent sur une ligne de moins à 372 px (−21, −19).

Deux corrections de gabarit sont venues de cette mesure sous 991 px :

  • le titre de clôture de la référence est une seule chaîne équilibrée, qui casse au point sur desktop par hasard de largeur. Notre <br> forcé donnait quatre lignes au lieu de trois à 372 px (+47 px, 28,47 % de pixels différents). Il est masqué sous 991 px ;
  • l'espacement entre le texte et les boutons de clôture passe de 3em à 2,25em sous 991 px (le pas « 48 » de la référence vaut 36 px nominaux en tablette, mesuré : 49,1 px sur une base de 21,8 px). Clôture : +12,8 / +16,4 / +11,5 px → 0 à 1024 / 768 / 390.

Contrôle structurel sur les 36 pages (6 agents × 6 langues) × 5 largeurs (shots/final.mjs, 180 rendus, puis 66 rendus de contre-vérification après les derniers titres) : statut 200, aucune clé brute ni {placeholder} visible, aucun tiret, aucun débordement horizontal, et aucune pastille renvoyée seule sur une ligne. Le premier passage en trouvait 15 (surtout en portugais, dont le libellé « Em desenvolvimento » est le plus long) : les titres porteurs d'une pastille ont été raccourcis langue par langue.

2.1 Harnais

Le harnais compare-uw pendait à 1024, 768 et 390 : à une fenêtre de 1024 px, le panneau ne fait que 586 px, et à cette largeur le document.fonts.ready de la référence ne se résout jamais (requêtes de police bloquées). La variante compare-uw2 borne l'attente des polices et des images à 4 s et ouvre la référence dans son propre contexte ; les chiffres « après » ci-dessus en sortent.

3. Corrections du gabarit

FichierCorrectionMesure
agent-page-closing.tsx / .module.cssLe lien d'équipe est positionné sous les CTA, dans le padding bas (5em desktop, 3,75em mobile)clôture Δh +23,7 → 0 (1440), +31,6 → 0 (1920)
feature-status.module.cssmargin-block: -0.3em : la pastille (≈ 18 px) ne grandit plus la ligne de 14,7 px qui la portecarte signée et ligne « Veille marché » : Δh +5,6 → 0 (1440)
agent-page-disclosure.module.cssBordure 2px outset rgb(0 0 0) des déclencheurs, valeur calculée sur la référence (bordure UA jamais remise à zéro, rendue en biseau), déclencheur carré sur la ligne arrondiecapacités 10,92 % → 6,24 %, FAQ 5,18 % → 2,91 % (1440)
agent-page-faq.module.cssMême bordure sur les ongletsidem
agent-page-disclosure.module.cssUn seul chevron qui bascule en rotateX(180deg) sur 0,45 s au lieu de l'échange instantané bas/haut du primitifparité mouvement (§4)
agent-page-wall.tsx / .module.cssOuverture du mur : le fondu glisse (mask-position 0 100 % → 0 0, 0,45 s), la pastille « voir plus » se réduit à 0,9 et s'efface, puis sort de l'ordre de tabulationparité mouvement (§4)
agent-page-wall.module.cssletter-spacing: normal sur la pastille « voir plus » (espacement UA d'un bouton, mesuré)largeur −2,4 → 0 px
agent-page-disclosure.module.cssanimation: none sur le panneau : le keyframe Tailwind du primitif ouvrait le panneau d'un coup et court-circuitait la transition de hauteuraccordéon : 100 % à 150 ms → 2 % · 90 % · 99 % (réf. 15 % · 89 % · 99,6 %)
agent-page-closing.tsx / .module.css<br> du titre masqué sous 991 px (la référence n'en a pas) ; espacement texte / boutons 3em → 2,25em sous 991 pxclôture à 372 px : +47,1 → 0, diff 28,47 % → 9,26 %

4. Parité des animations

Jetons relevés sur la référence : --smooth-ease: cubic-bezier(0.5, 0, 0, 1), --duration-s: 0.45s (toutes les transitions d'interface), rotation des anneaux 10s linear infinite avec pause hors écran, entrée de la ligne d'avis (0.58s cubic-bezier(.22,1,.36,1) à 0,10 s, étoiles 0.42s décalées de 0,085 s à partir de 0,22 s), entrée d'un onglet de FAQ (translateX(1em) → 0 et opacité, 0,45 s). La référence n'a aucune animation d'entrée au défilement sur les sections : nous n'en ajoutons pas (la loi 8 du skill cède ici à la parité, décision du 2026-09-26).

Mesure : shots/motion.mjs déclenche chaque interaction sur les deux pages et relit la propriété animée à t = 0, 150, 300, 450, 800 ms et 2 s. Les deux pages n'ont pas la même largeur (notre panneau de 1002 px contre la référence à 1440 px), donc on compare la fraction du trajet parcourue, pas des pixels.

MouvementRéférenceNous, avant la passeNous, aprèsParité
Anneaux du hero (et du bandeau de clôture)10 s linéaire, 36°/s, pause hors écran36°/s (5,4° en 150 ms), pause hors écran et onglet cachéinchangé✅
Survol du CTA (le libellé monte d'une ligne)0 % · 26 % · 91,5 % · 100 % à 150 / 300 / 450 / 800 ms0 % · 26 % · 91,5 % · 100 %inchangé✅ identique
Carrousel des scénarios (bouton suivant)14,6 % · 89,3 % · 99,5 % à 150 / 300 / 450 ms14,6 % · 89,3 % · 99,5 %inchangé✅ identique
Accordéon des capacités (hauteur du panneau)14,7 % · 89 % · 99,6 %100 % dès 150 ms : le keyframe Tailwind du primitif écrasait la transition2 % · 90 % · 99 % (≈ 40 ms de latence React au départ)✅ corrigé
Chevron de l'accordéonun seul glyphe, rotateX(180deg) en 0,45 séchange instantané bas / hautun glyphe, rotateX(180deg) en 0,45 s✅ corrigé
Onglets FAQ : pastille active26 % · 91,5 % · 100 %8 % · 87 % · 99 % (Base UI publie la boîte un rendu plus tard)inchangé≈ (même courbe, ≈ 50 ms de retard)
Onglets FAQ : entrée du volettranslateX(1em) + opacité, 0,45 sidem, mais aussi au premier affichageopacité 0,02 → 0,79 → 0,98, translation 12 → 2,5 → 0,2 px✅
Mur : ouverture, fondumask-position 100 % → 0 %, 0,45 s : 26 % · 91,5 % · 99,8 %saut instantané (le masque était retiré)4,5 % · 83 % · 98,7 %✅ corrigé (≈ 50 ms de retard React)
Mur : pastille « voir plus »masquée à l'instant (sa règle display: none l'emporte sur son propre fondu déclaré)démontée à l'instantmasquée à l'instant✅
Ligne d'avis (étoiles décalées)0,58 s + étoiles 0,42 s décalées de 0,085 sideminchangé✅ (visible seulement avec un vrai résumé Trustpilot)
prefers-reduced-motionanneaux à l'arrêt, toutes les transitions coupéesanneaux arrêtés, carrousel, accordéon, onglets, mur et survol instantanésidem, chevron compris✅

Captures du hero à t = 0 / 300 / 800 / 2000 ms, nous et référence : scratchpad/shots/motion-{ours,ref}-hero-t{0,300,800,2000}.png. La référence y rend un fond sans anneau : son SVG d'anneau et son masque de points sont des assets distants que le banc bloque. La parité de ce fond se vérifie donc sur ses paramètres (anneau de 62,5em flouté de 1,5em, halo 1440 × 868 flouté de 2em et remonté de 60 %, recouvrement à 6 %, rotation 10 s), pas au pixel.

5. Le mur devient un mur d'avis réels

Demande : réutiliser la source et la logique du mur de la home. Constat : la home (home-sections/testimonials-section.tsx sur TestimonialWall) ne rend aucun avis aujourd'hui. Ses huit cartes sont des exemples de première partie, signés « BoostEcom Demo » et badgés « Demo », tirés du catalogue home.testimonials, et le docblock de TestimonialWall annonce une « seconde branche » pour les vrais avis. Les reprendre sur une page agent aurait présenté des exemples comme des avis.

Ce qui est livré :

  • src/lib/content/reviews.ts : la source des avis publics (PUBLIC_REVIEWS, vide à ce jour). Une ligne n'y entre qu'avec le consentement de son auteur (consentAt), dans la langue où il l'a écrite (jamais traduite en une citation qu'il n'a pas écrite). C'est cette liste que la seconde branche de TestimonialWall devra lire, pour que les deux murs ne se contredisent jamais (à faire côté app-shell, qui possède la home).
  • reviewsForAgentWall(slug, prénom) : les avis qui nomment l'agent d'abord (tag agents ou @Prénom dans le texte), puis tous les autres, neuf au plus. Sous REVIEW_WALL_MIN (six : la fenêtre repliée montre deux rangées de trois ; la home n'a pas de seuil, elle rend des exemples), la fonction rend null.
  • Le mur de la page agent garde son motif (maçonnerie, fondu, pastille « voir plus », accent) et affiche alors étoiles, citation, initiale, nom et rôle ; null : il garde les neuf cartes signées par l'agent. Jamais un mur vide, jamais un exemple présenté comme un avis.
  • Tests : src/lib/content/reviews.test.ts (seuil, priorité, plafond, consentement) et agent-page-content.test.ts (repli sans avis, ordre et libellés avec six avis).

6. Bandeau d'intégrations : non ajouté

Le registre (identity-registry.ts → connectors) nomme Shopify, Klaviyo, Meta Ads et Google, et les connexions OAuth existent dans les réglages de la boutique. Mais aucun outil d'agent ne lit Klaviyo, Meta ou Google aujourd'hui (specialists.ts) : un bandeau de logos promettrait une intégration que l'agent n'utilise pas. La FAQ Intégrations le dit en toutes lettres (« Pas encore directement… en développement »). Le bandeau vaudra la peine le jour où un outil d'agent lira ces sources ; il devra alors dériver ses logos du registre des connecteurs, pas d'une liste.

7. Copy : ce qui a changé et pourquoi

Source : le français, voix de marque. Anglais américain, allemand (vouvoiement, comme le catalogue), espagnol et italien (tutoiement), portugais du Brésil (« você », montants en R$ recalculés pour que la marge de l'exemple de @Marco reste juste). Inspiration de structure : des pages agent de référence (scénario → réponse chiffrée → étape à valider, items QUOI + OUTIL + BÉNÉFICE, FAQ en trois onglets) ; aucun texte, avis ou chiffre repris.

Règles tenues :

  • Vrai d'abord. Chaque promesse a été vérifiée contre le toolset du runtime : @Atlas délègue (delegateTo_*), lit la fiche (searchStoreIntelligence), la base de connaissances (knowledgeSearch) et l'Activité (listStoreActivity, createTask, addNote) ; son cycle quotidien (proactive-cycle.ts) est test ; @Marco écrit via shopifyAdminGraphQL mais ne modifie pas le thème (edit.theme refusé) ; @Otis rédige webhooks et Flow en brouillon mais ne connecte pas lui-même Klaviyo ou Stripe (dev) ; @Faye lit le graphe et prédit, sans lire vos commandes (dev) ; @Sam rédige depuis la base de connaissances, sans lire les commandes Shopify (dev) ni se brancher sur un helpdesk (dit tel quel).
  • Scénarios : chiffres illustratifs, exemple de boutique (bougies, sweats, coffret), dernière phrase = une étape à valider. La légende partagée le dit : « boutiques et chiffres fictifs ». Aucun avis inventé, aucun compteur d'utilisateurs.
  • FAQ : les quatre questions demandées, avec des réponses vérifiables (Activité de la boutique et journal d'audit, niveau d'autonomie lu du registre via {level} / {levelName}, règles de la boutique, connexions OAuth).
  • Forme : aucun tiret cadratin ou demi-cadratin, aucune tournure interdite, aucun CTA « Découvrir / En savoir plus » (gardes copy:dashes, copy:case, copy:cta vertes), personas toujours préfixés @, @Atlas au masculin.
  • Chiffres : la garde marketing-proof-is-placed.test.ts exige qu'un chiffre de marketingPages.* soit un placeholder dérivé du code, ou qu'il vive sous un plafond motivé. Les 22 chaînes chiffrées des scénarios sont des données d'exemple sur des boutiques fictives, pas des affirmations sur le produit : elles ne peuvent pas dériver du code. Elles sont donc sous un plafond agents: 22 avec cette raison. Alternative écartée pour l'instant : sortir chaque chiffre dans une fixture ICU comme agent-scenarios.ts, ce qui multiplierait les valeurs par six langues et deux devises sans rien rendre plus vrai. Point à trancher par le propriétaire s'il préfère la fixture.
  • Parité pixel : réponses de scénario ramenées à la longueur de la référence (≈ 170 caractères en français), promesses du hero ≤ 32 caractères, titres portant une pastille courts (≤ 22 caractères).

Carte des statuts (feature-status-map.ts, graine du PlatformConfig) : réécrite pour la nouvelle copy. À faire côté admin après déploiement : une ligne featureStatus déjà enregistrée dans /admin/content/agent-status s'applique par chemin, donc un statut saisi sur l'ancienne copy (par exemple hero.features.b d'Atlas) s'accrocherait à la nouvelle phrase au même chemin. Réinitialiser la section, ou la relire, au premier passage.

8. Vérifications

PorteRésultat
pnpm i18n:parityvert, 13 798 clés dans les six langues, 0 manquante, 0 en trop, 0 écart ICU
pnpm copy:dashes · copy:case · copy:ctaverts
pnpm pages:audit56 pages, aucun nouveau rouge
vitest run src/app/(marketing)/agents src/components/patterns/marketing src/test src/lib/content/reviews.test.ts482 fichiers, 7 386 tests verts
tsc --noEmit -p tsconfig.jsonvert
eslint --max-warnings 0 sur les répertoires touchésvert
pnpm deadcodeaucune entrée nouvelle dans les fichiers touchés (reviewMentionsAgent, exporté par erreur, est redevenu local)

Fichiers de mesure (hors dépôt, scratchpad/) : cmp-base.log (avant), cmp-final3-{1920,1440}.log et cmp-final5-{1024,768,390}.log (après), sbs-* et diff-* par section, motion.log et shots/motion-report.json, final*.log et shots/final-report.json, captures shots/final-<agent>-<langue>-<largeur>.png.