SPB Git forge

spb/groupe-ka

Public

Groupe KA — site du holding + KA ID (compte unique & SSO des 7 plateformes). Next.js 16, SQLite, Google & Apple login.

81commits 1branches 0releases
89.2 MBsize
maindefault branch
22 days agolast push
TypeScript 70.4% HTML 18.4% JavaScript 4% Python 3.8% CSS 3.4%

design(ka-id): courriels et page de connexion à l'identité « éditorial sharp »

- gabarits Resend refaits en tables HTML compatibles clients courriel : manchette « Groupe [Ka] » à puce lime, carte blanche à bordure encre + ombre décalée dure, bouton pilule encre/lime, panneau code 2FA encre/lime façon carte de membre, micro-étiquettes mono uppercase, pied écosystème
- /connexion recomposée en éditorial HQ : héros display géant à gauche (« Un seul ·Ka pour tout »), points de confiance en filets (lien magique, 2FA, Loi 25), carte d'auth à droite avec en-tête d'inventaire, index des 12 plateformes en filets sous la pliure
- vérifié par captures Chromium headless (courriels + page desktop 1440 et mobile 390) et envoi Resend réel

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Simon-Pierre Boucher committed 1 mo ago (Aug 26, 2026) parent 0d1813c

3 changed files +280 −60

