Le crème vivait sur `<body>`, que Gmail supprime en réécrivant le message : la
carte blanche se retrouvait collée au fond du client, blanc sur blanc chez les
uns, bloc éblouissant sur fond noir chez les autres. Elle n'a plus de fond du tout
— autour d'elle passe celui du lecteur, et c'est la bordure qui délimite, ce qui
reste juste sur n'importe quel fond, y compris ceux qu'on ne connaît pas. Le mode
sombre est déclaré (`color-scheme`) plutôt que subi : sans cette déclaration, Gmail
et Apple inversent au jugé et le vert de marque en ressort délavé.
Un piège qui ne se voyait qu'à l'écran : la marge intérieure était posée sur les
`<table>`. En modèle de bordures fusionnées — celui de tout mail, et celui
qu'Outlook applique de toute façon — la spécification CSS ignore la marge d'un
élément `table`. Tout le message se collait aux bords. Elle est passée sur les
`<td>`. Un châssis unique remplace les trois mises en page séparées.
Le logo entre dans les mails, en image et non en texte, sur un bandeau qui reste
sombre dans les deux modes : il n'a ainsi qu'une seule version à exister, juste
partout. `public/` n'existait pas, et `output: "standalone"` ne le trace pas —
sans la ligne ajoutée au Dockerfile le logo aurait répondu 404 chez tous les
destinataires, panne invisible en développement.
Les fiches promettaient 24 fois un geste qui n'existe pas — « si c'est déjà fait,
dis-le-moi et je me tais », « dis-moi sa date d'expiration et je gère le tempo ».
Le pied de page avait déjà été purgé de la même promesse ; le corps la répétait.
Retirées sans remplacement : elles reviendront le jour où l'état utilisateur
s'écrira. Le bloc d'accompagnement du rappel groupé disparaît aussi — « pour que
tu n'aies pas à y penser toi-même » se démentait dans le seul message qui demande
justement d'y penser. Et chaque échéance dont la fiche a une page publique porte
désormais un lien vers elle : la bicéphalie ne servait que le SEO, alors que c'est
au destinataire d'un rappel que l'explication entière manque le plus.
Enfin, le lien d'activation. Il n'était pas un raccourci : sa vérification était
la seule chose au monde capable de créer le compte de l'acheteur, better-auth ne
le créant qu'à ce moment, `resolveMembership` n'inscrivant le premier membre que
s'il y a une session, et `destinatairesFoyer` ne connaissant que les
appartenances. Un mail lu le soir, un lien ouvert deux fois, un antispam qui le
déclenche à vide, et un foyer payé 19,99 € restait sans compte, sans appartenance,
donc sans destinataire de rappel, définitivement. Ce n'est pas un cas de bord : ce
message n'est envoyé que quand personne n'était connecté avant de payer, donc
l'unique mail d'activation qui existe était précisément celui dont l'expiration
était fatale. Le webhook crée le compte et l'appartenance lui-même, depuis
l'adresse que Stripe vient de collecter. Contrepartie assumée : Stripe ne prouve
pas la possession de la boîte, et qui paierait pour l'adresse d'un tiers lui
rattacherait un foyer — un ajout indésirable, jamais une prise de contrôle, et
facturé à celui qui le tente.
`npm run mails:echantillon` rend un exemplaire de chaque message depuis les vraies
fiches, et, si une adresse est donnée, le rappel réel de ses foyers.
D-048, D-049.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>