diff --git a/astro.config.mjs b/astro.config.mjs
index 215423d..6a1feb0 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -13,5 +13,5 @@ export default defineConfig({
},
build: { inlineStylesheets: 'auto' },
// In development the form posts to the contact handler started with `npm run contact`.
- vite: { server: { proxy: { '^/(contact(/challenge)?|notify(/verify)?)$': { target: 'http://127.0.0.1:3000', changeOrigin: false } } } },
+ vite: { server: { proxy: { '^/(contact(/challenge)?|notify(/verify)?)(\\?|$)': { target: 'http://127.0.0.1:3000', changeOrigin: false } } } },
});
diff --git a/src/components/verifier/Seal.astro b/src/components/verifier/Seal.astro
new file mode 100644
index 0000000..09ec580
--- /dev/null
+++ b/src/components/verifier/Seal.astro
@@ -0,0 +1,103 @@
+---
+/**
+ * The seal on the sign-up result pages: a target drawn as a technical line drawing. On load the
+ * corner brackets close in and the result is drawn in the centre: a check when the link verified,
+ * a cross when it did not. Then it stays still.
+ * Geometry is computed at build time; the motion is CSS only and off under reduced motion.
+ */
+interface Props { state: 'confirmed' | 'error'; label: string }
+const { state, label } = Astro.props;
+
+const C = 200, R = 184, N = 60;
+const f = (n: number) => Math.round(n * 10) / 10;
+const at = (r: number, a: number): [number, number] => [C + r * Math.sin(a), C - r * Math.cos(a)];
+
+/* Ticks clockwise from twelve o'clock; every fifth is long, so the ring reads as a scale. */
+const ticks = Array.from({ length: N }, (_, i) => {
+ const a = (i / N) * Math.PI * 2;
+ const [x1, y1] = at(R, a);
+ const [x2, y2] = at(R - (i % 5 === 0 ? 20 : 10), a);
+ return { x1: f(x1), y1: f(y1), x2: f(x2), y2: f(y2) };
+});
+
+/* Corner brackets, each an L drawn about the centre and moved in by CSS. */
+const B = 112, arm = 30;
+const brackets = [[-1, -1], [1, -1], [1, 1], [-1, 1]].map(([sx, sy]) =>
+ `M${C + sx * B},${C + sy * (B - arm)} L${C + sx * B},${C + sy * B} L${C + sx * (B - arm)},${C + sy * B}`);
+---
+
+
+
diff --git a/src/i18n/ui.ts b/src/i18n/ui.ts
index 8888262..704ac98 100644
--- a/src/i18n/ui.ts
+++ b/src/i18n/ui.ts
@@ -276,11 +276,23 @@ const en = {
title: 'Registered.',
body: 'Your address is confirmed. You will hear from us once, when the CipherVerifier alpha is released.',
link: 'Back to CipherVerifier',
+ seal: 'A target, locked, with a check at its centre.',
+ rows: [
+ { key: 'Status', value: 'Confirmed' },
+ { key: 'Signature', value: 'Valid' },
+ { key: 'List', value: 'CipherVerifier alpha' },
+ { key: 'Next email', value: 'At release' },
+ ],
},
error: {
title: 'Not registered.',
body: 'The link has expired or is incomplete, or the registration did not go through. Register again, or write to office@ciphercell.de.',
link: 'Register again',
+ seal: 'A target that did not lock, with a cross at its centre.',
+ rows: [
+ { key: 'Status', value: 'Not confirmed' },
+ { key: 'Address', value: 'Not stored' },
+ ],
},
},
},
@@ -615,11 +627,23 @@ const de: typeof en = {
title: 'Registriert.',
body: 'Ihre Adresse ist bestätigt. Sie hören einmal von uns, sobald die Alpha von CipherVerifier erscheint.',
link: 'Zurück zu CipherVerifier',
+ seal: 'Ein Zielkreuz, eingerastet, mit einem Haken in der Mitte.',
+ rows: [
+ { key: 'Status', value: 'Bestätigt' },
+ { key: 'Signatur', value: 'Gültig' },
+ { key: 'Liste', value: 'CipherVerifier-Alpha' },
+ { key: 'Nächste E-Mail', value: 'Zur Veröffentlichung' },
+ ],
},
error: {
title: 'Nicht registriert.',
body: 'Der Link ist abgelaufen oder unvollständig, oder die Registrierung ist nicht angekommen. Registrieren Sie sich erneut oder schreiben Sie an office@ciphercell.de.',
link: 'Erneut registrieren',
+ seal: 'Ein Zielkreuz, nicht eingerastet, mit einem Kreuz in der Mitte.',
+ rows: [
+ { key: 'Status', value: 'Nicht bestätigt' },
+ { key: 'Adresse', value: 'Nicht gespeichert' },
+ ],
},
},
},
diff --git a/src/pages/_NotifyPage.astro b/src/pages/_NotifyPage.astro
index ee346d8..8f8b9a6 100644
--- a/src/pages/_NotifyPage.astro
+++ b/src/pages/_NotifyPage.astro
@@ -1,5 +1,6 @@
---
import Base from '../layouts/Base.astro';
+import Seal from '../components/verifier/Seal.astro';
import { localePath, t, type Lang } from '../i18n/ui';
interface Props { lang: Lang; page: 'confirmed' | 'error' }
const { lang, page } = Astro.props;
@@ -8,22 +9,74 @@ const r = p[page];
const product = localePath(lang, '/cipherverifier');
---
-
-