Files
familienarchiv/docs/specs/register-page-spec.html
Marcel f7747ba352
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m43s
CI / OCR Service Tests (push) Successful in 44s
CI / Backend Unit Tests (push) Failing after 2m53s
docs(spec): add register page design spec
Captures the centered-card registration design 1:1 from the claude.ai/design export. Covers all 10 sections: desktop overview, header, above-card copy, form fields, password states, notification card, submit button, success panel, mobile layout, and i18n/a11y/backend implementation notes.

Relates to #269

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:24:06 +02:00

1372 lines
78 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register Page · Final Design Spec · Familienarchiv</title>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Helvetica Neue',Arial,sans-serif;background:#ECEAE4;color:#1A1A1A;line-height:1.5}
.doc{max-width:1400px;margin:0 auto;padding:48px 32px}
/* ── Masthead ─── */
.mast{background:#0D2240;border-radius:10px;padding:32px 40px;margin-bottom:48px}
.mast-top{display:flex;align-items:flex-start;justify-content:space-between;gap:24px;margin-bottom:16px}
.mast h1{font-size:22px;font-weight:900;color:#fff;letter-spacing:-.4px;margin-bottom:6px}
.mast p{font-size:12px;color:rgba(255,255,255,.5);max-width:640px;line-height:1.7}
.mast-badge{font-size:9px;font-weight:800;padding:3px 9px;border-radius:20px;text-transform:uppercase;letter-spacing:.8px;white-space:nowrap;flex-shrink:0;margin-top:4px}
.mb-final{background:#A6DAD8;color:#002850}
.decisions{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:20px;border-top:1px solid rgba(255,255,255,.1);padding-top:16px}
.dec{background:rgba(255,255,255,.06);border-radius:6px;padding:10px 12px}
.dec-label{font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:rgba(255,255,255,.35);margin-bottom:5px}
.dec-value{font-size:9.5px;font-weight:700;color:#fff;line-height:1.5}
/* ── Section headings ─── */
.sec{margin-bottom:64px}
.sec+.sec{border-top:2px dashed #C8C4BE;padding-top:56px}
.sec-h{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:1.2px;color:#888;margin-bottom:20px;display:flex;align-items:center;gap:10px}
.sec-h::after{content:'';flex:1;height:1px;background:#D8D4CE}
.sec-num{background:#0D2240;color:#fff;font-size:9px;font-weight:900;padding:2px 7px;border-radius:10px}
/* ── Grid ─── */
.sg{display:grid;gap:20px;align-items:start}
.sg-2{grid-template-columns:1fr 1fr}
.sg-3{grid-template-columns:1fr 1fr 1fr}
.sg-4{grid-template-columns:1fr 1fr 1fr 1fr}
.sb{display:flex;flex-direction:column}
.sl{font-size:9px;font-weight:800;color:#888;text-transform:uppercase;letter-spacing:1.5px;margin-bottom:6px;display:flex;align-items:center;gap:6px}
.sc{font-size:10px;color:#888;margin-top:8px;font-style:italic;line-height:1.5}
/* ── Browser chrome (desktop) ─── */
.wf{background:#fff;border:2px solid #B8B4AE;border-radius:10px;overflow:hidden;box-shadow:0 4px 18px rgba(0,0,0,.08)}
.wf-inner{border-radius:0;overflow:hidden}
.wf-bar{height:24px;background:#E8E4DF;border-bottom:1px solid #C8C4BE;display:flex;align-items:center;padding:0 9px;gap:4px}
.dot{width:7px;height:7px;border-radius:50%;background:#C8C4BE}
.dot.r{background:#F87171}.dot.y{background:#FCD34D}.dot.g{background:#4ADE80}
.urlbar{flex:1;height:11px;background:#D8D4CE;border-radius:3px;margin-left:6px;display:flex;align-items:center;padding:0 5px}
.urlbar span{font-size:7.5px;color:#888;font-family:monospace}
/* ── Mobile chrome ─── */
.WF-M{background:#fff;border:2px solid #B8B4AE;border-radius:16px;overflow:hidden;box-shadow:0 4px 18px rgba(0,0,0,.08);width:220px}
.WF-M-STATUS{height:14px;background:#012851;display:flex;align-items:center;justify-content:space-between;padding:0 10px}
.WF-M-TIME{font-size:6px;color:#fff;font-weight:700}
.WF-M-ICONS{display:flex;gap:3px}
.WF-M-ICON{width:5px;height:5px;background:rgba(255,255,255,.5);border-radius:1px}
/* ── State pill labels ─── */
.state-label{display:inline-block;font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;border-radius:3px;padding:1px 5px;margin-bottom:5px}
.st-default{background:#E8E4DF;color:#555}
.st-hover{background:#DBEAFE;color:#1D4ED8}
.st-focus{background:#EDE9FE;color:#6D28D9}
.st-active{background:#DCFCE7;color:#166534}
.st-loading{background:#FEF9C3;color:#854D0E}
.st-error{background:#FEE2E2;color:#991B1B}
.st-success{background:#DCFCE7;color:#166534}
.st-checked{background:#CFFAFE;color:#0e7490}
/* ── Annotation callouts ─── */
.ann-block{background:#FFF7ED;border:1px solid #FDBA74;border-radius:5px;padding:10px 14px;font-size:10.5px;color:#7C2D12;line-height:1.6;margin-top:14px}
.ann-block strong{font-weight:800}
.ann-block ul{padding-left:16px;display:flex;flex-direction:column;gap:3px;margin-top:6px}
.ann-info{background:#EFF6FF;border:1px solid #BFDBFE;border-radius:5px;padding:10px 14px;font-size:10.5px;color:#1E3A5F;line-height:1.6;margin-top:14px}
.ann-info strong{font-weight:800}
.ann-info ul{padding-left:16px;display:flex;flex-direction:column;gap:3px;margin-top:6px}
/* ── Spec disclaimer ─── */
.spec-disclaimer{background:#FFF8E1;border:1.5px solid #FFC107;border-radius:6px;padding:11px 16px;font-size:11px;color:#6D4C00;margin-bottom:32px;line-height:1.6}
.spec-disclaimer strong{font-weight:800}
/* ── impl-ref ─── */
.impl-ref{background:#0d1117;border-radius:8px;margin-top:20px;overflow:hidden;border:1px solid #30363d}
.impl-ref-hdr{background:#161b22;padding:9px 16px;font-size:9.5px;font-weight:800;color:#f0883e;border-bottom:1px solid #30363d;display:flex;align-items:center;gap:8px;letter-spacing:.4px;text-transform:uppercase}
.impl-ref-hdr::before{content:'⚙';font-size:12px}
.impl-ref-hdr span{color:rgba(240,136,62,.55);font-weight:400;margin-left:auto;font-size:9px;text-transform:none;letter-spacing:0}
.impl-ref table{width:100%;border-collapse:collapse;font-size:10px}
.impl-ref th{text-align:left;font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#8b949e;padding:8px 14px;border-bottom:1px solid #21262d}
.impl-ref td{padding:6px 14px;border-bottom:1px solid #161b22;vertical-align:top;line-height:1.6;color:#c9d1d9}
.impl-ref tr:last-child td{border-bottom:none}
.impl-ref td:first-child{color:#79c0ff;font-weight:700;white-space:nowrap;width:210px}
.impl-ref td code{font-family:'SFMono-Regular',Consolas,monospace;font-size:9.5px;background:#161b22;color:#a5d6ff;padding:1px 5px;border-radius:3px;white-space:nowrap}
.impl-ref .ir-px{color:#7ee787;font-family:monospace;font-size:9.5px}
/* ════════════════════════════════════════
REGISTER PAGE MOCKUP COMPONENTS
All sizes scaled to ~55% of real values.
DO NOT copy sizes from here — use impl-ref tables.
════════════════════════════════════════ */
/* Page shell */
.RP{background:#f0efe9;display:flex;flex-direction:column;font-family:'Helvetica Neue',Arial,sans-serif}
/* ── Header ─── */
.RP-HEADER{background:#012851;flex-shrink:0}
.RP-STRIPE{height:2px;background:#a1dcd8}
.RP-NAV{height:35px;display:flex;align-items:center;padding:0 18px;gap:10px}
.RP-WORDMARK{font-size:11px;font-weight:900;color:#fff;letter-spacing:.15em;text-transform:uppercase}
.RP-LANG{margin-left:auto;display:flex;align-items:center;gap:2px}
.RP-LANG-BTN{font-size:6.5px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:rgba(255,255,255,.55);padding:3px 6px;border:none;background:transparent;border-bottom:1.5px solid transparent;cursor:pointer}
.RP-LANG-BTN.on{color:#fff;border-bottom-color:#a1dcd8}
/* ── Main area ─── */
.RP-MAIN{flex:1;display:flex;justify-content:center;align-items:flex-start;padding:35px 18px}
/* ── Narrow artboard (card only, for close-ups) ─── */
.RP-INNER{width:352px}
/* ── Above-card ─── */
.RP-ABOVE{text-align:center;margin-bottom:20px}
.RP-EYEBROW{display:inline-block;font-size:6px;font-weight:800;letter-spacing:.22em;text-transform:uppercase;color:#4b5563;border-top:1px solid #d4d2c5;border-bottom:1px solid #d4d2c5;padding:3px 8px;margin-bottom:10px}
.RP-H1{font-size:25px;font-weight:400;color:#012851;line-height:1.12;letter-spacing:-.005em;margin-bottom:9px;font-family:Georgia,serif}
.RP-SUB{font-size:10px;color:#4b5563;line-height:1.6;font-family:Georgia,serif;max-width:297px;margin:0 auto}
/* ── Card ─── */
.RP-CARD{background:#fff;border:1px solid #e4e2d7;box-shadow:0 1px 2px 0 rgb(0 0 0/.05);border-radius:2px;padding:22px}
/* ── Section caption ─── */
.RP-SCAP{display:flex;align-items:center;gap:7px;margin-bottom:9px}
.RP-SCAP-TEXT{font-size:6px;font-weight:800;letter-spacing:.18em;text-transform:uppercase;color:#012851;white-space:nowrap}
.RP-SCAP-RULE{flex:1;height:1px;background:#e4e2d7}
/* ── Fields ─── */
.RP-FIELDS{display:flex;flex-direction:column;gap:15px}
.RP-FIELD{display:flex;flex-direction:column;gap:0}
.RP-LABEL{font-size:6.5px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:#4b5563;margin-bottom:4px}
.RP-INPUT{width:100%;background:#fff;border:1px solid #e4e2d7;border-radius:2px;padding:8px 9px;font-size:9.5px;color:#012851;font-family:Georgia,serif;outline:none}
.RP-INPUT.focused{border-color:#012851;box-shadow:0 0 0 1.5px rgba(1,40,81,.15)}
.RP-INPUT.invalid{border-color:#c0392b}
.RP-INPUT::placeholder{color:#9ca3af}
.RP-HINT{font-size:7px;color:#6b7280;margin-top:3px;display:flex;align-items:center;gap:3px}
.RP-HINT.ok{color:#0a6b56}
.RP-HINT.err{color:#c0392b}
.RP-HINT-DOT{width:4.5px;height:4.5px;border-radius:999px;background:#00c7b1;flex-shrink:0}
/* ── 2-col name grid ─── */
.RP-2COL{display:grid;grid-template-columns:1fr 1fr;gap:9px}
/* ── Password input wrapper ─── */
.RP-PWINPUT{position:relative}
.RP-PWINPUT .RP-INPUT{padding-right:50px}
.RP-SHOWBTN{position:absolute;right:0;top:0;bottom:0;padding:0 8px;background:transparent;border:none;border-left:1px solid #e4e2d7;font-size:6px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:#4b5563;cursor:pointer;display:flex;align-items:center}
/* ── Notification card (CheckOption) ─── */
.RP-CHECK{display:flex;gap:8px;padding:8px 9px;border:1px solid #e4e2d7;background:#fff;border-radius:2px;align-items:flex-start;cursor:pointer}
.RP-CHECK.checked{border-color:#012851;background:rgba(161,220,216,.18)}
.RP-CHECKBOX{flex-shrink:0;width:12px;height:12px;border-radius:2px;border:1.5px solid #9ca3af;background:#fff;display:flex;align-items:center;justify-content:center;margin-top:1px}
.RP-CHECKBOX.checked{background:#012851;border-color:#012851}
.RP-CHECKMARK{width:8px;height:8px}
.RP-CHECK-BODY{display:flex;flex-direction:column;gap:1px}
.RP-CHECK-TITLE{font-size:8px;font-weight:700;color:#012851}
.RP-CHECK-DESC{font-size:8.5px;color:#4b5563;font-family:Georgia,serif;line-height:1.5}
/* ── Submit button ─── */
.RP-SUBMIT{width:100%;background:#012851;color:#fff;border:none;border-radius:2px;padding:9px 13px;font-size:7px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;display:flex;align-items:center;justify-content:center;gap:5px;cursor:pointer;margin-top:18px}
.RP-SUBMIT:hover,.RP-SUBMIT.hover{background:#01386f}
.RP-SUBMIT.loading{opacity:.85;cursor:wait}
.RP-SUBMIT svg{width:9px;height:9px}
/* ── Footer links ─── */
.RP-FOOTER{text-align:center;margin-top:13px;display:flex;flex-direction:column;gap:8px}
.RP-PRIVACY{font-size:7.5px;color:#6b7280;font-family:Georgia,serif;line-height:1.6}
.RP-PRIVACY a{color:#012851;text-decoration:underline;text-decoration-color:#a1dcd8;text-underline-offset:2px;text-decoration-thickness:1.5px}
.RP-SIGNIN{font-size:7px;color:#4b5563}
.RP-SIGNIN a{color:#012851;font-weight:800;text-transform:uppercase;letter-spacing:.1em;font-size:6.5px;text-decoration:underline;text-decoration-color:#a1dcd8;text-underline-offset:2px;text-decoration-thickness:1.5px}
/* ── Success panel ─── */
.RP-SUCCESS{text-align:center;padding:4px 0}
.RP-SUCCESS-CIRCLE{width:40px;height:40px;border-radius:999px;background:rgba(161,220,216,.35);display:inline-flex;align-items:center;justify-content:center;margin-bottom:13px}
.RP-SUCCESS-H{font-size:16.5px;font-weight:400;color:#012851;margin-bottom:8px;font-family:Georgia,serif;line-height:1.2}
.RP-SUCCESS-B{font-size:9.5px;color:#4b5563;font-family:Georgia,serif;line-height:1.55;max-width:231px;margin:0 auto 15px}
.RP-SUCCESS-ACTIONS{display:flex;flex-direction:column;gap:5px;max-width:176px;margin:0 auto}
.RP-SUCCESS-RESEND{background:transparent;border:none;font-size:6.5px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:#4b5563;padding:5px;cursor:pointer;text-decoration:underline;text-decoration-color:#a1dcd8;text-underline-offset:3px;text-decoration-thickness:1.5px}
/* ── Loading spinner ─── */
.RP-SPINNER{width:8px;height:8px;border:1.5px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:rpspin .7s linear infinite;flex-shrink:0}
@keyframes rpspin{to{transform:rotate(360deg)}}
/* ── Section divider within card ─── */
.RP-SEC-GAP{margin-bottom:15px}
</style>
</head>
<body>
<div class="doc">
<!-- ══════════════════════════════════════
MASTHEAD
══════════════════════════════════════ -->
<div class="mast">
<div class="mast-top">
<div>
<h1>Register Page · Final Design Spec</h1>
<p>Centered-card variant (Variant A). Friendly, inviting registration form for Familienarchiv — a collaborative archive of family letters from the 19th and 20th centuries. Designed for a dual audience (60+ and 2542), with large 17px serif inputs, du-Anrede (informal you), German-first with DE/EN/ES toggle, live password-match feedback, mention-notification opt-in, and a post-submit success panel.</p>
</div>
<span class="mast-badge mb-final">Final · Ready for implementation</span>
</div>
<div class="decisions">
<div class="dec">
<div class="dec-label">Variant</div>
<div class="dec-value">A — Centered card · 640px white card on sand canvas</div>
</div>
<div class="dec">
<div class="dec-label">Fields</div>
<div class="dec-value">Vorname · Nachname · E-Mail · Passwort · Passwort bestätigen · Benachrichtigungen</div>
</div>
<div class="dec">
<div class="dec-label">Languages</div>
<div class="dec-value">DE (default) / EN / ES — header toggle; swaps all labels, hints, and validation messages live</div>
</div>
<div class="dec">
<div class="dec-label">Audience</div>
<div class="dec-value">60+ primary (48px touch targets, 17px serif, calm layout) · 2542 secondary</div>
</div>
</div>
</div>
<!-- ── spec disclaimer ── -->
<div class="spec-disclaimer">
<strong>📐 Mockup scale notice —</strong> all font-size, height, and padding values
in the mockup CSS are scaled to ~55% of actual implementation values.
<strong>Do not copy sizes from mockup CSS.</strong> Use the ⚙ Implementation
Reference tables after each section.
</div>
<!-- ══════════════════════════════════════
SECTION 1 — FULL PAGE OVERVIEW (DESKTOP)
══════════════════════════════════════ -->
<div class="sec">
<div class="sec-h">
<span class="sec-num">1</span>
Full page — desktop (1280px)
</div>
<div class="sb">
<span class="state-label st-default">Default — form at rest, German language</span>
<div class="wf" style="max-width:860px">
<div class="wf-inner">
<div class="wf-bar">
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
<div class="urlbar"><span>localhost:3000/register</span></div>
</div>
<div class="RP">
<div class="RP-HEADER">
<div class="RP-STRIPE"></div>
<div class="RP-NAV">
<span class="RP-WORDMARK">Familienarchiv</span>
<div class="RP-LANG">
<button class="RP-LANG-BTN on">DE</button>
<button class="RP-LANG-BTN">EN</button>
<button class="RP-LANG-BTN">ES</button>
</div>
</div>
</div>
<div class="RP-MAIN">
<div class="RP-INNER">
<div class="RP-ABOVE">
<div class="RP-EYEBROW">Ein Familienprojekt</div>
<div class="RP-H1">Schön, dass du da bist.</div>
<div class="RP-SUB">Bereits 1.500 Briefe aus vielen Jahrzehnten sind im Archiv. Mit deinem Konto hilfst du mit, sie zu lesen, zu ordnen und für die nächsten Generationen zu bewahren.</div>
</div>
<div class="RP-CARD">
<div class="RP-FIELDS">
<!-- Über dich -->
<div class="RP-SEC-GAP">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
<div class="RP-2COL">
<div class="RP-FIELD">
<div class="RP-LABEL">Vorname</div>
<input class="RP-INPUT" placeholder="z.B. Frieda" readonly>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Nachname</div>
<input class="RP-INPUT" placeholder="z.B. Lehmann" readonly>
</div>
</div>
</div>
<!-- Konto -->
<div class="RP-SEC-GAP">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
<div style="display:flex;flex-direction:column;gap:9px">
<div class="RP-FIELD">
<div class="RP-LABEL">E-Mail-Adresse</div>
<input class="RP-INPUT" placeholder="name@beispiel.de" readonly>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" placeholder="••••••••" readonly>
<button class="RP-SHOWBTN">Anzeigen</button>
</div>
<div class="RP-HINT">Mindestens 8 Zeichen.</div>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort bestätigen</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" placeholder="••••••••" readonly>
<button class="RP-SHOWBTN">Anzeigen</button>
</div>
</div>
</div>
</div>
<!-- Benachrichtigungen -->
<div>
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Benachrichtigungen</span><span class="RP-SCAP-RULE"></span></div>
<div class="RP-CHECK checked">
<div class="RP-CHECKBOX checked">
<svg class="RP-CHECKMARK" viewBox="0 0 16 16" fill="none">
<path d="M3 8.5l3.2 3.2L13 5" stroke="#fff" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="RP-CHECK-BODY">
<div class="RP-CHECK-TITLE">Benachrichtige mich,</div>
<div class="RP-CHECK-DESC">wenn jemand mich in einem Kommentar erwähnt oder mir auf einen Kommentar antwortet.</div>
</div>
</div>
</div>
</div>
<button class="RP-SUBMIT">
Konto erstellen
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
</button>
<div class="RP-FOOTER">
<div class="RP-PRIVACY">Mit dem Erstellen eines Kontos stimmst du der <a href="#">Datenschutzerklärung</a> und den <a href="#">Nutzungsbedingungen</a> zu.</div>
<div class="RP-SIGNIN">Du hast bereits ein Konto? <a href="#">Anmelden</a></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference — Page structure<span>routes/register/+page.svelte</span></div>
<table>
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
<tr><td>Page wrapper</td><td><code>flex min-h-screen flex-col bg-canvas</code></td><td></td><td>Same pattern as login page</td></tr>
<tr><td>Main centering wrapper</td><td><code>flex flex-1 justify-center items-start px-8 pt-16</code></td><td>64px top padding</td><td>Items start (not center) so tall forms don't overflow</td></tr>
<tr><td>Content column</td><td><code>w-full max-w-[640px]</code></td><td>640px max</td><td>Fixed max-width card column</td></tr>
<tr><td>Form card</td><td><code>bg-surface border border-line rounded-sm shadow-sm p-10</code></td><td>40px padding</td><td><code>rounded-sm</code> = 2px; <code>shadow-sm</code> = 0 1px 2px rgb(0 0 0/.05)</td></tr>
</table>
</div>
</div>
<!-- ══════════════════════════════════════
SECTION 2 — HEADER ANATOMY
══════════════════════════════════════ -->
<div class="sec">
<div class="sec-h">
<span class="sec-num">2</span>
Header — anatomy &amp; language toggle states
</div>
<div class="sg sg-3" style="gap:20px;align-items:start">
<!-- DE active -->
<div class="sb">
<span class="state-label st-default">DE active (default)</span>
<div class="wf">
<div class="wf-inner">
<div class="wf-bar">
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
<div class="urlbar"><span>localhost:3000/register</span></div>
</div>
<div class="RP-HEADER">
<div class="RP-STRIPE"></div>
<div class="RP-NAV">
<span class="RP-WORDMARK">Familienarchiv</span>
<div class="RP-LANG">
<button class="RP-LANG-BTN on">DE</button>
<button class="RP-LANG-BTN">EN</button>
<button class="RP-LANG-BTN">ES</button>
</div>
</div>
</div>
</div>
</div>
<div class="sc">Active language: mint underline + white text. Inactive: 55% opacity.</div>
</div>
<!-- EN active -->
<div class="sb">
<span class="state-label st-hover">EN active</span>
<div class="wf">
<div class="wf-inner">
<div class="wf-bar">
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
<div class="urlbar"><span>localhost:3000/register?lang=en</span></div>
</div>
<div class="RP-HEADER">
<div class="RP-STRIPE"></div>
<div class="RP-NAV">
<span class="RP-WORDMARK">Familienarchiv</span>
<div class="RP-LANG">
<button class="RP-LANG-BTN">DE</button>
<button class="RP-LANG-BTN on">EN</button>
<button class="RP-LANG-BTN">ES</button>
</div>
</div>
</div>
</div>
</div>
<div class="sc">Toggle switches all copy live (no page reload). All labels, hints, and validation messages switch language.</div>
</div>
<!-- ES active -->
<div class="sb">
<span class="state-label st-active">ES active</span>
<div class="wf">
<div class="wf-inner">
<div class="wf-bar">
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
<div class="urlbar"><span>localhost:3000/register?lang=es</span></div>
</div>
<div class="RP-HEADER">
<div class="RP-STRIPE"></div>
<div class="RP-NAV">
<span class="RP-WORDMARK">Familienarchiv</span>
<div class="RP-LANG">
<button class="RP-LANG-BTN">DE</button>
<button class="RP-LANG-BTN">EN</button>
<button class="RP-LANG-BTN on">ES</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference — Header<span>routes/AuthHeader.svelte (extend) or new RegisterHeader.svelte</span></div>
<table>
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
<tr><td>Header wrapper</td><td><code>sticky top-0 z-50 bg-[#012851]</code></td><td></td><td>Same as authenticated header but no nav links (unauthenticated page)</td></tr>
<tr><td>Mint accent stripe</td><td><code>h-1 bg-accent</code></td><td>4px</td><td><code>--c-accent: #a1dcd8</code>. Always at the very top of the header.</td></tr>
<tr><td>Nav bar</td><td><code>h-16 flex items-center px-8</code></td><td>64px tall, 32px side padding</td><td>Max-width container inside: <code>max-w-screen-xl mx-auto w-full</code></td></tr>
<tr><td>Wordmark</td><td><code>font-sans text-xl font-bold tracking-[.15em] text-white uppercase</code></td><td>20px</td><td>Not a link on the register page (user is not yet logged in)</td></tr>
<tr><td>Lang toggle container</td><td><code>ml-auto flex items-center gap-1</code></td><td>4px gap</td><td></td></tr>
<tr><td>Lang button — inactive</td><td><code>font-sans text-xs font-bold tracking-[.12em] uppercase text-white/55 px-2.5 py-1.5 border-b-2 border-transparent transition-colors hover:text-white</code></td><td>12px, padding 6px 10px</td><td>Touch target: 12+6+6 = 24px tall, acceptable for a header toggle since it's not a primary action</td></tr>
<tr><td>Lang button — active</td><td><code>text-white border-b-2 border-accent</code></td><td></td><td>Mint underline border</td></tr>
</table>
</div>
</div>
<!-- ══════════════════════════════════════
SECTION 3 — ABOVE-CARD SECTION
══════════════════════════════════════ -->
<div class="sec">
<div class="sec-h">
<span class="sec-num">3</span>
Above-card — eyebrow, headline, subtext
</div>
<div class="sg sg-3" style="gap:20px;align-items:start">
<!-- German -->
<div class="sb">
<span class="state-label st-default">German (DE)</span>
<div class="wf">
<div class="wf-inner" style="background:#f0efe9;padding:22px 18px">
<div class="RP-ABOVE" style="margin-bottom:0">
<div class="RP-EYEBROW">Ein Familienprojekt</div>
<div class="RP-H1">Schön, dass du da bist.</div>
<div class="RP-SUB">Bereits 1.500 Briefe aus vielen Jahrzehnten sind im Archiv. Mit deinem Konto hilfst du mit, sie zu lesen, zu ordnen und für die nächsten Generationen zu bewahren.</div>
</div>
</div>
</div>
</div>
<!-- English -->
<div class="sb">
<span class="state-label st-hover">English (EN)</span>
<div class="wf">
<div class="wf-inner" style="background:#f0efe9;padding:22px 18px">
<div class="RP-ABOVE" style="margin-bottom:0">
<div class="RP-EYEBROW">A family project</div>
<div class="RP-H1">Glad to have you here.</div>
<div class="RP-SUB">Already 1,500 letters from many decades are in the archive. Your account helps read them, organise them, and keep them safe for the next generations.</div>
</div>
</div>
</div>
</div>
<!-- Spanish -->
<div class="sb">
<span class="state-label st-active">Spanish (ES)</span>
<div class="wf">
<div class="wf-inner" style="background:#f0efe9;padding:22px 18px">
<div class="RP-ABOVE" style="margin-bottom:0">
<div class="RP-EYEBROW">Un proyecto familiar</div>
<div class="RP-H1">Qué bien tenerte aquí.</div>
<div class="RP-SUB">Ya hay 1.500 cartas de muchas décadas en el archivo. Con tu cuenta ayudas a leerlas, organizarlas y conservarlas para las próximas generaciones.</div>
</div>
</div>
</div>
</div>
</div>
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference — Above-card<span>routes/register/+page.svelte</span></div>
<table>
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
<tr><td>Above-card wrapper</td><td><code>text-center mb-9</code></td><td>36px bottom margin</td><td></td></tr>
<tr><td>Eyebrow label</td><td><code>inline-block font-sans text-[11px] font-bold tracking-[.22em] uppercase text-ink-2 border-t border-b border-[#d4d2c5] px-3.5 py-1.5 mb-[18px]</code></td><td>11px / padding 6px 14px</td><td>i18n key: <code>m.register_eyebrow()</code></td></tr>
<tr><td>Headline</td><td><code>font-serif font-normal text-[46px] leading-[1.12] tracking-[-0.005em] text-ink mb-4</code></td><td>46px</td><td>i18n key: <code>m.register_headline()</code>. Tinos (serif), not bold — weight 400.</td></tr>
<tr><td>Subtext</td><td><code>font-serif text-lg leading-relaxed text-ink-2 max-w-[540px] mx-auto</code></td><td>18px, max-width 540px</td><td>i18n key: <code>m.register_sub()</code>. <code>text-wrap: pretty</code> via inline style for better line breaks.</td></tr>
</table>
</div>
</div>
<!-- ══════════════════════════════════════
SECTION 4 — FORM: ÜBER DICH (NAME FIELDS)
══════════════════════════════════════ -->
<div class="sec">
<div class="sec-h">
<span class="sec-num">4</span>
Form section — Über dich (name fields)
</div>
<div class="sg sg-4" style="gap:16px;align-items:start">
<!-- Empty / default -->
<div class="sb">
<span class="state-label st-default">Empty</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
<div class="RP-2COL">
<div class="RP-FIELD">
<div class="RP-LABEL">Vorname</div>
<input class="RP-INPUT" placeholder="z.B. Frieda" readonly>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Nachname</div>
<input class="RP-INPUT" placeholder="z.B. Lehmann" readonly>
</div>
</div>
</div>
</div>
</div>
<!-- Focused -->
<div class="sb">
<span class="state-label st-focus">Focused — Vorname</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
<div class="RP-2COL">
<div class="RP-FIELD">
<div class="RP-LABEL">Vorname</div>
<input class="RP-INPUT focused" placeholder="z.B. Frieda" readonly>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Nachname</div>
<input class="RP-INPUT" placeholder="z.B. Lehmann" readonly>
</div>
</div>
</div>
</div>
<div class="sc">Focus: navy border + soft navy glow ring.</div>
</div>
<!-- Filled -->
<div class="sb">
<span class="state-label st-active">Filled</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
<div class="RP-2COL">
<div class="RP-FIELD">
<div class="RP-LABEL">Vorname</div>
<input class="RP-INPUT" value="Frieda" readonly style="color:#012851">
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Nachname</div>
<input class="RP-INPUT" value="Lehmann" readonly style="color:#012851">
</div>
</div>
</div>
</div>
</div>
<!-- Error (submitted empty) -->
<div class="sb">
<span class="state-label st-error">Error — submitted empty</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
<div class="RP-2COL">
<div class="RP-FIELD">
<div class="RP-LABEL">Vorname</div>
<input class="RP-INPUT invalid" placeholder="z.B. Frieda" readonly>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Nachname</div>
<input class="RP-INPUT invalid" placeholder="z.B. Lehmann" readonly>
</div>
</div>
</div>
</div>
<div class="sc">Red border only — no text error for empty name fields (border color communicates invalid state on submit).</div>
</div>
</div>
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference — Name fields<span>routes/register/+page.svelte</span></div>
<table>
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
<tr><td>Section caption wrapper</td><td><code>flex items-center gap-3 mb-1</code></td><td></td><td>Gap 12px between text and rule line</td></tr>
<tr><td>Section caption text</td><td><code>font-sans text-[11px] font-bold tracking-[.18em] uppercase text-ink whitespace-nowrap</code></td><td>11px</td><td>i18n key: <code>m.register_section_about()</code></td></tr>
<tr><td>Section caption rule</td><td><code>flex-1 h-px bg-line</code></td><td>1px</td><td></td></tr>
<tr><td>2-column name grid</td><td><code>grid grid-cols-2 gap-4 mt-4</code></td><td>16px gap, 16px top margin</td><td>On mobile (&lt;480px): <code>grid-cols-1</code></td></tr>
<tr><td>Field wrapper</td><td><code>flex flex-col</code></td><td></td><td></td></tr>
<tr><td>Field label</td><td><code>block font-sans text-xs font-bold tracking-[.1em] uppercase text-ink-2 mb-2</code></td><td>12px, 8px bottom margin</td><td>i18n keys: <code>m.register_first_name()</code>, <code>m.register_last_name()</code></td></tr>
<tr><td>Text input — default</td><td><code>w-full border border-line bg-surface font-serif text-[17px] text-ink placeholder:text-ink-3 px-4 py-[14px] rounded-none outline-none focus-visible:ring-2 focus-visible:ring-focus-ring/15 focus-visible:border-ink transition-colors</code></td><td>17px font / 48px tall (14+14+17+1px borders)</td><td>No border-radius (Tailwind's rounded-none keeps it at 2px via border-radius:2px from global reset — or use <code>rounded-sm</code>)</td></tr>
<tr><td>Text input — invalid</td><td>+ <code>border-danger focus-visible:ring-danger/20</code></td><td></td><td>Applied when <code>submitted &amp;&amp; !field.trim()</code></td></tr>
<tr><td>Placeholders</td><td><code>placeholder:text-ink-3</code></td><td></td><td>DE: "z.B. Frieda" / "z.B. Lehmann" · EN: "e.g. Frieda" / "e.g. Lehmann"</td></tr>
</table>
</div>
</div>
<!-- ══════════════════════════════════════
SECTION 5 — FORM: KONTO (EMAIL + PASSWORDS)
══════════════════════════════════════ -->
<div class="sec">
<div class="sec-h">
<span class="sec-num">5</span>
Form section — Konto (e-mail &amp; passwords)
</div>
<div class="sg sg-3" style="gap:16px;align-items:start">
<!-- Default -->
<div class="sb">
<span class="state-label st-default">Default — empty</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
<div style="display:flex;flex-direction:column;gap:9px;margin-top:9px">
<div class="RP-FIELD">
<div class="RP-LABEL">E-Mail-Adresse</div>
<input class="RP-INPUT" placeholder="name@beispiel.de" readonly>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" placeholder="••••••••" readonly>
<button class="RP-SHOWBTN">Anzeigen</button>
</div>
<div class="RP-HINT">Mindestens 8 Zeichen.</div>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort bestätigen</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" placeholder="••••••••" readonly>
<button class="RP-SHOWBTN">Anzeigen</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Password valid + match -->
<div class="sb">
<span class="state-label st-active">Password valid + passwords match</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
<div style="display:flex;flex-direction:column;gap:9px;margin-top:9px">
<div class="RP-FIELD">
<div class="RP-LABEL">E-Mail-Adresse</div>
<input class="RP-INPUT" value="frieda@beispiel.de" readonly style="color:#012851">
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" value="••••••••" readonly style="color:#012851">
<button class="RP-SHOWBTN">Verbergen</button>
</div>
<div class="RP-HINT ok"><span class="RP-HINT-DOT"></span>Mindestens 8 Zeichen. ✓</div>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort bestätigen</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" value="••••••••" readonly style="color:#012851">
<button class="RP-SHOWBTN">Verbergen</button>
</div>
<div class="RP-HINT ok"><span class="RP-HINT-DOT"></span>Passwörter stimmen überein.</div>
</div>
</div>
</div>
</div>
<div class="sc">Green dot + green text when passwords match. Show button label changes to "Verbergen".</div>
</div>
<!-- Password mismatch error -->
<div class="sb">
<span class="state-label st-error">Password mismatch</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
<div style="display:flex;flex-direction:column;gap:9px;margin-top:9px">
<div class="RP-FIELD">
<div class="RP-LABEL">E-Mail-Adresse</div>
<input class="RP-INPUT" value="frieda@beispiel.de" readonly style="color:#012851">
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" value="••••••••" readonly style="color:#012851">
<button class="RP-SHOWBTN">Anzeigen</button>
</div>
<div class="RP-HINT ok"><span class="RP-HINT-DOT"></span>Mindestens 8 Zeichen. ✓</div>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort bestätigen</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT invalid" value="•••••••" readonly style="color:#012851">
<button class="RP-SHOWBTN">Anzeigen</button>
</div>
<div class="RP-HINT err">Die beiden Passwörter stimmen noch nicht überein.</div>
</div>
</div>
</div>
</div>
<div class="sc">Red border on confirm field + red error text. No dot icon for errors.</div>
</div>
</div>
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference — Konto fields<span>routes/register/+page.svelte</span></div>
<table>
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
<tr><td>Email input</td><td><code>w-full border border-line bg-surface font-serif text-[17px] text-ink placeholder:text-ink-3 px-4 py-[14px] rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-focus-ring/15 focus-visible:border-ink transition-colors</code></td><td>17px font / 48px tall</td><td>type="email", autocomplete="email"</td></tr>
<tr><td>Password input wrapper</td><td><code>relative</code></td><td></td><td>Contains input + show/hide button</td></tr>
<tr><td>Password input</td><td>Same as email input + <code>pr-[92px]</code></td><td>92px right padding to clear show/hide btn</td><td>type="password" toggles to type="text" on show</td></tr>
<tr><td>Show/hide button</td><td><code>absolute right-0 inset-y-0 px-3.5 font-sans text-[11px] font-bold tracking-[.1em] uppercase text-ink-2 border-l border-line hover:text-ink transition-colors</code></td><td>11px font</td><td>type="button" (prevent form submit). Label: <code>m.password_show()</code> / <code>m.password_hide()</code></td></tr>
<tr><td>Hint text — neutral</td><td><code>font-sans text-[13px] text-ink-3 mt-1.5</code></td><td>13px, 6px top margin</td><td>i18n key: <code>m.register_pw_hint()</code> ("Mindestens 8 Zeichen.")</td></tr>
<tr><td>Hint text — success (match)</td><td><code>font-sans text-[13px] text-[#0a6b56] mt-1.5 flex items-center gap-1.5</code></td><td></td><td>Shown when <code>pw.length &gt; 0 &amp;&amp; pw === pw2</code>. Dot: <code>w-2 h-2 rounded-full bg-turquoise inline-block</code></td></tr>
<tr><td>Hint text — error (mismatch)</td><td><code>font-sans text-[13px] text-danger mt-1.5</code></td><td></td><td>Shown when <code>pw2.length &gt; 0 &amp;&amp; pw !== pw2</code>. i18n key: <code>m.register_pw_match_no()</code></td></tr>
<tr><td>Validation logic</td><td></td><td></td><td>pwValid: length ≥ 8. pwMatch: pw === pw2 &amp;&amp; pw.length &gt; 0. pwMismatch: pw2.length &gt; 0 &amp;&amp; pw !== pw2. Errors only shown after first submit attempt or onBlur.</td></tr>
</table>
</div>
</div>
<!-- ══════════════════════════════════════
SECTION 6 — NOTIFICATION PREFERENCE CARD
══════════════════════════════════════ -->
<div class="sec">
<div class="sec-h">
<span class="sec-num">6</span>
Notification preference — CheckOption card
</div>
<div class="sg sg-2" style="gap:20px;align-items:start;max-width:720px">
<!-- Unchecked -->
<div class="sb">
<span class="state-label st-default">Unchecked</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Benachrichtigungen</span><span class="RP-SCAP-RULE"></span></div>
<div style="margin-top:9px">
<div class="RP-CHECK">
<div class="RP-CHECKBOX"></div>
<div class="RP-CHECK-BODY">
<div class="RP-CHECK-TITLE">Benachrichtige mich,</div>
<div class="RP-CHECK-DESC">wenn jemand mich in einem Kommentar erwähnt oder mir auf einen Kommentar antwortet.</div>
</div>
</div>
</div>
</div>
</div>
<div class="sc">Border: sand (#e4e2d7). Background: white. Checkbox: gray border, white fill.</div>
</div>
<!-- Checked (default) -->
<div class="sb">
<span class="state-label st-checked">Checked (default state)</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Benachrichtigungen</span><span class="RP-SCAP-RULE"></span></div>
<div style="margin-top:9px">
<div class="RP-CHECK checked">
<div class="RP-CHECKBOX checked">
<svg class="RP-CHECKMARK" viewBox="0 0 16 16" fill="none">
<path d="M3 8.5l3.2 3.2L13 5" stroke="#fff" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="RP-CHECK-BODY">
<div class="RP-CHECK-TITLE">Benachrichtige mich,</div>
<div class="RP-CHECK-DESC">wenn jemand mich in einem Kommentar erwähnt oder mir auf einen Kommentar antwortet.</div>
</div>
</div>
</div>
</div>
</div>
<div class="sc">Default state: checked (opt-in by default). Border: navy. Background: mint tint (rgba(161,220,216,.18)).</div>
</div>
</div>
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference — CheckOption card<span>routes/register/+page.svelte</span></div>
<table>
<tr><th>Element</th><th>Tailwind classes / values</th><th>Real px</th><th>Notes</th></tr>
<tr><td>Card wrapper (label)</td><td><code>&lt;label&gt;</code> with <code>flex gap-3.5 p-3.5 border rounded-sm cursor-pointer transition-colors items-start</code></td><td>14px gap, 14px 16px padding</td><td>The entire card is a <code>&lt;label&gt;</code> for large click target. Wrap real <code>&lt;input type="checkbox"&gt;</code> visually hidden inside.</td></tr>
<tr><td>Card — unchecked</td><td><code>border-line bg-surface</code></td><td></td><td></td></tr>
<tr><td>Card — checked</td><td><code>border-ink bg-[rgba(161,220,216,0.18)]</code></td><td></td><td>Mint tint bg. No Tailwind utility exists — use inline style or arbitrary value.</td></tr>
<tr><td>Real checkbox input</td><td><code>sr-only</code> (visually hidden, accessible)</td><td></td><td>Bind <code>checked</code> state to this. Default: <code>true</code>.</td></tr>
<tr><td>Custom checkbox box</td><td><code>flex-shrink-0 w-[22px] h-[22px] rounded-sm border-2 flex items-center justify-center mt-px transition-all</code></td><td>22×22px</td><td>Min touch target met by the card itself.</td></tr>
<tr><td>Checkbox — unchecked</td><td><code>border-gray-400 bg-surface</code></td><td></td><td></td></tr>
<tr><td>Checkbox — checked</td><td><code>border-ink bg-ink</code></td><td></td><td>White checkmark SVG inside.</td></tr>
<tr><td>Checkmark SVG</td><td>14×14px, path: M3 8.5l3.2 3.2L13 5, stroke white 2.4, round caps</td><td>14px icon</td><td>Only rendered when checked.</td></tr>
<tr><td>Option title</td><td><code>font-sans text-sm font-semibold text-ink</code></td><td>14px</td><td>i18n key: <code>m.register_notify_label()</code></td></tr>
<tr><td>Option description</td><td><code>font-serif text-[15px] leading-normal text-ink-2 mt-0.5</code></td><td>15px</td><td>i18n key: <code>m.register_notify_desc()</code></td></tr>
</table>
</div>
</div>
<!-- ══════════════════════════════════════
SECTION 7 — SUBMIT BUTTON + FOOTER LINKS
══════════════════════════════════════ -->
<div class="sec">
<div class="sec-h">
<span class="sec-num">7</span>
Submit button &amp; footer links
</div>
<div class="sg sg-3" style="gap:16px;align-items:start">
<!-- Default -->
<div class="sb">
<span class="state-label st-default">Default</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<button class="RP-SUBMIT">
Konto erstellen
<svg viewBox="0 0 24 24" width="9" height="9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
</button>
<div class="RP-FOOTER">
<div class="RP-PRIVACY">Mit dem Erstellen eines Kontos stimmst du der <a href="#">Datenschutzerklärung</a> und den <a href="#">Nutzungsbedingungen</a> zu.</div>
<div class="RP-SIGNIN">Du hast bereits ein Konto? <a href="#">Anmelden</a></div>
</div>
</div>
</div>
</div>
<!-- Hover -->
<div class="sb">
<span class="state-label st-hover">Submit — hover</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<button class="RP-SUBMIT hover">
Konto erstellen
<svg viewBox="0 0 24 24" width="9" height="9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
</button>
<div class="RP-FOOTER">
<div class="RP-PRIVACY">Mit dem Erstellen eines Kontos stimmst du der <a href="#">Datenschutzerklärung</a> und den <a href="#">Nutzungsbedingungen</a> zu.</div>
<div class="RP-SIGNIN">Du hast bereits ein Konto? <a href="#">Anmelden</a></div>
</div>
</div>
</div>
<div class="sc">Bg darkens: #012851 → #01386f. No scale transform.</div>
</div>
<!-- Loading -->
<div class="sb">
<span class="state-label st-loading">Loading (after submit)</span>
<div class="wf">
<div class="wf-inner" style="background:#fff;padding:16px">
<button class="RP-SUBMIT loading">
<div class="RP-SPINNER"></div>
Wird erstellt…
</button>
<div class="RP-FOOTER">
<div class="RP-PRIVACY">Mit dem Erstellen eines Kontos stimmst du der <a href="#">Datenschutzerklärung</a> und den <a href="#">Nutzungsbedingungen</a> zu.</div>
<div class="RP-SIGNIN">Du hast bereits ein Konto? <a href="#">Anmelden</a></div>
</div>
</div>
</div>
<div class="sc">Arrow icon replaced by spinner. Button opacity 0.85. cursor: wait. 850ms simulated delay then success.</div>
</div>
</div>
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference — Submit &amp; footer<span>routes/register/+page.svelte</span></div>
<table>
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
<tr><td>Submit button</td><td><code>w-full bg-primary text-primary-fg font-sans text-[13px] font-bold tracking-[.12em] uppercase py-4 px-6 rounded-sm flex items-center justify-center gap-2.5 transition-colors hover:bg-[#01386f] mt-8</code></td><td>13px font / 16px top+bottom padding / ~53px tall</td><td>Arrow icon: 16×16px SVG. Loading state: add <code>opacity-85 cursor-wait</code>, swap icon for spinner.</td></tr>
<tr><td>Loading spinner</td><td><code>w-4 h-4 rounded-full border-2 border-white/30 border-t-white animate-spin</code></td><td>16px</td><td>Replaces arrow icon during loading state</td></tr>
<tr><td>Footer wrapper</td><td><code>mt-6 flex flex-col gap-3.5 text-center</code></td><td>24px top margin, 14px gap</td><td></td></tr>
<tr><td>Privacy text</td><td><code>font-serif text-sm text-ink-3 leading-relaxed</code></td><td>14px</td><td>Links within: <code>text-ink underline decoration-accent underline-offset-[3px] decoration-2</code></td></tr>
<tr><td>Sign-in prompt</td><td><code>font-sans text-[13px] text-ink-2</code></td><td>13px</td><td>i18n: <code>m.register_have_account()</code> + <code>m.register_sign_in()</code></td></tr>
<tr><td>Sign-in link</td><td><code>font-sans text-xs font-bold tracking-[.1em] uppercase text-ink underline decoration-accent underline-offset-[4px] decoration-2</code></td><td>12px</td><td>href="/login"</td></tr>
</table>
</div>
</div>
<!-- ══════════════════════════════════════
SECTION 8 — SUCCESS PANEL
══════════════════════════════════════ -->
<div class="sec">
<div class="sec-h">
<span class="sec-num">8</span>
Success panel — post-submit state
</div>
<div class="sg sg-2" style="gap:20px;align-items:start;max-width:720px">
<!-- Success — German -->
<div class="sb">
<span class="state-label st-success">Success — German</span>
<div class="wf" style="max-width:380px">
<div class="wf-inner">
<div class="wf-bar">
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
<div class="urlbar"><span>localhost:3000/register</span></div>
</div>
<div class="RP">
<div class="RP-HEADER">
<div class="RP-STRIPE"></div>
<div class="RP-NAV">
<span class="RP-WORDMARK">Familienarchiv</span>
<div class="RP-LANG">
<button class="RP-LANG-BTN on">DE</button>
<button class="RP-LANG-BTN">EN</button>
<button class="RP-LANG-BTN">ES</button>
</div>
</div>
</div>
<div class="RP-MAIN">
<div class="RP-INNER">
<div class="RP-CARD">
<div class="RP-SUCCESS">
<div class="RP-SUCCESS-CIRCLE">
<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="#012851" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.2 4.2L19 7"/></svg>
</div>
<div class="RP-SUCCESS-H">Willkommen im Familienarchiv.</div>
<div class="RP-SUCCESS-B">Wir haben dir eine E-Mail an frieda@beispiel.de geschickt, um deine Adresse zu bestätigen. Anschließend kannst du dich anmelden.</div>
<div class="RP-SUCCESS-ACTIONS">
<button class="RP-SUBMIT" style="margin-top:0">Zur Anmeldung</button>
<button class="RP-SUCCESS-RESEND">E-Mail erneut senden</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Success — English -->
<div class="sb">
<span class="state-label st-success">Success — English</span>
<div class="wf" style="max-width:380px">
<div class="wf-inner">
<div class="wf-bar">
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
<div class="urlbar"><span>localhost:3000/register?lang=en</span></div>
</div>
<div class="RP">
<div class="RP-HEADER">
<div class="RP-STRIPE"></div>
<div class="RP-NAV">
<span class="RP-WORDMARK">Familienarchiv</span>
<div class="RP-LANG">
<button class="RP-LANG-BTN">DE</button>
<button class="RP-LANG-BTN on">EN</button>
<button class="RP-LANG-BTN">ES</button>
</div>
</div>
</div>
<div class="RP-MAIN">
<div class="RP-INNER">
<div class="RP-CARD">
<div class="RP-SUCCESS">
<div class="RP-SUCCESS-CIRCLE">
<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="#012851" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.2 4.2L19 7"/></svg>
</div>
<div class="RP-SUCCESS-H">Welcome to Familienarchiv.</div>
<div class="RP-SUCCESS-B">We sent an email to frieda@example.com to confirm your address. After that, you can sign in.</div>
<div class="RP-SUCCESS-ACTIONS">
<button class="RP-SUBMIT" style="margin-top:0">Go to sign-in</button>
<button class="RP-SUCCESS-RESEND">Resend email</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference — Success panel<span>routes/register/+page.svelte</span></div>
<table>
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
<tr><td>Success wrapper</td><td><code>text-center py-2</code></td><td>8px top/bottom padding</td><td>Replaces the entire <code>&lt;form&gt;</code> block. Same card container as the form.</td></tr>
<tr><td>Checkmark circle</td><td><code>w-[72px] h-[72px] rounded-full bg-[rgba(161,220,216,0.35)] inline-flex items-center justify-center mb-6</code></td><td>72px</td><td>Checkmark SVG: 34×34px, stroke #012851 2.2px.</td></tr>
<tr><td>Success headline</td><td><code>font-serif font-normal text-[30px] leading-tight text-ink mb-3.5</code></td><td>30px</td><td>i18n key: <code>m.register_success_h()</code></td></tr>
<tr><td>Success body</td><td><code>font-serif text-[17px] leading-[1.55] text-ink-2 max-w-[420px] mx-auto mb-7</code></td><td>17px, max 420px</td><td>i18n key: <code>m.register_success_b()</code>. Replace <code>{email}</code> token with entered email.</td></tr>
<tr><td>CTA button</td><td>Same as submit button — full-width navy</td><td></td><td>href="/login". i18n: <code>m.register_success_action()</code></td></tr>
<tr><td>Resend link</td><td><code>font-sans text-xs font-bold tracking-[.1em] uppercase text-ink-2 px-2.5 py-2.5 underline decoration-accent underline-offset-[4px] decoration-2</code></td><td>12px</td><td>i18n: <code>m.register_success_resend()</code>. Calls <code>/api/auth/resend-verification</code>. Container: <code>max-w-[320px] mx-auto flex flex-col gap-2.5</code></td></tr>
</table>
</div>
</div>
<!-- ══════════════════════════════════════
SECTION 9 — MOBILE LAYOUT (320px)
══════════════════════════════════════ -->
<div class="sec">
<div class="sec-h">
<span class="sec-num">9</span>
Mobile layout — 320px
</div>
<div class="sg sg-3" style="gap:20px;align-items:start">
<!-- Mobile default -->
<div class="sb">
<span class="state-label st-default">320px — form at rest</span>
<div class="WF-M">
<div class="WF-M-STATUS">
<span class="WF-M-TIME">9:41</span>
<div class="WF-M-ICONS">
<div class="WF-M-ICON"></div>
<div class="WF-M-ICON"></div>
<div class="WF-M-ICON"></div>
</div>
</div>
<div class="RP" style="min-height:480px">
<div class="RP-HEADER">
<div class="RP-STRIPE"></div>
<div class="RP-NAV" style="padding:0 10px">
<span class="RP-WORDMARK" style="font-size:8px">Familienarchiv</span>
<div class="RP-LANG">
<button class="RP-LANG-BTN on" style="font-size:5px;padding:2px 4px">DE</button>
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">EN</button>
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">ES</button>
</div>
</div>
</div>
<div style="padding:14px 10px;background:#f0efe9">
<!-- Above-card collapsed -->
<div style="text-align:center;margin-bottom:11px">
<div class="RP-EYEBROW" style="font-size:5px;padding:2px 6px;margin-bottom:6px">Ein Familienprojekt</div>
<div class="RP-H1" style="font-size:15px;margin-bottom:5px">Schön, dass du<br>da bist.</div>
<div class="RP-SUB" style="font-size:7.5px;max-width:none">Bereits 1.500 Briefe sind im Archiv. Mit deinem Konto hilfst du mit, sie zu bewahren.</div>
</div>
<!-- Form card -->
<div class="RP-CARD" style="padding:13px">
<div style="margin-bottom:9px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
<!-- Single column on mobile -->
<div style="display:flex;flex-direction:column;gap:7px;margin-top:7px">
<div class="RP-FIELD">
<div class="RP-LABEL">Vorname</div>
<input class="RP-INPUT" placeholder="z.B. Frieda" style="font-size:8px;padding:5px 7px" readonly>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Nachname</div>
<input class="RP-INPUT" placeholder="z.B. Lehmann" style="font-size:8px;padding:5px 7px" readonly>
</div>
</div>
</div>
<div style="margin-bottom:9px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
<div style="display:flex;flex-direction:column;gap:7px;margin-top:7px">
<div class="RP-FIELD">
<div class="RP-LABEL">E-Mail</div>
<input class="RP-INPUT" placeholder="name@beispiel.de" style="font-size:8px;padding:5px 7px" readonly>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" placeholder="••••••••" style="font-size:8px;padding:5px 7px" readonly>
<button class="RP-SHOWBTN" style="font-size:5px;padding:0 5px">Zeig.</button>
</div>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort best.</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" placeholder="••••••••" style="font-size:8px;padding:5px 7px" readonly>
<button class="RP-SHOWBTN" style="font-size:5px;padding:0 5px">Zeig.</button>
</div>
</div>
</div>
</div>
<button class="RP-SUBMIT" style="font-size:6px;padding:7px;margin-top:10px">Konto erstellen →</button>
</div>
</div>
</div>
</div>
<div class="sc">Name grid collapses to single column. Subtext shortened. Card padding reduced. All inputs remain 17px on real device (browser zooms accordingly).</div>
</div>
<!-- Mobile — filled + near submit -->
<div class="sb">
<span class="state-label st-active">320px — filled, ready to submit</span>
<div class="WF-M">
<div class="WF-M-STATUS">
<span class="WF-M-TIME">9:41</span>
<div class="WF-M-ICONS">
<div class="WF-M-ICON"></div>
<div class="WF-M-ICON"></div>
<div class="WF-M-ICON"></div>
</div>
</div>
<div class="RP" style="min-height:480px">
<div class="RP-HEADER">
<div class="RP-STRIPE"></div>
<div class="RP-NAV" style="padding:0 10px">
<span class="RP-WORDMARK" style="font-size:8px">Familienarchiv</span>
<div class="RP-LANG">
<button class="RP-LANG-BTN on" style="font-size:5px;padding:2px 4px">DE</button>
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">EN</button>
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">ES</button>
</div>
</div>
</div>
<div style="padding:14px 10px;background:#f0efe9">
<div style="text-align:center;margin-bottom:11px">
<div class="RP-H1" style="font-size:15px;margin-bottom:5px">Schön, dass du<br>da bist.</div>
</div>
<div class="RP-CARD" style="padding:13px">
<div style="margin-bottom:9px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
<div style="display:flex;flex-direction:column;gap:7px;margin-top:7px">
<div class="RP-FIELD">
<div class="RP-LABEL">Vorname</div>
<input class="RP-INPUT" value="Frieda" style="font-size:8px;padding:5px 7px;color:#012851" readonly>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Nachname</div>
<input class="RP-INPUT" value="Lehmann" style="font-size:8px;padding:5px 7px;color:#012851" readonly>
</div>
</div>
</div>
<div style="margin-bottom:9px">
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
<div style="display:flex;flex-direction:column;gap:7px;margin-top:7px">
<div class="RP-FIELD">
<div class="RP-LABEL">E-Mail</div>
<input class="RP-INPUT" value="frieda@b.de" style="font-size:8px;padding:5px 7px;color:#012851" readonly>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Passwort</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" value="••••••••" style="font-size:8px;padding:5px 7px;color:#012851" readonly>
<button class="RP-SHOWBTN" style="font-size:5px;padding:0 5px">Zeig.</button>
</div>
<div class="RP-HINT ok" style="font-size:6px"><span class="RP-HINT-DOT"></span>✓ 8 Zeichen</div>
</div>
<div class="RP-FIELD">
<div class="RP-LABEL">Best.</div>
<div class="RP-PWINPUT">
<input class="RP-INPUT" value="••••••••" style="font-size:8px;padding:5px 7px;color:#012851" readonly>
<button class="RP-SHOWBTN" style="font-size:5px;padding:0 5px">Zeig.</button>
</div>
<div class="RP-HINT ok" style="font-size:6px"><span class="RP-HINT-DOT"></span>Stimmen überein.</div>
</div>
</div>
</div>
<button class="RP-SUBMIT" style="font-size:6px;padding:7px;margin-top:10px">Konto erstellen →</button>
</div>
</div>
</div>
</div>
</div>
<!-- Mobile success -->
<div class="sb">
<span class="state-label st-success">320px — success panel</span>
<div class="WF-M">
<div class="WF-M-STATUS">
<span class="WF-M-TIME">9:41</span>
<div class="WF-M-ICONS">
<div class="WF-M-ICON"></div>
<div class="WF-M-ICON"></div>
<div class="WF-M-ICON"></div>
</div>
</div>
<div class="RP" style="min-height:340px">
<div class="RP-HEADER">
<div class="RP-STRIPE"></div>
<div class="RP-NAV" style="padding:0 10px">
<span class="RP-WORDMARK" style="font-size:8px">Familienarchiv</span>
<div class="RP-LANG">
<button class="RP-LANG-BTN on" style="font-size:5px;padding:2px 4px">DE</button>
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">EN</button>
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">ES</button>
</div>
</div>
</div>
<div style="padding:14px 10px;background:#f0efe9">
<div class="RP-CARD" style="padding:16px">
<div class="RP-SUCCESS" style="padding:6px 0">
<div class="RP-SUCCESS-CIRCLE" style="width:30px;height:30px;margin-bottom:8px">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#012851" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.2 4.2L19 7"/></svg>
</div>
<div class="RP-SUCCESS-H" style="font-size:12px;margin-bottom:5px">Willkommen im Familienarchiv.</div>
<div class="RP-SUCCESS-B" style="font-size:7.5px;max-width:none;margin-bottom:10px">Wir haben dir eine E-Mail an frieda@b.de geschickt, um deine Adresse zu bestätigen.</div>
<div style="max-width:none">
<button class="RP-SUBMIT" style="font-size:6px;padding:7px;margin-top:0">Zur Anmeldung</button>
<button class="RP-SUCCESS-RESEND" style="font-size:6px;padding:4px">E-Mail erneut senden</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference — Mobile breakpoints<span>routes/register/+page.svelte</span></div>
<table>
<tr><th>Breakpoint</th><th>Change</th><th>Tailwind</th><th>Notes</th></tr>
<tr><td>Default (mobile-first, &lt;480px)</td><td>Single-column name grid</td><td><code>grid grid-cols-1 gap-4</code></td><td>Start mobile-first; add <code>sm:grid-cols-2</code> at 480px+</td></tr>
<tr><td>sm: 480px+</td><td>2-column name grid</td><td><code>sm:grid-cols-2</code></td><td>Custom breakpoint or use <code>min-[480px]:grid-cols-2</code></td></tr>
<tr><td>Default (mobile-first)</td><td>Card padding 24px</td><td><code>p-6</code></td><td>Reduced from desktop 40px</td></tr>
<tr><td>sm: 640px+</td><td>Card padding 40px</td><td><code>sm:p-10</code></td><td>Full desktop padding</td></tr>
<tr><td>Default (mobile-first)</td><td>Main padding 32px top</td><td><code>pt-8</code></td><td>Reduced from desktop 64px</td></tr>
<tr><td>sm: 640px+</td><td>Main padding 64px top</td><td><code>sm:pt-16</code></td><td></td></tr>
<tr><td>All breakpoints</td><td>Input font size 17px</td><td><code>text-[17px]</code></td><td>Do NOT reduce on mobile — large text is critical for the 60+ audience. The browser will zoom the viewport to show text clearly.</td></tr>
<tr><td>All breakpoints</td><td>Touch targets ≥ 44px</td><td>Button: <code>py-4</code> = 16px × 2 + 13px font = 45px ✓</td><td>CheckOption card: entire card is the touch target (~52px+ tall) ✓</td></tr>
</table>
</div>
</div>
<!-- ══════════════════════════════════════
SECTION 10 — IMPLEMENTATION NOTES
══════════════════════════════════════ -->
<div class="sec">
<div class="sec-h">
<span class="sec-num">10</span>
Implementation notes — i18n, accessibility, backend
</div>
<div class="sg sg-3" style="gap:20px;align-items:start">
<!-- i18n keys -->
<div class="sb">
<div class="sl">i18n keys (Paraglide)</div>
<div class="impl-ref" style="margin-top:0">
<div class="impl-ref-hdr">New keys — messages/de.json + en.json + es.json</div>
<table>
<tr><th>Key</th><th>DE</th></tr>
<tr><td><code>register_eyebrow</code></td><td>Ein Familienprojekt</td></tr>
<tr><td><code>register_headline</code></td><td>Schön, dass du da bist.</td></tr>
<tr><td><code>register_sub</code></td><td>Bereits 1.500 Briefe aus vielen Jahrzehnten…</td></tr>
<tr><td><code>register_section_about</code></td><td>Über dich</td></tr>
<tr><td><code>register_section_account</code></td><td>Konto</td></tr>
<tr><td><code>register_section_prefs</code></td><td>Benachrichtigungen</td></tr>
<tr><td><code>register_first_name</code></td><td>Vorname</td></tr>
<tr><td><code>register_last_name</code></td><td>Nachname</td></tr>
<tr><td><code>register_email</code></td><td>E-Mail-Adresse</td></tr>
<tr><td><code>register_password</code></td><td>Passwort</td></tr>
<tr><td><code>register_password_confirm</code></td><td>Passwort bestätigen</td></tr>
<tr><td><code>register_pw_hint</code></td><td>Mindestens 8 Zeichen.</td></tr>
<tr><td><code>register_pw_match_ok</code></td><td>Passwörter stimmen überein.</td></tr>
<tr><td><code>register_pw_match_no</code></td><td>Die beiden Passwörter stimmen noch nicht überein.</td></tr>
<tr><td><code>register_notify_label</code></td><td>Benachrichtige mich,</td></tr>
<tr><td><code>register_notify_desc</code></td><td>wenn jemand mich in einem Kommentar erwähnt…</td></tr>
<tr><td><code>register_submit</code></td><td>Konto erstellen</td></tr>
<tr><td><code>register_submit_loading</code></td><td>Wird erstellt…</td></tr>
<tr><td><code>register_have_account</code></td><td>Du hast bereits ein Konto?</td></tr>
<tr><td><code>register_sign_in</code></td><td>Anmelden</td></tr>
<tr><td><code>register_success_h</code></td><td>Willkommen im Familienarchiv.</td></tr>
<tr><td><code>register_success_b</code></td><td>Wir haben dir eine E-Mail an {email} geschickt…</td></tr>
<tr><td><code>register_success_action</code></td><td>Zur Anmeldung</td></tr>
<tr><td><code>register_success_resend</code></td><td>E-Mail erneut senden</td></tr>
<tr><td><code>register_privacy</code></td><td>Mit dem Erstellen eines Kontos stimmst du der</td></tr>
<tr><td><code>register_privacy_link</code></td><td>Datenschutzerklärung</td></tr>
<tr><td><code>register_terms_link</code></td><td>Nutzungsbedingungen</td></tr>
<tr><td><code>password_show</code></td><td>Anzeigen</td></tr>
<tr><td><code>password_hide</code></td><td>Verbergen</td></tr>
<tr><td><code>page_title_register</code></td><td>Registrieren — Familienarchiv</td></tr>
</table>
</div>
</div>
<!-- Accessibility -->
<div class="sb">
<div class="sl">Accessibility requirements</div>
<div class="impl-ref" style="margin-top:0">
<div class="impl-ref-hdr">WCAG 2.2 checklist</div>
<table>
<tr><th>Criterion</th><th>Implementation</th><th>Level</th></tr>
<tr><td>1.4.3 Contrast (text)</td><td>Navy #012851 on white: 14.5:1 ✓. Gray #4b5563 on white: 7.6:1 ✓. Gray #6b7280 on white: 5.9:1 ✓ (AA only — use for large text / hints)</td><td>AA</td></tr>
<tr><td>2.4.2 Page title</td><td><code>&lt;svelte:head&gt;&lt;title&gt;{m.page_title_register()}&lt;/title&gt;</code></td><td>A</td></tr>
<tr><td>1.3.1 Form labels</td><td>Every input has <code>&lt;label for="..."&gt;</code>. No placeholder-as-label.</td><td>A</td></tr>
<tr><td>2.4.3 Focus order</td><td>Tab order: DE/EN/ES → Vorname → Nachname → Email → Password → Confirm → Checkbox → Submit</td><td>A</td></tr>
<tr><td>2.4.7 Focus visible</td><td><code>focus-visible:ring-2 focus-visible:ring-focus-ring/15</code> on all inputs. <code>focus-visible:ring-2 focus-visible:ring-white/80</code> on submit button.</td><td>AA</td></tr>
<tr><td>2.5.3 Touch target (2.2)</td><td>All buttons ≥ 44px tall. CheckOption card ≥ 44px. Show/hide button: 44px tall (same as input height).</td><td>AA</td></tr>
<tr><td>4.1.3 Status messages</td><td>Password match/mismatch feedback announced via <code>aria-live="polite"</code> region.</td><td>AA</td></tr>
<tr><td>1.4.4 Resize text</td><td>All sizes in rem/px that scale with browser zoom. 17px input font = 1.0625rem.</td><td>AA</td></tr>
<tr><td>Error identification</td><td><code>aria-invalid="true"</code> on invalid inputs. <code>aria-describedby</code> linking to error message element.</td><td>A</td></tr>
</table>
</div>
</div>
<!-- Backend wiring -->
<div class="sb">
<div class="sl">Backend wiring</div>
<div class="impl-ref" style="margin-top:0">
<div class="impl-ref-hdr">Route + API integration</div>
<table>
<tr><th>Concern</th><th>Details</th></tr>
<tr><td>Route</td><td><code>frontend/src/routes/register/+page.svelte</code> + <code>+page.server.ts</code></td></tr>
<tr><td>Form action</td><td>POST <code>?/register</code><code>+page.server.ts</code> action</td></tr>
<tr><td>API endpoint</td><td><code>POST /api/auth/register</code> — create new AppUser (first name, last name, email, password, notifyOnMention)</td></tr>
<tr><td>On success</td><td>Backend sends verification email. Frontend shows success panel with entered email.</td></tr>
<tr><td>Resend endpoint</td><td><code>POST /api/auth/resend-verification</code> — body: <code>{ email }</code></td></tr>
<tr><td>Client-side validation</td><td>Run before server submit: required fields, email format, password ≥ 8 chars, password match. Show errors inline on first submit attempt.</td></tr>
<tr><td>Server-side errors</td><td>Map backend error codes via <code>getErrorMessage(code)</code>: EMAIL_ALREADY_EXISTS → Paraglide key. Never display raw backend messages.</td></tr>
<tr><td>notifyOnMention field</td><td>CheckOption checked state maps to <code>notifyOnMention: boolean</code> in request body. Default: <code>true</code>.</td></tr>
<tr><td>Language preference</td><td>The active language is a client-side Paraglide state only (page.svelte <code>$state</code>). Not persisted until the user creates an account; can be added to the registration payload as <code>preferredLanguage: 'de' | 'en' | 'es'</code> if the backend supports it.</td></tr>
<tr><td>Auth header</td><td>Register page is unauthenticated. Use a minimal header (AuthHeader without nav links) or a dedicated RegisterHeader.</td></tr>
</table>
</div>
<div class="ann-info" style="margin-top:16px">
<strong>Implementation sequence:</strong>
<ul>
<li>1. Add <code>POST /api/auth/register</code> to Spring Boot backend (new controller + service method)</li>
<li>2. Add <code>POST /api/auth/resend-verification</code></li>
<li>3. Regenerate TypeScript API types (<code>npm run generate:api</code>)</li>
<li>4. Implement <code>frontend/src/routes/register/+page.svelte</code></li>
<li>5. Add i18n keys to all three message files</li>
<li>6. Add route to navigation (unauthenticated footer or login page link)</li>
</ul>
</div>
</div>
</div>
</div>
</div><!-- /.doc -->
</body>
</html>