Files
familienarchiv/docs/specs/person-title-type-fields-spec.html
Marcel 7a6b3d66fb docs(spec): add design spec for person title & type fields UI
Covers segmented type control, title input, conditional field
visibility, PersonCard title display, mobile layout, and a11y.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 21:42:17 +02:00

1021 lines
59 KiB
HTML
Raw Permalink 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="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Person Title &amp; Type Fields — 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:1440px;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:620px;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}
.dec-value s{color:rgba(255,255,255,.3);font-weight:400}
/* ── 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}
/* ── Screen grid ─── */
.sg{display:grid;gap:20px;align-items:start}
.sg-2{grid-template-columns:1fr 1fr}
.sg-2a{grid-template-columns:1.3fr 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}
.sz{background:#E8E4DF;color:#666;padding:1px 5px;border-radius:3px;font-size:8px}
.state{padding:1px 6px;border-radius:3px;font-size:8px;font-weight:700}
.st-new{background:#DCFCE7;color:#166534}
.st-changed{background:#DBEAFE;color:#1E40AF}
.sc{font-size:8.5px;color:#888;margin-top:6px;font-style:italic;line-height:1.5}
/* ── Annotation callouts ─── */
.ann{display:inline-block;font-size:7.5px;font-weight:700;color:#C2410C;background:#FFF7ED;border:1px solid #FDBA74;border-radius:3px;padding:1px 5px;white-space:nowrap}
.ann-block{background:#FFF7ED;border:1px solid #FDBA74;border-radius:5px;padding:8px 10px;font-size:10px;color:#7C2D12;line-height:1.5;margin-top:10px}
.ann-block strong{font-weight:800}
.ann-block ul{padding-left:14px;display:flex;flex-direction:column;gap:3px;margin-top:5px}
/* ── Chrome (browser frame) ─── */
.wf{background:#fff;border:2px solid #B8B4AE;border-radius:10px;overflow:hidden;box-shadow:0 4px 18px rgba(0,0,0,.08)}
.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}
.N{height:42px;background:#0D2240;display:flex;align-items:center;padding:0 16px;gap:12px;flex-shrink:0}
.logo{font-size:9px;font-weight:900;color:#fff;letter-spacing:1px}
.nl{font-size:7.5px;color:rgba(255,255,255,.4);font-weight:700;text-transform:uppercase;letter-spacing:.5px}
.nl.on{color:#fff;border-bottom:2px solid #A6DAD8;padding-bottom:2px}
.nr{margin-left:auto;display:flex;gap:7px;align-items:center}
.nico{width:20px;height:20px;background:rgba(255,255,255,.1);border-radius:4px}
.MAIN{padding:14px 18px;display:flex;flex-direction:column;gap:10px;background:#ECEAE4}
/* ── Form elements ─── */
.CARD{background:#fff;border:1.5px solid #E0DDD6;border-radius:4px;overflow:hidden;padding:14px 18px}
.CARD-H{font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:1px;color:#AAA;margin-bottom:10px}
.FL{font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:#888;margin-bottom:3px}
.FI{height:24px;border:1.5px solid #D1D5DB;border-radius:3px;background:#fff;display:flex;align-items:center;padding:0 8px;font-size:9px;color:#333;font-family:Georgia,serif}
.FI.placeholder{color:#C8C4BE;font-style:italic}
.FI.sm{width:60px}
.FI.md{width:120px}
.FTA{height:48px;border:1.5px solid #D1D5DB;border-radius:3px;background:#fff;padding:5px 8px;font-size:9px;color:#C8C4BE;font-style:italic;font-family:Georgia,serif;resize:none}
.FORM-ROW{display:flex;gap:8px;margin-bottom:8px}
.FORM-COL{flex:1}
.FORM-COL.narrow{flex:0 0 60px}
.FORM-COL.full{flex:0 0 100%}
/* ── Segmented control ─── */
.SEG{display:flex;border:1.5px solid #D1D5DB;border-radius:4px;overflow:hidden;margin-bottom:12px}
.SEG-BTN{flex:1;text-align:center;font-size:8px;font-weight:700;padding:6px 4px;color:#888;background:#F7F5F2;border-right:1px solid #D1D5DB;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:3px}
.SEG-BTN:last-child{border-right:none}
.SEG-BTN.active{background:#002850;color:#fff}
.SEG-BTN svg{width:10px;height:10px}
/* ── PersonCard (detail view) ─── */
.PC{background:#fff;border:1.5px solid #E0DDD6;border-radius:4px;overflow:hidden}
.PC-BAR{height:5px;background:#002850}
.PC-BODY{padding:14px;text-align:center}
.AV{border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:900;flex-shrink:0;margin:0 auto 8px}
.AV-lg{width:36px;height:36px;font-size:11px}
.AV-navy{background:#002850;color:#fff}
.PC-TITLE{font-size:7px;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:#AAA;margin-bottom:2px}
.PC-NAME{font-size:11px;font-weight:700;color:#002850;font-family:Georgia,serif;margin-bottom:2px}
.PC-ALIAS{font-size:8px;color:#888;font-style:italic;margin-bottom:2px}
.PC-DATES{font-size:8px;color:#AAA;margin-bottom:8px}
.PC-BADGE{display:inline-flex;align-items:center;gap:3px;font-size:7px;font-weight:600;padding:2px 7px;border-radius:10px;margin-bottom:2px}
.PC-BADGE.inst{background:#e8eff7;color:#1a4971;border:1px solid #c4d5e8}
.PC-NOTES{background:#F7F5F2;border:1px solid #E8E4DF;border-radius:3px;padding:6px 8px;font-size:8px;color:#888;text-align:left;margin-bottom:8px;line-height:1.5}
.PC-NOTES-LABEL{font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#AAA;margin-bottom:3px}
.PC-BTN{display:flex;align-items:center;justify-content:center;gap:4px;width:100%;border:1.5px solid #E0DDD6;border-radius:3px;padding:5px;font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#888;cursor:pointer}
/* ── List card (grid view) ─── */
.LC{background:#fff;border:1.5px solid #E0DDD6;border-radius:4px;padding:10px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:3px}
.LC:hover{border-left:3px solid #A6DAD8;box-shadow:0 2px 8px rgba(0,0,0,.08)}
.AV-sm{width:24px;height:24px;font-size:7px}
.LC-NAME{font-size:8px;font-weight:700;color:#002850;font-family:Georgia,serif}
.LC-ALIAS{font-size:7px;color:#888;font-style:italic}
.LC-DATES{font-size:6.5px;color:#AAA}
.LC-BADGE{font-size:6px;font-weight:600;padding:1px 5px;border-radius:8px}
.LC-COUNT{font-size:6.5px;font-weight:600;color:#888;border:1px solid #E0DDD6;border-radius:8px;padding:1px 5px;background:#F7F5F2}
/* ── Save bar ─── */
.SAVE{margin-top:8px;border:1.5px solid #E0DDD6;border-radius:4px;background:#fff;padding:8px 18px;display:flex;align-items:center;justify-content:space-between}
.SAVE-DISCARD{font-size:8px;color:#888;font-weight:600}
.SAVE-BTN{background:#002850;color:#fff;font-size:7px;font-weight:800;padding:5px 16px;border-radius:3px;text-transform:uppercase;letter-spacing:.8px}
/* ── Back link ─── */
.BACK{font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:1px;color:#AAA;margin-bottom:4px;display:flex;align-items:center;gap:3px}
.BACK-ARROW{font-size:9px}
.PAGE-TITLE{font-size:15px;font-weight:700;color:#002850;font-family:Georgia,serif;margin-bottom:10px}
/* ── Changelog / decision list ─── */
.CHANGES{background:#fff;border:1.5px solid #E0DDD6;border-radius:8px;padding:20px 24px;margin-bottom:40px}
.CHANGES h2{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#888;margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid #E8E4DF}
.CHANGES-GRID{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.C-COL h3{font-size:10px;font-weight:800;color:#444;margin-bottom:8px}
.C-COL ul{list-style:none;display:flex;flex-direction:column;gap:5px}
.C-COL ul li{font-size:11px;color:#555;padding-left:16px;position:relative;line-height:1.5}
.C-COL.new li::before{content:'✦';position:absolute;left:0;color:#002850;font-size:8px}
.C-COL.keep li::before{content:'→';position:absolute;left:0;color:#888}
/* ── Impl notes ─── */
.IMPL{background:#0D2240;border-radius:8px;padding:20px 24px;margin-top:48px}
.IMPL h2{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:rgba(255,255,255,.4);margin-bottom:16px;padding-bottom:10px;border-bottom:1px solid rgba(255,255,255,.08)}
.IMPL-GRID{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px}
.IMPL-COL h3{font-size:9.5px;font-weight:800;color:rgba(255,255,255,.6);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px}
.IMPL-COL ul{list-style:none;display:flex;flex-direction:column;gap:5px}
.IMPL-COL ul li{font-size:10.5px;color:rgba(255,255,255,.75);padding-left:14px;position:relative;line-height:1.5}
.IMPL-COL ul li::before{content:'';position:absolute;left:0;color:rgba(255,255,255,.3)}
.IMPL-COL code{font-family:monospace;font-size:9.5px;background:rgba(255,255,255,.08);padding:1px 4px;border-radius:3px;color:#A6DAD8}
/* ── 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}
/* ── Agent Implementation Reference (impl-ref) ─────────────────────────
Appears after every visual section. Mockup CSS values are at ~55% scale
and must NOT be used as implementation values. Use these tables instead.
──────────────────────────────────────────────────────────────────────── */
.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:190px}
.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}
/* ── Conditional fields hint ─── */
.COND{background:#EFF6FF;border:1px solid #93C5FD;border-radius:4px;padding:6px 10px;font-size:8px;color:#1E40AF;margin-top:6px;line-height:1.5}
.COND strong{font-weight:800}
/* ── 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:18px;background:#0D2240;display:flex;align-items:center;justify-content:space-between;padding:0 10px}
.WF-M-TIME{font-size:7px;color:#fff;font-weight:700}
.WF-M-ICONS{display:flex;gap:3px}
.WF-M-ICON{width:6px;height:6px;background:rgba(255,255,255,.5);border-radius:1px}
.N-M{height:40px;background:#0D2240;display:flex;align-items:center;padding:0 12px;justify-content:space-between}
</style>
</head>
<body>
<div class="doc">
<!-- ══════════════════════════════════
MASTHEAD
══════════════════════════════════ -->
<div class="mast">
<div class="mast-top">
<div>
<h1>Person Title &amp; Type Fields — Design Spec</h1>
<p>Surface the existing <code style="background:rgba(255,255,255,.12);padding:1px 4px;border-radius:3px;color:#A6DAD8;font-size:11px">title</code> and <code style="background:rgba(255,255,255,.12);padding:1px 4px;border-radius:3px;color:#A6DAD8;font-size:11px">personType</code> fields in the person edit/create forms, detail card, and list cards. Adds conditional field visibility based on entity type.</p>
</div>
<span class="mast-badge mb-final">Final &middot; Ready for implementation</span>
</div>
<div class="decisions">
<div class="dec">
<div class="dec-label">Title field</div>
<div class="dec-value"><s>Backend only</s><br>&rarr; Editable in forms</div>
</div>
<div class="dec">
<div class="dec-label">Type selector</div>
<div class="dec-value"><s>Import only</s><br>&rarr; Segmented control</div>
</div>
<div class="dec">
<div class="dec-label">SKIP value</div>
<div class="dec-value">Hidden from UI<br>(import-only type)</div>
</div>
<div class="dec">
<div class="dec-label">Conditional fields</div>
<div class="dec-value">Institution/Group hide<br>title, firstName, birth/death</div>
</div>
<div class="dec">
<div class="dec-label">Detail card</div>
<div class="dec-value">Title above name<br>small-caps label</div>
</div>
<div class="dec">
<div class="dec-label">List card</div>
<div class="dec-value">No change needed<br>displayName includes title</div>
</div>
<div class="dec">
<div class="dec-label">Mobile</div>
<div class="dec-value">Title + firstName share row<br>Type stacks vertically</div>
</div>
<div class="dec">
<div class="dec-label">Backend DTO</div>
<div class="dec-value">Add <code style="color:#A6DAD8;font-size:8px">personType</code><br>to PersonUpdateDTO</div>
</div>
</div>
</div>
<div class="spec-disclaimer">
<strong>&#x1F4D0; Mockup scale notice &mdash;</strong> all font-size, height, padding, and spacing values in the mockup CSS below are scaled to ~55% of their real implementation values so they fit on screen. <strong>Do not copy sizes from the mockup HTML/CSS.</strong> Each section ends with an <strong>&#x2699; Implementation Reference</strong> table listing the exact Tailwind classes and real pixel values to use in code.
</div>
<!-- ══════════════════════════════════
CHANGES
══════════════════════════════════ -->
<div class="CHANGES">
<h2>What changes vs. current implementation</h2>
<div class="CHANGES-GRID">
<div class="C-COL new">
<h3>New / changed</h3>
<ul>
<li>PersonEditForm: segmented type control added above name fields</li>
<li>PersonEditForm: <code>title</code> input (narrow column) added before firstName</li>
<li>PersonEditForm: conditional visibility &mdash; INSTITUTION/GROUP hide title, firstName, birthYear, deathYear; lastName relabeled to "Name"</li>
<li>PersonEditForm: UNKNOWN shows lastName + notes only</li>
<li>New person form: same type selector + title field + conditional logic</li>
<li>PersonCard (detail): title rendered above displayName as small-caps label</li>
<li>PersonUpdateDTO (backend): add <code>personType</code> field</li>
<li>+page.server.ts (edit &amp; new): read and submit <code>title</code> + <code>personType</code></li>
</ul>
</div>
<div class="C-COL keep">
<h3>Kept unchanged</h3>
<ul>
<li>PersonTypeBadge component &mdash; still used as-is in detail + list</li>
<li>List card layout &mdash; displayName already includes title via backend formatter</li>
<li>Avatar logic &mdash; type-based icons vs. initials unchanged</li>
<li>NameHistoryEditCard &mdash; no changes</li>
<li>PersonDangerZone &mdash; no changes</li>
<li>PersonEditSaveBar &mdash; no changes</li>
</ul>
</div>
</div>
</div>
<!-- ══════════════════════════════════
SECTION 1 — EDIT FORM: PERSON TYPE
══════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">1</span> Edit Form &mdash; Type Selector &amp; Title Field</div>
<div class="sg sg-2">
<!-- ── Desktop: PERSON type selected ── -->
<div class="sb">
<div class="sl">Person selected <span class="sz">max-w-2xl</span> <span class="state st-changed">changed</span></div>
<div class="wf">
<div class="wf-bar"><div class="dot r"></div><div class="dot y"></div><div class="dot g"></div><div class="urlbar"><span>/persons/abc-123/edit</span></div></div>
<div class="N"><span class="logo">FAMILIENARCHIV</span><span class="nl">Dokumente</span><span class="nl on">Personen</span><span class="nr"><div class="nico"></div></span></div>
<div class="MAIN">
<!-- Back link -->
<div class="BACK"><span class="BACK-ARROW">&larr;</span> Prof. Dr. Heinrich Raddatz</div>
<div class="PAGE-TITLE">Person bearbeiten</div>
<div class="CARD">
<div class="CARD-H">Details</div>
<!-- Type selector — segmented control -->
<div class="SEG">
<div class="SEG-BTN active">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
Person
</div>
<div class="SEG-BTN">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0H5m14 0h2m-2 0v-2M5 21H3m2 0v-2m4-12h2m-2 4h2m4-4h2m-2 4h2"/></svg>
Institution
</div>
<div class="SEG-BTN">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
Gruppe
</div>
<div class="SEG-BTN">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01"/></svg>
Unbekannt
</div>
</div>
<!-- Name row: title (narrow) + firstName + lastName -->
<div class="FORM-ROW">
<div class="FORM-COL narrow">
<div class="FL">Titel</div>
<div class="FI sm">Prof. Dr.</div>
</div>
<div class="FORM-COL">
<div class="FL">Vorname *</div>
<div class="FI">Heinrich</div>
</div>
<div class="FORM-COL">
<div class="FL">Nachname *</div>
<div class="FI">Raddatz</div>
</div>
</div>
<!-- Alias row -->
<div class="FORM-ROW">
<div class="FORM-COL">
<div class="FL">Alias</div>
<div class="FI placeholder">z.B. Spitzname, Geburtsname&hellip;</div>
</div>
</div>
<!-- Year row -->
<div class="FORM-ROW">
<div class="FORM-COL">
<div class="FL">Geburtsjahr</div>
<div class="FI">1878</div>
</div>
<div class="FORM-COL">
<div class="FL">Sterbejahr</div>
<div class="FI">1944</div>
</div>
</div>
<!-- Notes -->
<div class="FORM-ROW">
<div class="FORM-COL">
<div class="FL">Notizen</div>
<div class="FTA">Biografische Notizen&hellip;</div>
</div>
</div>
</div>
<div class="SAVE">
<span class="SAVE-DISCARD">Verwerfen</span>
<span class="SAVE-BTN">Speichern</span>
</div>
</div>
</div>
<div class="sc">PERSON type: all fields visible. Title field is a narrow input (max-w-[120px]) before firstName. Three columns on md+.</div>
</div>
<!-- ── Desktop: INSTITUTION type selected ── -->
<div class="sb">
<div class="sl">Institution selected <span class="sz">max-w-2xl</span> <span class="state st-new">new behavior</span></div>
<div class="wf">
<div class="wf-bar"><div class="dot r"></div><div class="dot y"></div><div class="dot g"></div><div class="urlbar"><span>/persons/def-456/edit</span></div></div>
<div class="N"><span class="logo">FAMILIENARCHIV</span><span class="nl">Dokumente</span><span class="nl on">Personen</span><span class="nr"><div class="nico"></div></span></div>
<div class="MAIN">
<div class="BACK"><span class="BACK-ARROW">&larr;</span> Reichsfechtschule Leipzig</div>
<div class="PAGE-TITLE">Person bearbeiten</div>
<div class="CARD">
<div class="CARD-H">Details</div>
<!-- Type selector — INSTITUTION active -->
<div class="SEG">
<div class="SEG-BTN">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
Person
</div>
<div class="SEG-BTN active">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0H5m14 0h2m-2 0v-2M5 21H3m2 0v-2m4-12h2m-2 4h2m4-4h2m-2 4h2"/></svg>
Institution
</div>
<div class="SEG-BTN">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
Gruppe
</div>
<div class="SEG-BTN">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01"/></svg>
Unbekannt
</div>
</div>
<!-- Name row: only "Name" (full width) — title + firstName hidden -->
<div class="FORM-ROW">
<div class="FORM-COL">
<div class="FL">Name *</div>
<div class="FI">Reichsfechtschule Leipzig</div>
</div>
</div>
<!-- Alias row -->
<div class="FORM-ROW">
<div class="FORM-COL">
<div class="FL">Alias</div>
<div class="FI placeholder">z.B. Kurzname, früherer Name&hellip;</div>
</div>
</div>
<!-- Notes (no birth/death year) -->
<div class="FORM-ROW">
<div class="FORM-COL">
<div class="FL">Notizen</div>
<div class="FTA">Historische Anmerkungen&hellip;</div>
</div>
</div>
<div class="COND">
<strong>Conditional fields:</strong> Title, Vorname, Geburtsjahr, and Sterbejahr are hidden when type is INSTITUTION or GROUP. The "Nachname" label changes to "Name".
</div>
</div>
<div class="SAVE">
<span class="SAVE-DISCARD">Verwerfen</span>
<span class="SAVE-BTN">Speichern</span>
</div>
</div>
</div>
<div class="sc">INSTITUTION type: title, firstName, birthYear, deathYear hidden. "Nachname" relabeled to "Name". Same layout for GROUP type.</div>
</div>
</div>
<!-- impl-ref -->
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference &mdash; Type Selector &amp; Form Fields
<span>Real values &middot; mockup above is ~55% scale</span>
</div>
<table>
<thead><tr><th>Element</th><th>Tailwind classes</th><th>Real size</th><th>Notes</th></tr></thead>
<tbody>
<tr>
<td>Type selector container</td>
<td><code>flex rounded-lg border border-line overflow-hidden mb-5</code></td>
<td><span class="ir-px">h ~44px per button</span></td>
<td>Use <code>role="radiogroup"</code>, each button <code>role="radio"</code> with <code>aria-checked</code>. Arrow key navigation. Hidden <code>&lt;input type="hidden" name="personType"&gt;</code> holds the value.</td>
</tr>
<tr>
<td>Type button (inactive)</td>
<td><code>flex-1 flex items-center justify-center gap-1.5 text-sm font-bold text-ink-3 bg-muted border-r border-line cursor-pointer py-2.5</code></td>
<td><span class="ir-px">14px / 700, py 10px</span></td>
<td>Last button: no <code>border-r</code>. Hover: <code>bg-surface</code>.</td>
</tr>
<tr>
<td>Type button (active)</td>
<td><code>flex-1 flex items-center justify-center gap-1.5 text-sm font-bold text-primary-fg bg-primary py-2.5</code></td>
<td><span class="ir-px">14px / 700</span></td>
<td>Active state replaces bg + text color. Icon inherits <code>currentColor</code>.</td>
</tr>
<tr>
<td>Type button icon</td>
<td><code>w-4 h-4</code></td>
<td><span class="ir-px">16px</span></td>
<td>Same SVG paths as PersonTypeBadge + person silhouette for PERSON.</td>
</tr>
<tr>
<td>Title input</td>
<td><code>block w-full max-w-[120px] rounded border border-line px-3 py-2 font-serif text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring</code></td>
<td><span class="ir-px">max-w 120px, py 8px</span></td>
<td><code>aria-label</code> = i18n key for "Akademischer Titel". Only visible when type = PERSON.</td>
</tr>
<tr>
<td>Title label</td>
<td><code>mb-1 block text-xs font-bold tracking-widest text-ink-3 uppercase</code></td>
<td><span class="ir-px">12px / 700</span></td>
<td>Matches existing label pattern. Label text: paraglide key <code>form_label_title</code>.</td>
</tr>
<tr>
<td>Name row (PERSON)</td>
<td><code>grid grid-cols-[120px_1fr_1fr] gap-4 md:grid-cols-[120px_1fr_1fr]</code></td>
<td><span class="ir-px">gap 16px</span></td>
<td>Mobile (&lt;md): <code>grid-cols-[80px_1fr]</code> — title + firstName share row, lastName goes below full-width.</td>
</tr>
<tr>
<td>Name row (INSTITUTION/GROUP)</td>
<td><code>grid grid-cols-1 gap-4</code></td>
<td><span class="ir-px">full width</span></td>
<td>Single "Name" field using the <code>lastName</code> input. Label changes via conditional: <code>form_label_name</code> i18n key.</td>
</tr>
<tr>
<td>Conditional visibility</td>
<td colspan="2">Svelte <code>{#if selectedType === 'PERSON'}</code></td>
<td>Hide: title, firstName, birthYear, deathYear for INSTITUTION/GROUP. Hide: title, firstName, birthYear, deathYear, alias for UNKNOWN. Use <code>$state</code> for reactive type.</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ══════════════════════════════════
SECTION 2 — NEW PERSON FORM
══════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">2</span> New Person Form</div>
<div class="sg sg-2">
<!-- ── Desktop: default (PERSON) ── -->
<div class="sb">
<div class="sl">Default state &mdash; Person <span class="sz">max-w-2xl</span> <span class="state st-changed">changed</span></div>
<div class="wf">
<div class="wf-bar"><div class="dot r"></div><div class="dot y"></div><div class="dot g"></div><div class="urlbar"><span>/persons/new</span></div></div>
<div class="N"><span class="logo">FAMILIENARCHIV</span><span class="nl">Dokumente</span><span class="nl on">Personen</span><span class="nr"><div class="nico"></div></span></div>
<div class="MAIN">
<div class="BACK"><span class="BACK-ARROW">&larr;</span> Zurück zur Übersicht</div>
<div class="PAGE-TITLE">Neue Person</div>
<div class="CARD">
<div class="CARD-H">Details</div>
<!-- Type selector — PERSON active (default) -->
<div class="SEG">
<div class="SEG-BTN active">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
Person
</div>
<div class="SEG-BTN">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0H5m14 0h2m-2 0v-2M5 21H3m2 0v-2m4-12h2m-2 4h2m4-4h2m-2 4h2"/></svg>
Institution
</div>
<div class="SEG-BTN">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
Gruppe
</div>
<div class="SEG-BTN">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01"/></svg>
Unbekannt
</div>
</div>
<!-- Name row with title -->
<div class="FORM-ROW">
<div class="FORM-COL narrow">
<div class="FL">Titel</div>
<div class="FI sm placeholder">Dr.</div>
</div>
<div class="FORM-COL">
<div class="FL">Vorname *</div>
<div class="FI placeholder">Vorname</div>
</div>
<div class="FORM-COL">
<div class="FL">Nachname *</div>
<div class="FI placeholder">Nachname</div>
</div>
</div>
<div class="FORM-ROW">
<div class="FORM-COL">
<div class="FL">Alias</div>
<div class="FI placeholder">z.B. Spitzname, Geburtsname&hellip;</div>
</div>
</div>
<div class="FORM-ROW">
<div class="FORM-COL">
<div class="FL">Geburtsjahr</div>
<div class="FI placeholder">z.B. 1920</div>
</div>
<div class="FORM-COL">
<div class="FL">Sterbejahr</div>
<div class="FI placeholder">z.B. 1995</div>
</div>
</div>
<div class="FORM-ROW">
<div class="FORM-COL">
<div class="FL">Notizen</div>
<div class="FTA">Biografische Notizen&hellip;</div>
</div>
</div>
</div>
<div class="SAVE">
<span class="SAVE-DISCARD">Abbrechen</span>
<span class="SAVE-BTN">Erstellen</span>
</div>
</div>
</div>
<div class="sc">New person form defaults to PERSON type. Same segmented control + title field as edit form. All fields empty with placeholders.</div>
</div>
<!-- ── Mobile: PERSON type ── -->
<div class="sb">
<div class="sl">Mobile &mdash; Person <span class="sz">375px</span> <span class="state st-new">responsive</span></div>
<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="N-M"><span class="logo" style="font-size:7px">FAMILIENARCHIV</span><div class="nico" style="width:16px;height:16px"></div></div>
<div style="padding:10px 12px;background:#ECEAE4">
<div class="BACK" style="font-size:6px;margin-bottom:3px"><span class="BACK-ARROW">&larr;</span> Zurück</div>
<div style="font-size:11px;font-weight:700;color:#002850;font-family:Georgia,serif;margin-bottom:8px">Neue Person</div>
<div style="background:#fff;border:1px solid #E0DDD6;border-radius:4px;padding:10px 12px">
<div style="font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#AAA;margin-bottom:6px">Details</div>
<!-- Type selector — stacked 2x2 on mobile -->
<div style="display:grid;grid-template-columns:1fr 1fr;border:1px solid #D1D5DB;border-radius:3px;overflow:hidden;margin-bottom:8px">
<div style="text-align:center;font-size:6.5px;font-weight:700;padding:5px 2px;background:#002850;color:#fff;border-right:1px solid #D1D5DB;border-bottom:1px solid #D1D5DB">Person</div>
<div style="text-align:center;font-size:6.5px;font-weight:700;padding:5px 2px;color:#888;background:#F7F5F2;border-bottom:1px solid #D1D5DB">Institution</div>
<div style="text-align:center;font-size:6.5px;font-weight:700;padding:5px 2px;color:#888;background:#F7F5F2;border-right:1px solid #D1D5DB">Gruppe</div>
<div style="text-align:center;font-size:6.5px;font-weight:700;padding:5px 2px;color:#888;background:#F7F5F2">Unbekannt</div>
</div>
<!-- Title + firstName share a row -->
<div style="display:flex;gap:4px;margin-bottom:4px">
<div style="flex:0 0 50px">
<div style="font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;color:#888;margin-bottom:2px">Titel</div>
<div class="FI" style="height:20px;font-size:7px;padding:0 4px;color:#C8C4BE;font-style:italic">Dr.</div>
</div>
<div style="flex:1">
<div style="font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;color:#888;margin-bottom:2px">Vorname *</div>
<div class="FI" style="height:20px;font-size:7px;padding:0 4px"></div>
</div>
</div>
<div style="margin-bottom:4px">
<div style="font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;color:#888;margin-bottom:2px">Nachname *</div>
<div class="FI" style="height:20px;font-size:7px;padding:0 4px"></div>
</div>
<div style="margin-bottom:4px">
<div style="font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;color:#888;margin-bottom:2px">Alias</div>
<div class="FI" style="height:20px;font-size:7px;padding:0 4px"></div>
</div>
<div style="display:flex;gap:4px;margin-bottom:4px">
<div style="flex:1">
<div style="font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;color:#888;margin-bottom:2px">Geburtsjahr</div>
<div class="FI" style="height:20px;font-size:7px;padding:0 4px"></div>
</div>
<div style="flex:1">
<div style="font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;color:#888;margin-bottom:2px">Sterbejahr</div>
<div class="FI" style="height:20px;font-size:7px;padding:0 4px"></div>
</div>
</div>
<div>
<div style="font-size:6px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;color:#888;margin-bottom:2px">Notizen</div>
<div style="height:32px;border:1px solid #D1D5DB;border-radius:3px;background:#fff"></div>
</div>
</div>
<div style="margin-top:6px;border:1px solid #E0DDD6;border-radius:4px;background:#fff;padding:6px 12px;display:flex;align-items:center;justify-content:space-between">
<span style="font-size:7px;color:#888">Abbrechen</span>
<span style="background:#002850;color:#fff;font-size:6px;font-weight:800;padding:4px 12px;border-radius:3px;text-transform:uppercase;letter-spacing:.6px">Erstellen</span>
</div>
</div>
</div>
<div class="sc">Mobile: type selector wraps to 2&times;2 grid. Title + firstName share a row (title ~80px, firstName fills rest). lastName goes full-width below.</div>
</div>
</div>
<!-- impl-ref -->
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference &mdash; New Person Form
<span>Real values &middot; mockup above is ~55% scale</span>
</div>
<table>
<thead><tr><th>Element</th><th>Tailwind classes</th><th>Real size</th><th>Notes</th></tr></thead>
<tbody>
<tr>
<td>Type selector (mobile)</td>
<td><code>grid grid-cols-2 rounded-lg border border-line overflow-hidden mb-5</code></td>
<td><span class="ir-px">min-h-[44px] per button</span></td>
<td>Below <code>md</code> breakpoint: 2&times;2 grid. Above <code>md</code>: single row (flex). Same <code>role="radiogroup"</code> pattern.</td>
</tr>
<tr>
<td>Title + firstName row (mobile)</td>
<td><code>flex gap-4</code></td>
<td><span class="ir-px">title w-[80px], firstName flex-1</span></td>
<td>Below <code>md</code>: title shrinks to 80px. Above <code>md</code>: title gets 120px in the 3-col grid.</td>
</tr>
<tr>
<td>Default personType</td>
<td colspan="2"><code>let selectedType = $state(person?.personType ?? 'PERSON')</code></td>
<td>New person defaults to PERSON. Edit form uses existing value.</td>
</tr>
<tr>
<td>Hidden input</td>
<td><code>&lt;input type="hidden" name="personType" value={selectedType}&gt;</code></td>
<td>&mdash;</td>
<td>Submits type with the form. Read in +page.server.ts via <code>formData.get('personType')</code>.</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ══════════════════════════════════
SECTION 3 — PERSON CARD (DETAIL VIEW)
══════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">3</span> Person Detail Card &mdash; Title Display</div>
<div class="sg sg-4">
<!-- ── Person with title ── -->
<div class="sb">
<div class="sl">Person with title <span class="state st-changed">changed</span></div>
<div class="PC">
<div class="PC-BAR"></div>
<div class="PC-BODY">
<div class="AV AV-lg AV-navy">HR</div>
<div class="PC-TITLE">Prof. Dr.</div>
<div class="PC-NAME">Heinrich Raddatz</div>
<div class="PC-ALIAS">&bdquo;Der Professor&ldquo;</div>
<div class="PC-DATES">1878 &ndash; 1944</div>
<div class="PC-NOTES">
<div class="PC-NOTES-LABEL">Notizen</div>
Professor der Germanistik in Leipzig.
</div>
<div class="PC-BTN">&#x270E; Bearbeiten</div>
</div>
</div>
<div class="sc">Title shown as small-caps label above the serif display name. Color: text-ink-3.</div>
</div>
<!-- ── Person without title ── -->
<div class="sb">
<div class="sl">Person, no title <span class="state st-changed">unchanged look</span></div>
<div class="PC">
<div class="PC-BAR"></div>
<div class="PC-BODY">
<div class="AV AV-lg AV-navy">MR</div>
<div class="PC-NAME">Martha Raddatz</div>
<div class="PC-DATES">1882 &ndash; 1961</div>
<div class="PC-BTN">&#x270E; Bearbeiten</div>
</div>
</div>
<div class="sc">No title field &rarr; no label rendered. Layout collapses naturally.</div>
</div>
<!-- ── Institution ── -->
<div class="sb">
<div class="sl">Institution <span class="state st-changed">unchanged look</span></div>
<div class="PC">
<div class="PC-BAR"></div>
<div class="PC-BODY">
<div class="AV AV-lg AV-navy" style="font-family:sans-serif;font-size:14px;font-weight:400">&#x1F3DB;</div>
<div class="PC-NAME">Reichsfechtschule Leipzig</div>
<div class="PC-BADGE inst">
<svg style="width:8px;height:8px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0H5m14 0h2m-2 0v-2M5 21H3m2 0v-2m4-12h2m-2 4h2m4-4h2m-2 4h2"/></svg>
Institution
</div>
<div class="PC-BTN">&#x270E; Bearbeiten</div>
</div>
</div>
<div class="sc">Institutions never show a title line. PersonTypeBadge already renders.</div>
</div>
<!-- ── Group ── -->
<div class="sb">
<div class="sl">Group <span class="state st-changed">unchanged look</span></div>
<div class="PC">
<div class="PC-BAR"></div>
<div class="PC-BODY">
<div class="AV AV-lg AV-navy" style="font-family:sans-serif;font-size:14px;font-weight:400">&#x1F465;</div>
<div class="PC-NAME">Familie Müller</div>
<div class="PC-BADGE" style="background:#f0e8f5;color:#5a2d6f;border:1px solid #d8c5e3">
<svg style="width:8px;height:8px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
Gruppe
</div>
<div class="PC-BTN">&#x270E; Bearbeiten</div>
</div>
</div>
<div class="sc">Groups never show a title line either. Badge + icon avatar as before.</div>
</div>
</div>
<!-- impl-ref -->
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference &mdash; PersonCard Title Display
<span>Real values &middot; mockup above is ~55% scale</span>
</div>
<table>
<thead><tr><th>Element</th><th>Tailwind classes</th><th>Real size</th><th>Notes</th></tr></thead>
<tbody>
<tr>
<td>Title label</td>
<td><code>text-xs font-bold uppercase tracking-widest text-ink-3 text-center</code></td>
<td><span class="ir-px">12px / 700</span></td>
<td>Only render when <code>person.title</code> is truthy AND <code>personType === 'PERSON'</code>. Placed between avatar and displayName.</td>
</tr>
<tr>
<td>Title spacing</td>
<td><code>mb-0.5</code></td>
<td><span class="ir-px">2px</span></td>
<td>Tight spacing to displayName below. When absent, avatar's mb-4 flows directly to name.</td>
</tr>
<tr>
<td>PersonCard prop</td>
<td colspan="2"><code>title?: string | null</code> added to the person type</td>
<td>Already in PersonSummaryDTO. Pass from +page.server.ts load.</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ══════════════════════════════════
SECTION 4 — CONDITIONAL FIELD MATRIX
══════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">4</span> Conditional Field Visibility Matrix</div>
<div style="background:#fff;border:1.5px solid #E0DDD6;border-radius:8px;overflow:hidden;margin-bottom:20px">
<table style="width:100%;border-collapse:collapse;font-size:11px">
<thead>
<tr style="background:#F0EDE6">
<th style="text-align:left;padding:10px 16px;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#888;border-bottom:2px solid #E0DDD6">Field</th>
<th style="text-align:center;padding:10px 16px;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#002850;border-bottom:2px solid #E0DDD6">Person</th>
<th style="text-align:center;padding:10px 16px;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#1a4971;border-bottom:2px solid #E0DDD6">Institution</th>
<th style="text-align:center;padding:10px 16px;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#5a2d6f;border-bottom:2px solid #E0DDD6">Group</th>
<th style="text-align:center;padding:10px 16px;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#7a5a0a;border-bottom:2px solid #E0DDD6">Unknown</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom:1px solid #F0EDE6">
<td style="padding:8px 16px;font-weight:700;color:#333">Title</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; visible</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
</tr>
<tr style="border-bottom:1px solid #F0EDE6;background:#FAFAF8">
<td style="padding:8px 16px;font-weight:700;color:#333">firstName</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; "Vorname"</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
</tr>
<tr style="border-bottom:1px solid #F0EDE6">
<td style="padding:8px 16px;font-weight:700;color:#333">lastName</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; "Nachname"</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; "Name"</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; "Gruppenname"</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; "Name"</td>
</tr>
<tr style="border-bottom:1px solid #F0EDE6;background:#FAFAF8">
<td style="padding:8px 16px;font-weight:700;color:#333">alias</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; visible</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; visible</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; visible</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
</tr>
<tr style="border-bottom:1px solid #F0EDE6">
<td style="padding:8px 16px;font-weight:700;color:#333">birthYear</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; "Geburtsjahr"</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
</tr>
<tr style="border-bottom:1px solid #F0EDE6;background:#FAFAF8">
<td style="padding:8px 16px;font-weight:700;color:#333">deathYear</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; "Sterbejahr"</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
<td style="text-align:center;padding:8px 16px;color:#DC2626">hidden</td>
</tr>
<tr>
<td style="padding:8px 16px;font-weight:700;color:#333">notes</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; visible</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; visible</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; visible</td>
<td style="text-align:center;padding:8px 16px;color:#166534;font-weight:700">&#x2713; visible</td>
</tr>
</tbody>
</table>
</div>
<div class="ann-block">
<strong>Key behavioral note:</strong> When switching type in the segmented control, hidden fields are not cleared — their values are preserved in case the user switches back. The backend already handles null/empty values gracefully. Only the <code>personType</code> value changes on submission.
</div>
<!-- impl-ref -->
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference &mdash; Conditional Field Logic
<span>Real values &middot; Svelte reactivity</span>
</div>
<table>
<thead><tr><th>Element</th><th>Tailwind classes</th><th>Real size</th><th>Notes</th></tr></thead>
<tbody>
<tr>
<td>Type state</td>
<td><code>let selectedType = $state&lt;PersonType&gt;('PERSON')</code></td>
<td>&mdash;</td>
<td>Reactive state drives all conditionals. Type imported from generated API types.</td>
</tr>
<tr>
<td>showPersonFields</td>
<td><code>const showPersonFields = $derived(selectedType === 'PERSON')</code></td>
<td>&mdash;</td>
<td>Controls title, firstName, birthYear, deathYear visibility.</td>
</tr>
<tr>
<td>showAlias</td>
<td><code>const showAlias = $derived(selectedType !== 'UNKNOWN')</code></td>
<td>&mdash;</td>
<td>UNKNOWN hides alias (too little info to have one).</td>
</tr>
<tr>
<td>lastNameLabel</td>
<td><code>const lastNameLabel = $derived.by(() =&gt; { switch... })</code></td>
<td>&mdash;</td>
<td>PERSON: <code>form_label_last_name</code>, INSTITUTION: <code>form_label_name</code>, GROUP: <code>form_label_group_name</code>, UNKNOWN: <code>form_label_name</code>. Needs 2 new i18n keys.</td>
</tr>
<tr>
<td>firstName required</td>
<td colspan="2"><code>required</code> attribute only when <code>selectedType === 'PERSON'</code></td>
<td>Remove <code>required</code> on firstName when hidden. lastName stays required always.</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ══════════════════════════════════
SECTION 5 — ACCESSIBILITY
══════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">5</span> Accessibility Requirements</div>
<div style="background:#fff;border:1.5px solid #E0DDD6;border-radius:8px;overflow:hidden">
<table style="width:100%;border-collapse:collapse;font-size:11px">
<thead>
<tr style="background:#F0EDE6">
<th style="text-align:left;padding:10px 16px;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#888;border-bottom:2px solid #E0DDD6;width:200px">Requirement</th>
<th style="text-align:left;padding:10px 16px;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#888;border-bottom:2px solid #E0DDD6">WCAG Criterion</th>
<th style="text-align:left;padding:10px 16px;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#888;border-bottom:2px solid #E0DDD6">Implementation</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom:1px solid #F0EDE6">
<td style="padding:8px 16px;font-weight:700;color:#333">Segmented control semantics</td>
<td style="padding:8px 16px;color:#555">4.1.2 Name, Role, Value</td>
<td style="padding:8px 16px;color:#555"><code style="font-size:10px">role="radiogroup"</code> on container, <code style="font-size:10px">role="radio"</code> + <code style="font-size:10px">aria-checked</code> on each button. <code style="font-size:10px">aria-label="Typ der Person"</code> on container.</td>
</tr>
<tr style="border-bottom:1px solid #F0EDE6;background:#FAFAF8">
<td style="padding:8px 16px;font-weight:700;color:#333">Keyboard navigation</td>
<td style="padding:8px 16px;color:#555">2.1.1 Keyboard</td>
<td style="padding:8px 16px;color:#555">Arrow keys cycle through options. Tab moves out of the group. Space/Enter selects.</td>
</tr>
<tr style="border-bottom:1px solid #F0EDE6">
<td style="padding:8px 16px;font-weight:700;color:#333">Touch target size</td>
<td style="padding:8px 16px;color:#555">2.5.8 Target Size (Minimum)</td>
<td style="padding:8px 16px;color:#555">Each segment button: <code style="font-size:10px">min-h-[44px]</code>. Mobile 2&times;2 grid: same minimum per cell.</td>
</tr>
<tr style="border-bottom:1px solid #F0EDE6;background:#FAFAF8">
<td style="padding:8px 16px;font-weight:700;color:#333">Title input label</td>
<td style="padding:8px 16px;color:#555">1.3.1 Info and Relationships</td>
<td style="padding:8px 16px;color:#555">Visible <code style="font-size:10px">&lt;label for="title"&gt;</code>. Also <code style="font-size:10px">aria-describedby</code> linking to help text if placeholder alone is ambiguous.</td>
</tr>
<tr style="border-bottom:1px solid #F0EDE6">
<td style="padding:8px 16px;font-weight:700;color:#333">Hidden field announcement</td>
<td style="padding:8px 16px;color:#555">4.1.2 Name, Role, Value</td>
<td style="padding:8px 16px;color:#555">When type changes, use <code style="font-size:10px">aria-live="polite"</code> region to announce which fields are now visible. E.g., "Felder für Person angezeigt".</td>
</tr>
<tr>
<td style="padding:8px 16px;font-weight:700;color:#333">Focus management</td>
<td style="padding:8px 16px;color:#555">2.4.3 Focus Order</td>
<td style="padding:8px 16px;color:#555">Tab order: type selector &rarr; title (if visible) &rarr; firstName (if visible) &rarr; lastName &rarr; alias (if visible) &rarr; birthYear (if visible) &rarr; deathYear (if visible) &rarr; notes.</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ══════════════════════════════════
IMPLEMENTATION NOTES
══════════════════════════════════ -->
<div class="IMPL">
<h2>Implementation Notes</h2>
<div class="IMPL-GRID">
<div class="IMPL-COL">
<h3>Backend</h3>
<ul>
<li>Add <code>personType</code> field to <code>PersonUpdateDTO</code> with <code>@NotNull</code> validation</li>
<li><code>PersonService.update()</code>: set <code>person.setPersonType(dto.getPersonType())</code></li>
<li><code>PersonService.create()</code>: same &mdash; accept type from DTO</li>
<li>Regenerate OpenAPI spec + frontend types after DTO change</li>
<li><code>SKIP</code> value: exclude from API validation (backend-only, used by import)</li>
</ul>
</div>
<div class="IMPL-COL">
<h3>Frontend</h3>
<ul>
<li>Refactor <code>PersonEditForm.svelte</code>: add <code>selectedType</code> state + segmented control</li>
<li>Add <code>title</code> input to name row (narrow, max-w-[120px])</li>
<li>Wrap conditional fields in <code>{#if showPersonFields}</code></li>
<li>Update <code>PersonCard.svelte</code> prop type: add <code>title</code></li>
<li>Update both <code>+page.server.ts</code> files: read/submit <code>title</code> + <code>personType</code></li>
<li>New person form: inline the same type selector + title + conditionals</li>
</ul>
</div>
<div class="IMPL-COL">
<h3>i18n Keys (new)</h3>
<ul>
<li><code>form_label_title</code> &mdash; "Titel" / "Title" / "T&iacute;tulo"</li>
<li><code>form_label_name</code> &mdash; "Name" / "Name" / "Nombre"</li>
<li><code>form_label_group_name</code> &mdash; "Gruppenname" / "Group name" / "Nombre del grupo"</li>
<li><code>form_label_person_type</code> &mdash; "Typ" / "Type" / "Tipo"</li>
<li><code>person_type_PERSON</code> &mdash; "Person" / "Person" / "Persona"</li>
<li><code>a11y_type_fields_visible</code> &mdash; "Felder f&uuml;r {type} angezeigt"</li>
</ul>
</div>
</div>
</div>
</div><!-- .doc -->
</body>
</html>