modified src/app/connexion/AuthForm.tsx +6 −0
@@ -205,6 +205,12 @@ export default function AuthForm({
205 205 /* ---------- formulaire principal ---------- */
206 206 return (
207 207 <div className="gk-card p-6 sm:p-8">
208 + <div className="mb-5 flex items-baseline justify-between gap-3 border-b-[1.5px] border-[rgba(20,24,20,0.85)] pb-3">
209 + <span className="klabel !text-ink">Accès KA ID</span>
210 + <span className="gk-mono text-[9.5px] font-bold tracking-[0.14em] text-ink-3 uppercase">
211 + Un compte · tout le groupe
212 + </span>
213 + </div>
208 214 {via && (
209 215 <p className="stat-chip mb-5 !text-[11px]">
210 216 <span className="pulse-dot" aria-hidden="true" />
modified src/app/connexion/page.tsx +110 −16
@@ -1,4 +1,6 @@
1 1 // Auteur : Simon-Pierre Boucher — contact@spboucher.ai
2 +// Connexion KA ID — composition éditoriale « HQ » : héros display à gauche,
3 +// carte d'authentification à droite, index des plateformes en filets.
2 4 import type { Metadata } from "next";
3 5 import { redirect } from "next/navigation";
4 6 import { getSessionUser, safeNext } from "@/lib/auth";
@@ -8,9 +10,42 @@ import AuthForm from "./AuthForm";
8 10 export const metadata: Metadata = {
9 11 title: "KA ID — connexion",
10 12 description:
11 − "Un seul compte pour toutes les plateformes du Groupe KA : créez votre KA ID ou continuez avec Google.",
13 + "Un seul compte pour toutes les plateformes du Groupe KA : créez votre KA ID, connectez-vous avec Google, Apple ou un simple lien par courriel.",
12 14 };
13 15
16 +const PLATFORMS: { name: string; field: string }[] = [
17 + { name: "Lou·Ka", field: "Location" },
18 + { name: "Immo·Ka", field: "Immobilier" },
19 + { name: "Vrai-Prix", field: "Évaluation" },
20 + { name: "Auto·Ka", field: "Véhicules" },
21 + { name: "Food·Ka", field: "Épicerie" },
22 + { name: "Resto·Ka", field: "Restaurants" },
23 + { name: "Sorti·Ka", field: "Sorties" },
24 + { name: "Créa·Ka", field: "Créateurs" },
25 + { name: "Job·Ka", field: "Emplois" },
26 + { name: "Fabri·Ka", field: "Artisans" },
27 + { name: "Trouve·Ka", field: "Annonces" },
28 + { name: "API·Ka", field: "Données" },
29 +];
30 +
31 +const ASSURANCES: { num: string; title: string; desc: string }[] = [
32 + {
33 + num: "01",
34 + title: "Sans mot de passe, si vous voulez",
35 + desc: "Un lien de connexion à usage unique, envoyé par courriel. Un clic, vous êtes connecté.",
36 + },
37 + {
38 + num: "02",
39 + title: "Vérification en deux étapes",
40 + desc: "Chaque connexion par mot de passe est confirmée par un code à 6 chiffres reçu par courriel.",
41 + },
42 + {
43 + num: "03",
44 + title: "Vos données restent ici",
45 + desc: "Conforme à la Loi 25 — aucun traceur publicitaire, aucun témoin tiers, jamais.",
46 + },
47 +];
48 +
14 49 export default async function Connexion({
15 50 searchParams,
16 51 }: {
@@ -28,23 +63,82 @@ export default async function Connexion({
28 63 const viaLabel = sp.via ? (SSO_CLIENTS[sp.via]?.label ?? sp.via) : undefined;
29 64
30 65 return (
31 − <main className="mx-auto max-w-md px-4 pb-16 sm:px-6">
32 − <section className="pt-12 text-center sm:pt-14">
33 − <p className="kicker rise justify-center">
34 − KA ID · un compte, sept plateformes
35 − </p>
36 − <h1 className="gk-display rise mt-4 text-[clamp(30px,5vw,44px)] leading-[1] font-bold tracking-[-0.035em] uppercase [animation-delay:0.08s]">
37 − Un seul <span className="hl">·Ka</span> pour tout
38 − </h1>
39 − <p className="rise mt-4 text-[14px] text-ink-2 [animation-delay:0.16s]">
40 − Votre KA ID vous suit sur Lou·Ka, Immo·Ka, Vrai-Prix, ValoPlex,
41 − Auto·Ka, Fabri·Ka et Food·Ka. Même profil partout — c&apos;est
42 − l&apos;agrégation, appliquée à vous-même.
66 + <main className="mx-auto max-w-6xl px-4 pb-20 sm:px-6">
67 + <section className="grid gap-10 pt-10 sm:pt-14 lg:grid-cols-[minmax(0,1.05fr)_minmax(0,440px)] lg:gap-16">
68 + {/* -------- colonne éditoriale -------- */}
69 + <div>
70 + <p className="kicker rise">KA ID · Un compte, tout l&apos;écosystème</p>
71 + <h1 className="gk-display rise mt-5 text-[clamp(36px,6vw,62px)] leading-[0.96] font-bold tracking-[-0.04em] uppercase [animation-delay:0.08s]">
72 + Un seul <span className="hl">·Ka</span>
73 + <br />
74 + pour tout
75 + </h1>
76 + <p className="rise mt-5 max-w-md text-[14.5px] leading-relaxed text-ink-2 [animation-delay:0.16s]">
77 + Votre KA ID vous suit partout dans le groupe — même profil, mêmes
78 + favoris, même carte de membre, de Lou·Ka à Sorti·Ka. C&apos;est
79 + l&apos;agrégation, appliquée à vous-même.
80 + </p>
81 +
82 + {/* points de confiance — filets éditoriaux (desktop) */}
83 + <div className="rise mt-10 hidden lg:block [animation-delay:0.24s]">
84 + {ASSURANCES.map((a) => (
85 + <div
86 + key={a.num}
87 + className="idx-row grid grid-cols-[44px_1fr] gap-4 py-4 pr-2"
88 + >
89 + <span className="sec-index pt-[3px]">{a.num}</span>
90 + <span>
91 + <span className="gk-display block text-[15px] font-bold tracking-[-0.01em]">
92 + {a.title}
93 + </span>
94 + <span className="mt-1 block text-[12.5px] leading-relaxed text-ink-2">
95 + {a.desc}
96 + </span>
97 + </span>
98 + </div>
99 + ))}
100 + </div>
101 + </div>
102 +
103 + {/* -------- carte d'authentification -------- */}
104 + <div className="rise lg:pt-2 [animation-delay:0.2s]">
105 + <AuthForm next={next} error={sp.error} via={viaLabel} />
106 + {/* rappel de confiance compact (mobile seulement) */}
107 + <p className="gk-mono mt-4 text-center text-[10px] font-bold tracking-[0.12em] text-ink-3 uppercase lg:hidden">
108 + Lien magique · Code 2FA · Loi 25
109 + </p>
110 + </div>
111 + </section>
112 +
113 + {/* -------- index des plateformes -------- */}
114 + <section className="mt-16 sm:mt-24">
115 + <div className="sec-head">
116 + <p className="kicker">Votre KA ID ouvre</p>
117 + <span className="sec-index">
118 + {PLATFORMS.length} plateformes · 1 compte
119 + </span>
120 + </div>
121 + <div className="mt-6 grid grid-cols-2 gap-x-8 sm:grid-cols-3 lg:grid-cols-4">
122 + {PLATFORMS.map((p) => (
123 + <div
124 + key={p.name}
125 + className="idx-row flex items-baseline justify-between gap-3 py-3"
126 + >
127 + <span className="gk-display text-[14.5px] font-bold tracking-[-0.01em]">
128 + {p.name}
129 + </span>
130 + <span className="gk-mono text-[10px] font-bold tracking-[0.1em] text-ink-3 uppercase">
131 + {p.field}
132 + </span>
133 + </div>
134 + ))}
135 + </div>
136 + <p className="gk-mono mt-6 text-[10.5px] leading-relaxed text-ink-3">
137 + … et les applications Ka pour iOS et Android. Un profil, une carte de
138 + membre, des favoris qui vous suivent d&apos;une plateforme à
139 + l&apos;autre.
43 140 </p>
44 141 </section>
45 − <div className="rise mt-8 [animation-delay:0.22s]">
46 − <AuthForm next={next} error={sp.error} via={viaLabel} />
47 − </div>
48 142 </main>
49 143 );
50 144 }
modified src/lib/email.ts +164 −44
@@ -1,6 +1,9 @@
1 1 // Auteur : Simon-Pierre Boucher — contact@spboucher.ai
2 2 // KA ID — courriels transactionnels via Resend (domaine groupe-ka.com).
3 3 // API REST directe (fetch), zéro dépendance — même philosophie que scrypt.
4 +// Gabarits « éditorial sharp » : encre #141814, lime #d9f26b, mono uppercase,
5 +// carte à bordure encre + ombre décalée dure — l'identité exacte du site,
6 +// en tables HTML compatibles clients courriel (Gmail, Apple Mail, Outlook).
4 7
5 8 const RESEND_ENDPOINT = "https://api.resend.com/emails";
6 9
@@ -35,71 +38,188 @@ export async function sendEmail(opts: {
35 38 }
36 39 }
37 40
38 −/* ---------- gabarits (encre sur papier, style Groupe Ka) ---------- */
41 +/* ---------- système de design courriel (identité Groupe Ka) ---------- */
39 42
40 −function layout(title: string, inner: string): string {
43 +const INK = "#141814";
44 +const INK_2 = "#4d5551";
45 +const INK_3 = "#8b928c";
46 +const PAPER = "#f5f3ee";
47 +const LIME = "#d9f26b";
48 +const GREEN = "#1c5c41";
49 +
50 +const F_DISPLAY = "'Space Grotesk',Arial,'Helvetica Neue',sans-serif";
51 +const F_BODY = "'Inter',Helvetica,Arial,sans-serif";
52 +const F_MONO = "'JetBrains Mono','SFMono-Regular',Menlo,Consolas,'Courier New',monospace";
53 +
54 +function layout(opts: {
55 + kicker: string;
56 + title: string;
57 + preheader: string;
58 + inner: string;
59 +}): string {
41 60 return `<!doctype html>
42 −<html lang="fr"><body style="margin:0;padding:0;background:#f2f0ea;">
43 − <div style="max-width:520px;margin:0 auto;padding:32px 20px;font-family:Georgia,'Times New Roman',serif;color:#141814;">
44 − <p style="margin:0 0 18px;font-family:Menlo,Consolas,monospace;font-size:11px;letter-spacing:0.14em;text-transform:uppercase;color:#4a4f4a;">
45 − Groupe Ka · KA ID
46 − </p>
47 − <div style="background:#fbfaf6;border:1.5px solid rgba(20,24,20,0.35);padding:28px 26px;">
48 − <h1 style="margin:0 0 14px;font-size:22px;line-height:1.15;letter-spacing:-0.02em;">${title}</h1>
49 − ${inner}
50 − </div>
51 − <p style="margin:16px 0 0;font-size:11.5px;line-height:1.5;color:#6a6f6a;">
52 − Un seul compte — le KA ID — pour toutes les plateformes du Groupe Ka.<br>
53 − Si vous n'êtes pas à l'origine de cette demande, ignorez ce courriel.
54 − </p>
55 − </div>
56 −</body></html>`;
61 +<html lang="fr">
62 +<head>
63 + <meta charset="utf-8">
64 + <meta name="viewport" content="width=device-width, initial-scale=1">
65 + <meta name="color-scheme" content="light">
66 + <meta name="supported-color-schemes" content="light">
67 + <title>${opts.title.replace(/<[^>]+>/g, " ")}</title>
68 +</head>
69 +<body style="margin:0;padding:0;background:${PAPER};">
70 + <div style="display:none;max-height:0;overflow:hidden;mso-hide:all;">${opts.preheader}&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;</div>
71 + <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${PAPER};">
72 + <tr>
73 + <td align="center" style="padding:40px 16px 36px;">
74 + <table role="presentation" cellpadding="0" cellspacing="0" style="width:560px;max-width:100%;">
75 +
76 + <!-- manchette : marque + étiquette mono -->
77 + <tr>
78 + <td style="padding:0 2px 16px;">
79 + <table role="presentation" width="100%" cellpadding="0" cellspacing="0">
80 + <tr>
81 + <td style="font-family:${F_DISPLAY};font-weight:700;font-size:22px;letter-spacing:-0.03em;color:${INK};white-space:nowrap;">Groupe&nbsp;<span style="display:inline-block;background:${LIME};color:${INK};padding:0 7px 3px;border-radius:6px;">Ka</span></td>
82 + <td align="right" style="font-family:${F_MONO};font-size:10px;font-weight:700;letter-spacing:0.16em;text-transform:uppercase;color:${INK_3};">${opts.kicker}</td>
83 + </tr>
84 + </table>
85 + </td>
86 + </tr>
87 +
88 + <!-- carte : bordure encre + ombre décalée dure (signature du site) -->
89 + <tr>
90 + <td style="background:#ffffff;border:2px solid ${INK};border-right-width:8px;border-bottom-width:8px;border-radius:12px;padding:30px 28px 24px;">
91 + <table role="presentation" width="100%" cellpadding="0" cellspacing="0">
92 + <tr>
93 + <td style="font-family:${F_MONO};font-size:11px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:${GREEN};"><span style="display:inline-block;width:22px;height:2px;background:${GREEN};vertical-align:middle;">&nbsp;</span>&nbsp;&nbsp;KA&nbsp;ID</td>
94 + </tr>
95 + <tr>
96 + <td style="padding:12px 0 0;font-family:${F_DISPLAY};font-weight:700;font-size:27px;line-height:1.08;letter-spacing:-0.03em;color:${INK};">${opts.title}</td>
97 + </tr>
98 + <tr><td style="padding:14px 0 0;">${opts.inner}</td></tr>
99 + <tr>
100 + <td style="padding:24px 0 0;">
101 + <table role="presentation" width="100%" cellpadding="0" cellspacing="0">
102 + <tr><td style="border-top:1.5px dashed rgba(20,24,20,0.25);font-size:0;line-height:0;">&nbsp;</td></tr>
103 + </table>
104 + </td>
105 + </tr>
106 + <tr>
107 + <td style="padding:8px 0 0;font-family:${F_MONO};font-size:10.5px;line-height:1.7;color:${INK_3};">Vous n'êtes pas à l'origine de cette demande&nbsp;? Ignorez ce courriel — rien ne se passera sans lui.</td>
108 + </tr>
109 + </table>
110 + </td>
111 + </tr>
112 +
113 + <!-- pied : écosystème -->
114 + <tr>
115 + <td style="padding:20px 2px 0;font-family:${F_MONO};font-size:9.5px;font-weight:700;line-height:1.9;letter-spacing:0.1em;text-transform:uppercase;color:${INK_3};">
116 + Un seul compte · tout l'écosystème<br>
117 + <a href="https://www.groupe-ka.com" style="color:${GREEN};text-decoration:none;">www.groupe-ka.com</a>
118 + &nbsp;—&nbsp;Lou·Ka&nbsp;· Immo·Ka&nbsp;· Vrai-Prix&nbsp;· Auto·Ka&nbsp;· Food·Ka&nbsp;· Resto·Ka&nbsp;· Sorti·Ka&nbsp;· Créa·Ka&nbsp;· Job·Ka&nbsp;…
119 + </td>
120 + </tr>
121 +
122 + </table>
123 + </td>
124 + </tr>
125 + </table>
126 +</body>
127 +</html>`;
128 +}
129 +
130 +function paragraph(html: string): string {
131 + return `<p style="margin:0;font-family:${F_BODY};font-size:14.5px;line-height:1.65;color:${INK_2};">${html}</p>`;
57 132 }
58 133
134 +/** Bouton pilule signature : encre pleine, texte lime (btn-primary du site). */
59 135 function button(url: string, label: string): string {
60 − return `<p style="margin:22px 0;">
61 − <a href="${url}" style="display:inline-block;background:#141814;color:#d9f26b;text-decoration:none;font-family:Menlo,Consolas,monospace;font-size:13px;letter-spacing:0.06em;padding:13px 22px;border:1.5px solid #141814;">${label}</a>
62 − </p>
63 − <p style="margin:0;font-size:12px;color:#6a6f6a;word-break:break-all;">Ou copiez ce lien : ${url}</p>`;
136 + return `<table role="presentation" cellpadding="0" cellspacing="0" style="margin:22px 0 10px;">
137 + <tr>
138 + <td style="background:${INK};border-radius:999px;mso-padding-alt:14px 30px;">
139 + <a href="${url}" style="display:inline-block;padding:14px 30px;font-family:${F_DISPLAY};font-weight:700;font-size:15px;letter-spacing:-0.01em;color:${LIME};text-decoration:none;border-radius:999px;">${label}&nbsp;&nbsp;&rarr;</a>
140 + </td>
141 + </tr>
142 + </table>
143 + <p style="margin:0;font-family:${F_MONO};font-size:10px;line-height:1.7;color:${INK_3};word-break:break-all;">Le bouton ne répond pas&nbsp;? Copiez ce lien&nbsp;:<br><a href="${url}" style="color:${GREEN};text-decoration:underline;">${url}</a></p>`;
144 +}
145 +
146 +/** Pastille d'info mono (durée de validité, usage unique…). */
147 +function metaChips(items: string[]): string {
148 + return `<p style="margin:16px 0 0;font-family:${F_MONO};font-size:9.5px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;color:${INK_3};">${items.join("&nbsp;&nbsp;·&nbsp;&nbsp;")}</p>`;
64 149 }
65 150
151 +/* ---------- gabarits ---------- */
152 +
66 153 export function verificationEmail(name: string, url: string) {
154 + const inner = `
155 + ${paragraph(
156 + `Bonjour <strong style="color:${INK};">${name}</strong>,<br>
157 + votre KA ID vient d'être créé — un seul compte pour toutes les
158 + plateformes du Groupe Ka. Dernière étape&nbsp;: confirmer que cette
159 + adresse est bien la vôtre. Vous serez connecté automatiquement.`,
160 + )}
161 + ${button(url, "Confirmer mon adresse")}
162 + ${metaChips(["Valable 24 heures", "Usage unique"])}`;
67 163 return {
68 164 subject: "Confirmez votre adresse — KA ID",
69 − html: layout(
70 − "Confirmez votre adresse courriel",
71 − `<p style="margin:0;font-size:14.5px;line-height:1.6;">Bonjour ${name},<br>
72 − votre KA ID vient d'être créé. Il ne reste qu'à confirmer que cette
73 − adresse est bien la vôtre — le lien est valable 24 heures.</p>
74 − ${button(url, "Confirmer mon adresse →")}`,
75 − ),
76 − text: `Bonjour ${name},\n\nConfirmez votre adresse pour activer votre KA ID (lien valable 24 h) :\n${url}\n\nSi vous n'êtes pas à l'origine de cette demande, ignorez ce courriel.`,
165 + html: layout({
166 + kicker: "KA ID · Confirmation",
167 + title: "Une adresse à confirmer,<br>un écosystème à ouvrir",
168 + preheader: "Confirmez votre adresse pour activer votre KA ID — lien valable 24 heures.",
169 + inner,
170 + }),
171 + text: `Bonjour ${name},\n\nVotre KA ID vient d'être créé. Confirmez votre adresse pour l'activer (lien valable 24 h, usage unique) :\n${url}\n\nSi vous n'êtes pas à l'origine de cette demande, ignorez ce courriel.\n\nGroupe Ka — www.groupe-ka.com`,
77 172 };
78 173 }
79 174
80 175 export function magicLinkEmail(name: string, url: string) {
176 + const inner = `
177 + ${paragraph(
178 + `Bonjour <strong style="color:${INK};">${name}</strong>,<br>
179 + vous avez demandé à vous connecter sans mot de passe. Un clic sur le
180 + bouton et vous êtes chez vous — sur toutes les plateformes du groupe.`,
181 + )}
182 + ${button(url, "Me connecter")}
183 + ${metaChips(["Valable 15 minutes", "Usage unique"])}`;
81 184 return {
82 185 subject: "Votre lien de connexion — KA ID",
83 − html: layout(
84 − "Connexion sans mot de passe",
85 − `<p style="margin:0;font-size:14.5px;line-height:1.6;">Bonjour ${name},<br>
86 − cliquez pour vous connecter à votre KA ID — aucun mot de passe requis.
87 − Le lien est valable 15 minutes et ne sert qu'une fois.</p>
88 − ${button(url, "Me connecter →")}`,
89 − ),
90 − text: `Bonjour ${name},\n\nVotre lien de connexion KA ID (valable 15 minutes, usage unique) :\n${url}\n\nSi vous n'êtes pas à l'origine de cette demande, ignorez ce courriel.`,
186 + html: layout({
187 + kicker: "KA ID · Connexion",
188 + title: "Votre lien de connexion,<br>sans mot de passe",
189 + preheader: "Un clic et vous êtes connecté — lien valable 15 minutes, usage unique.",
190 + inner,
191 + }),
192 + text: `Bonjour ${name},\n\nVotre lien de connexion KA ID (valable 15 minutes, usage unique) :\n${url}\n\nSi vous n'êtes pas à l'origine de cette demande, ignorez ce courriel.\n\nGroupe Ka — www.groupe-ka.com`,
91 193 };
92 194 }
93 195
94 196 export function loginCodeEmail(name: string, code: string) {
197 + // Panneau encre/lime — même langage que la carte de membre KA ID.
198 + const codePanel = `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:22px 0 0;">
199 + <tr>
200 + <td align="center" style="background:${INK};border-radius:12px;padding:26px 18px 20px;">
201 + <p style="margin:0 0 12px;font-family:${F_MONO};font-size:10px;font-weight:700;letter-spacing:0.26em;text-transform:uppercase;color:rgba(217,242,107,0.65);">Code de connexion</p>
202 + <p style="margin:0;font-family:${F_MONO};font-weight:700;font-size:42px;line-height:1;letter-spacing:0.28em;color:${LIME};">${code}</p>
203 + <p style="margin:16px 0 0;font-family:${F_MONO};font-size:9px;font-weight:700;letter-spacing:0.2em;text-transform:uppercase;color:rgba(245,243,238,0.45);">Valable 10 minutes&nbsp;&nbsp;·&nbsp;&nbsp;Usage unique</p>
204 + </td>
205 + </tr>
206 + </table>`;
207 + const inner = `
208 + ${paragraph(
209 + `Bonjour <strong style="color:${INK};">${name}</strong>,<br>
210 + votre mot de passe est bon — il ne manque que ce code pour ouvrir la
211 + session. Saisissez-le sur la page de connexion&nbsp;:`,
212 + )}
213 + ${codePanel}
214 + <p style="margin:14px 0 0;font-family:${F_BODY};font-size:12.5px;line-height:1.6;color:${INK_2};">Ce n'était pas vous&nbsp;? Quelqu'un connaît alors votre mot de passe&nbsp;— <a href="https://www.groupe-ka.com/compte" style="color:${GREEN};">changez-le dès maintenant</a>.</p>`;
95 215 return {
96 216 subject: `${code} — votre code de connexion KA ID`,
97 − html: layout(
98 − "Votre code de connexion",
99 − `<p style="margin:0;font-size:14.5px;line-height:1.6;">Bonjour ${name},<br>
100 − voici le code pour confirmer votre connexion. Il expire dans 10 minutes.</p>
101 − <p style="margin:22px 0 0;font-family:Menlo,Consolas,monospace;font-size:32px;letter-spacing:0.35em;font-weight:bold;">${code}</p>`,
102 − ),
103 − text: `Bonjour ${name},\n\nVotre code de connexion KA ID (expire dans 10 minutes) : ${code}\n\nSi vous n'êtes pas à l'origine de cette connexion, changez votre mot de passe.`,
217 + html: layout({
218 + kicker: "KA ID · Vérification",
219 + title: "Votre code de connexion",
220 + preheader: `Votre code : ${code} — il expire dans 10 minutes.`,
221 + inner,
222 + }),
223 + text: `Bonjour ${name},\n\nVotre code de connexion KA ID (expire dans 10 minutes, usage unique) : ${code}\n\nSi vous n'êtes pas à l'origine de cette connexion, changez votre mot de passe sur www.groupe-ka.com/compte.\n\nGroupe Ka — www.groupe-ka.com`,
104 224 };
105 225 }
106 226