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. Gabaritsrc/components/patterns/marketing/agent-page/**, chargeursrc/app/(marketing)/agents/[slug]/_components/**, copymessages/*.json→marketingPages.agents.page, vérité produitsrc/features/ai/agents/identity-registry.tsetsrc/features/ai/agents/agents/specialists.ts.Référence : la page Atlas approuvée (
standalone.htmldu paquetatlas-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 :
| Section | Manque ou écart constaté | Pages | Correction |
|---|---|---|---|
| Hero | Deux 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 |
| Hero | Promesses vagues (« Analyse votre marché en continu », « Alimente vos insights et systèmes ») | toutes | Verbes d'action + objet concret (« Délègue au bon spécialiste », « Chiffre la marge de chaque prix ») |
| Cas d'usage | Une ou deux cartes seulement : otis 1, sam 1, maya 2, marco 2 (le carrousel de trois cartes était à moitié vide) | 4 spécialistes | Trois scénarios par agent, écrits dans le catalogue, légende « exemples illustratifs, boutiques et chiffres fictifs » |
| Cas d'usage | Réponses sans chiffre ni exemple de boutique, sans prochaine étape | toutes | Chiffres illustratifs, exemple de boutique, fin sur une étape à valider (« Je la confie à @Marco pour vendredi ? ») |
| Capacités | Items « quoi » sans « avec quel outil » ni « pour quel bénéfice » | toutes | Formule QUOI + OUTIL + BÉNÉFICE, chaque outil vérifié dans le toolset du runtime |
| Capacités | La pastille de la carte signée faisait grandir la carte (+22 px à 1440) | atlas | Pastille qui ne grandit plus la ligne (§3) |
| Mur | Aucune branche « vrais avis » | toutes | Branche avis réels + repli sur les cartes signées par l'agent (§5) |
| FAQ | Absentes : « @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 ? » | toutes | Les quatre ajoutées pour les six agents (onglet Fonctionnement : 3 → 4 questions) |
| FAQ | Réponses de remplissage (« Où connaître les offres disponibles ? » renvoyait à une démo) | atlas | Question prix : crédits inclus dans les plans payants, renvoi à la page Tarifs |
| Clôture | Le lien « Rencontrer toute l'équipe » ajoutait 24 px (1440) à 32 px (1920) au bandeau | toutes | Lien sorti du flux, dans le padding bas du bandeau : zéro hauteur ajoutée |
| Intégrations | Pas de bandeau d'intégrations | toutes | Volontairement 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) | hero | cas d'usage | capacités | mur | FAQ | clô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, 390 | la 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) | hero | cas d'usage | capacités | mur | FAQ | clô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
| Fichier | Correction | Mesure |
|---|---|---|
agent-page-closing.tsx / .module.css | Le 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.css | margin-block: -0.3em : la pastille (≈ 18 px) ne grandit plus la ligne de 14,7 px qui la porte | carte signée et ligne « Veille marché » : Δh +5,6 → 0 (1440) |
agent-page-disclosure.module.css | Bordure 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 arrondie | capacités 10,92 % → 6,24 %, FAQ 5,18 % → 2,91 % (1440) |
agent-page-faq.module.css | Même bordure sur les onglets | idem |
agent-page-disclosure.module.css | Un seul chevron qui bascule en rotateX(180deg) sur 0,45 s au lieu de l'échange instantané bas/haut du primitif | parité mouvement (§4) |
agent-page-wall.tsx / .module.css | Ouverture 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 tabulation | parité mouvement (§4) |
agent-page-wall.module.css | letter-spacing: normal sur la pastille « voir plus » (espacement UA d'un bouton, mesuré) | largeur −2,4 → 0 px |
agent-page-disclosure.module.css | animation: none sur le panneau : le keyframe Tailwind du primitif ouvrait le panneau d'un coup et court-circuitait la transition de hauteur | accordé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 px | clô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.
| Mouvement | Référence | Nous, avant la passe | Nous, après | Parité |
|---|---|---|---|---|
| Anneaux du hero (et du bandeau de clôture) | 10 s linéaire, 36°/s, pause hors écran | 36°/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 ms | 0 % · 26 % · 91,5 % · 100 % | inchangé | ✅ identique |
| Carrousel des scénarios (bouton suivant) | 14,6 % · 89,3 % · 99,5 % à 150 / 300 / 450 ms | 14,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 transition | 2 % · 90 % · 99 % (≈ 40 ms de latence React au départ) | ✅ corrigé |
| Chevron de l'accordéon | un seul glyphe, rotateX(180deg) en 0,45 s | échange instantané bas / haut | un glyphe, rotateX(180deg) en 0,45 s | ✅ corrigé |
| Onglets FAQ : pastille active | 26 % · 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 volet | translateX(1em) + opacité, 0,45 s | idem, mais aussi au premier affichage | opacité 0,02 → 0,79 → 0,98, translation 12 → 2,5 → 0,2 px | ✅ |
| Mur : ouverture, fondu | mask-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'instant | masquée à l'instant | ✅ |
| Ligne d'avis (étoiles décalées) | 0,58 s + étoiles 0,42 s décalées de 0,085 s | idem | inchangé | ✅ (visible seulement avec un vrai résumé Trustpilot) |
prefers-reduced-motion | anneaux à l'arrêt, toutes les transitions coupées | anneaux arrêtés, carrousel, accordéon, onglets, mur et survol instantanés | idem, 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 deTestimonialWalldevra 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 (tagagentsou@Prénomdans le texte), puis tous les autres, neuf au plus. SousREVIEW_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 rendnull.- 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) etagent-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) esttest; @Marco écrit viashopifyAdminGraphQLmais ne modifie pas le thème (edit.themerefusé) ; @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:ctavertes), personas toujours préfixés@, @Atlas au masculin. - Chiffres : la garde
marketing-proof-is-placed.test.tsexige qu'un chiffre demarketingPages.*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 plafondagents: 22avec cette raison. Alternative écartée pour l'instant : sortir chaque chiffre dans une fixture ICU commeagent-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
| Porte | Résultat |
|---|---|
pnpm i18n:parity | vert, 13 798 clés dans les six langues, 0 manquante, 0 en trop, 0 écart ICU |
pnpm copy:dashes · copy:case · copy:cta | verts |
pnpm pages:audit | 56 pages, aucun nouveau rouge |
vitest run src/app/(marketing)/agents src/components/patterns/marketing src/test src/lib/content/reviews.test.ts | 482 fichiers, 7 386 tests verts |
tsc --noEmit -p tsconfig.json | vert |
eslint --max-warnings 0 sur les répertoires touchés | vert |
pnpm deadcode | aucune 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.