/** * The alpha sign-up confirmation email, in the site's voice and look: warm paper, warm black, * one neo-grotesque, hairlines, one square button. Email clients ignore most CSS, so the layout * is tables with inline styles, the type falls back from Inter Tight to Helvetica and Arial, and * the logo travels as an inline attachment (cid) rather than a remote image. * * Colours are the tokens from src/styles/tokens.css, written out: email has no custom properties. * The hairline is --line (ink at 14 %) flattened onto paper. */ import { fileURLToPath } from 'node:url'; const C = { paper: '#E9E5DF', ink: '#1A1A18', muted: '#57564F', line: '#CCC9C3', }; const FONT = "'Inter Tight', Inter, 'Helvetica Neue', Helvetica, Arial, sans-serif"; const LOGO_CID = 'logo@ciphercell.de'; const LOGO = { path: fileURLToPath(new URL('./logo.png', import.meta.url)), width: 140, height: 32 }; const COPY = { en: { subject: 'Confirm your CipherVerifier alpha registration', preheader: 'One click confirms your registration for the CipherVerifier alpha.', title: 'Confirm your registration.', body: (email) => `You registered ${email} for the CipherVerifier alpha. Confirm the address, and we will email you once, when the alpha is released. Nothing else.`, button: 'Confirm registration', expiry: 'The link is valid for 7 days. If you did not register, ignore this email. Your address is not stored.', fallback: 'If the button does not work, open this address in your browser:', }, de: { subject: 'Bitte bestätigen Sie Ihre Registrierung für die CipherVerifier-Alpha', preheader: 'Ein Klick bestätigt Ihre Registrierung für die Alpha von CipherVerifier.', title: 'Registrierung bestätigen.', body: (email) => `Sie haben ${email} für die Alpha von CipherVerifier registriert. Bestätigen Sie die Adresse, und wir schreiben Ihnen einmal, sobald die Alpha erscheint. Sonst nichts.`, button: 'Registrierung bestätigen', expiry: 'Der Link ist 7 Tage gültig. Wenn Sie sich nicht registriert haben, ignorieren Sie diese E-Mail. Ihre Adresse wird nicht gespeichert.', fallback: 'Falls der Button nicht funktioniert, öffnen Sie diese Adresse im Browser:', }, }; const ADDRESS = ['CipherCell', 'Innstr. 43, 94032 Passau']; const SITE_LABEL = 'www.ciphercell.de'; const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]); /** { subject, text, html, attachments } for nodemailer. */ export function confirmMail({ lang, email, link, site }) { const t = COPY[lang] ?? COPY.en; const text = [ t.title, '', t.body(email), '', `${t.button}:`, link, '', t.expiry, '', '-- ', ...ADDRESS, SITE_LABEL, ].join('\n'); const p = (content, { size = 16, color = C.ink, lineHeight = 1.55, extra = '' } = {}) => `
${content}
`; const row = (content, padding) => `
${esc(link)}`, { size: 14, color: C.muted, lineHeight: 1.4 }), '0 0 48px')} ${rule} ${row(p(`${ADDRESS.map(esc).join(' ')} ${SITE_LABEL}`, { size: 14, color: C.muted, lineHeight: 1.4 }), '20px 0 0')} |