Files
wannsee-kram/docs/superpowers/specs/2026-05-05-erbstuecke-wannsee-views.html
Marcel Raddatz 92c3d686c5 Add design specs and personas
Feature spec, system design, design system (colors/typography/components),
and per-view HTML specs for Erbstücke Wannsee. Also includes Claude personas
used during design sessions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 10:45:07 +02:00

705 lines
56 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="de">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Erbstücke Wannsee — View Specs</title>
<link href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"/>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Inter',system-ui,sans-serif;background:#EAE5DC;color:#1C2820;line-height:1.5;font-size:13px;padding:48px 32px 120px}
.doc{max-width:1200px;margin:0 auto}
/* ── Masthead ── */
.mh{padding-bottom:24px;border-bottom:3px solid #5B7A66;margin-bottom:56px}
.mh .kicker{font-size:9px;font-weight:800;letter-spacing:2px;text-transform:uppercase;color:#C4874A}
.mh h1{font-family:'Lora',Georgia,serif;font-size:30px;font-weight:700;color:#1C2820;letter-spacing:-.4px;margin-top:7px}
.mh p{font-size:13.5px;color:#6B6050;max-width:820px;line-height:1.8;margin-top:12px}
.byline{font-size:9px;color:#999;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;margin-top:16px}
.tag-row{display:flex;gap:6px;margin-top:10px;flex-wrap:wrap}
.tag{background:#5B7A66;color:#fff;padding:3px 9px;border-radius:2px;font-size:8.5px;font-weight:800;letter-spacing:.8px;text-transform:uppercase}
.tag.amber{background:#C4874A}.tag.outline{background:transparent;color:#5B7A66;border:1px solid #5B7A66}.tag.gray{background:#6B6050;color:#fff}
/* ── Section ── */
.section{margin-bottom:80px}
.section+.section{border-top:1px dashed #C8C0B4;padding-top:72px}
.section-kicker{font-size:9px;font-weight:800;letter-spacing:2px;text-transform:uppercase;color:#C4874A;display:block;margin-bottom:6px}
.section h2{font-family:'Lora',Georgia,serif;font-size:24px;font-weight:700;color:#1C2820;margin-bottom:10px}
.section-desc{font-size:13px;color:#6B6050;line-height:1.75;max-width:780px;margin-bottom:28px}
/* ── Viewport grid ── */
.vp-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:28px;align-items:start;margin-bottom:32px}
.vp-label{font-size:9px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase;color:#888;margin-bottom:8px}
/* ── Screen frame ── */
.frame{border-radius:10px;overflow:hidden;box-shadow:0 4px 20px rgba(0,0,0,.12);border:1.5px solid #C8C0B4}
.bar{height:20px;background:#C8C0B4;display:flex;align-items:center;padding:0 8px;gap:4px}
.bar .d{width:6px;height:6px;border-radius:50%;background:rgba(0,0,0,.2)}
/* ── App tokens ── */
:root{
--cv:#F2EDE4;--sf:#fff;--ln:#E0D8CC;
--pr:#5B7A66;--pd:#4A6855;--ac:#C4874A;
--ink:#1C2820;--im:#6B6050;
--fr:#4A7C5C;--tk:#9B6060;
--ab:#2A3B30;
}
/* ── App chrome ── */
.app-nav{height:44px;background:var(--pr);display:flex;align-items:center;padding:0 14px;flex-shrink:0}
.app-logo{font-family:'Lora',Georgia,serif;font-size:13px;font-weight:700;color:#fff}
.app-user{margin-left:auto;font-size:10px;color:rgba(255,255,255,.75);font-weight:600}
.filter-bar{background:var(--cv);border-bottom:1px solid var(--ln);padding:8px 10px;display:flex;gap:5px;overflow-x:auto;flex-shrink:0}
.pill{background:transparent;color:var(--im);border:1.5px solid var(--ln);font-family:'Inter',sans-serif;font-size:10px;font-weight:700;padding:4px 10px;border-radius:20px;min-height:30px;white-space:nowrap;cursor:pointer}
.pill.on{background:var(--pr);color:#fff;border-color:var(--pr)}
.screen-body{background:var(--cv);padding:10px}
/* ── Gallery cards ── */
.gcard{background:var(--sf);border:1px solid var(--ln);border-radius:8px;overflow:hidden;display:flex;margin-bottom:8px}
.gcard-img{width:72px;height:72px;background:#C4B8A8;flex-shrink:0}
.gcard-body{padding:10px 12px;display:flex;flex-direction:column;justify-content:center;gap:2px}
.gcard-cat{font-size:9px;color:var(--im);font-weight:800;text-transform:uppercase;letter-spacing:.6px}
.gcard-title{font-family:'Lora',Georgia,serif;font-size:12px;color:var(--ink);font-weight:600;line-height:1.3}
.gcard-sq{background:var(--sf);border:1px solid var(--ln);border-radius:8px;overflow:hidden}
.gcard-sq-img{background:#C4B8A8;aspect-ratio:1}
.gcard-sq-body{padding:7px 9px}
/* ── Status ── */
.sf{font-size:10px;font-weight:700;color:var(--fr)}
.st{font-size:10px;font-weight:700;color:var(--tk)}
.sm{font-size:9px;font-weight:700;background:#E8F5EC;color:#2E6645;padding:2px 8px;border-radius:10px;border:1px solid #A8D5B8;display:inline-block;margin-top:2px}
/* ── Buttons ── */
.btn{display:inline-flex;align-items:center;justify-content:center;font-family:'Inter',sans-serif;font-weight:700;border-radius:6px;cursor:pointer;border:none;min-height:44px;padding:0 20px;font-size:13px}
.btn-prim{background:var(--pr);color:#fff;width:100%}
.btn-danger{background:#FBF0F0;border:1.5px solid var(--tk);color:var(--tk);padding:0 19px;width:100%}
.btn-dis{background:var(--cv);border:1.5px solid var(--ln);color:#AAA;cursor:not-allowed;width:100%}
/* ── Gate Screen ── */
.gate-body{background:var(--cv);min-height:380px;display:flex;align-items:center;justify-content:center;padding:24px}
.gate-card{background:var(--sf);border:1px solid var(--ln);border-radius:12px;padding:28px 20px;width:100%;max-width:300px;text-align:center}
.gate-icon{width:52px;height:52px;border-radius:50%;background:#DFF0E6;display:flex;align-items:center;justify-content:center;margin:0 auto 14px;font-size:24px}
.gate-title{font-family:'Lora',Georgia,serif;font-size:18px;font-weight:700;color:var(--ink);margin-bottom:6px}
.gate-sub{font-size:12px;color:var(--im);line-height:1.6;margin-bottom:18px}
.gate-input{width:100%;height:48px;border:1.5px solid var(--ln);border-radius:8px;background:#FAFAF7;font-family:monospace;font-size:16px;font-weight:600;text-align:center;letter-spacing:4px;color:#AAA;margin-bottom:10px;display:flex;align-items:center;justify-content:center}
.gate-hint{font-size:10px;color:#AAA;line-height:1.5;margin-top:12px}
/* ── Modal ── */
.modal-overlay{background:rgba(0,0,0,.45);display:flex;align-items:flex-end}
.modal-sheet{background:var(--sf);border-radius:12px 12px 0 0;width:100%}
.modal-handle{width:36px;height:4px;background:var(--ln);border-radius:2px;margin:10px auto 14px}
.modal-gallery{background:#C4B8A8;aspect-ratio:4/3;position:relative}
.modal-counter{position:absolute;bottom:8px;right:10px;background:rgba(0,0,0,.45);color:#fff;font-size:9px;font-weight:700;padding:2px 7px;border-radius:10px}
.modal-body{padding:14px}
.modal-cat{display:inline-block;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;background:#DFF0E6;color:#2E6645;padding:2px 9px;border-radius:10px;border:1px solid #A8D5B8;margin-bottom:6px}
.modal-title{font-family:'Lora',Georgia,serif;font-size:17px;font-weight:700;color:var(--ink);line-height:1.3;margin-bottom:5px}
.modal-note{font-size:12px;color:var(--im);font-style:italic;line-height:1.5;margin-bottom:12px}
.modal-div{height:1px;background:var(--ln);margin-bottom:12px}
.modal-status{display:flex;flex-direction:column;gap:7px}
/* ── Admin ── */
.admin-layout{display:flex;min-height:420px}
.admin-sidebar{width:155px;background:var(--ab);flex-shrink:0;display:flex;flex-direction:column;padding:12px 0}
.admin-logo{font-family:'Lora',Georgia,serif;font-size:10px;font-weight:700;color:rgba(255,255,255,.9);padding:0 12px 12px;border-bottom:1px solid rgba(255,255,255,.1);margin-bottom:6px}
.admin-logo span{display:block;font-family:'Inter',sans-serif;font-size:7px;font-weight:600;color:rgba(255,255,255,.4);letter-spacing:.5px;text-transform:uppercase;margin-bottom:1px}
.slink{display:flex;align-items:center;gap:7px;padding:7px 12px;font-size:10px;font-weight:600;color:rgba(255,255,255,.45);border-left:3px solid transparent}
.slink.on{background:rgba(255,255,255,.1);color:#fff;border-left-color:var(--ac)}
.admin-content{flex:1;background:var(--cv);padding:14px;overflow:auto}
.page-title{font-family:'Lora',Georgia,serif;font-size:14px;font-weight:700;color:var(--ink);margin-bottom:12px;display:flex;align-items:center;gap:8px}
.back-btn{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--im);border:1px solid var(--ln);border-radius:4px;padding:2px 7px}
/* ── Admin table ── */
.atable{width:100%;border-collapse:collapse;background:var(--sf);border-radius:6px;overflow:hidden;border:1px solid var(--ln);font-size:11px}
.atable th{text-align:left;padding:7px 9px;font-size:8.5px;font-weight:800;color:#888;text-transform:uppercase;letter-spacing:.5px;border-bottom:2px solid var(--ln);background:#F7F4EF}
.atable td{padding:7px 9px;border-bottom:1px solid var(--ln);vertical-align:middle}
.atable tr:last-child td{border-bottom:none}
.thumb-sm{width:30px;height:30px;border-radius:4px;background:#C4B8A8}
.code-mono{font-family:monospace;font-size:11px;font-weight:600;color:var(--pr);letter-spacing:2px}
.act-btn{font-size:9px;font-weight:700;border-radius:4px;padding:3px 7px;cursor:pointer;border:none}
.act-edit{color:var(--im);background:var(--cv);border:1px solid var(--ln)}
.act-del{color:var(--tk);background:#FBF0F0;border:1px solid #E0C0C0}
.act-link{color:var(--pr);background:#DFF0E6;border:1px solid #A8D5B8}
/* ── Dashboard stats ── */
.stat-row{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin-bottom:12px}
.stat-card{background:var(--sf);border:1px solid var(--ln);border-radius:8px;padding:9px;text-align:center}
.stat-n{font-family:'Lora',Georgia,serif;font-size:20px;font-weight:700;color:var(--pr)}
.stat-l{font-size:8.5px;font-weight:700;color:var(--im);text-transform:uppercase;letter-spacing:.4px;margin-top:1px}
/* ── Reservations ── */
.res-head{background:var(--ab);color:#fff;padding:6px 9px;font-size:9.5px;font-weight:700;border-radius:4px 4px 0 0;margin-top:8px}
.res-head:first-child{margin-top:0}
.res-body{background:var(--sf);border:1px solid var(--ln);border-radius:0 0 4px 4px;margin-bottom:0}
.res-item{display:flex;align-items:center;gap:7px;padding:6px 9px;border-bottom:1px solid var(--ln);font-size:10px}
.res-item:last-child{border-bottom:none}
.res-thumb{width:26px;height:26px;border-radius:3px;background:#C4B8A8;flex-shrink:0}
.res-empty{color:rgba(255,255,255,.35);font-style:italic;padding:6px 9px;font-size:9px}
/* ── Add-item screen ── */
.mobile-shell{background:var(--sf);display:flex;flex-direction:column;min-height:560px}
.topbar{height:48px;background:var(--pr);display:flex;align-items:center;padding:0 14px;gap:10px;flex-shrink:0}
.topbar-back{font-size:10px;font-weight:700;color:rgba(255,255,255,.8);cursor:pointer}
.topbar-title{font-family:'Lora',Georgia,serif;font-size:13px;font-weight:700;color:#fff;flex:1;text-align:center}
.topbar-save{font-size:10px;font-weight:800;color:#fff;background:rgba(255,255,255,.2);border:none;border-radius:6px;padding:5px 10px;cursor:pointer;min-height:30px}
.topbar-save.off{opacity:.35;cursor:not-allowed}
.scroll-body{flex:1;overflow-y:auto;background:var(--cv)}
.cam-zone{padding:18px 14px;background:var(--cv);border-bottom:1px solid var(--ln)}
.cam-btn{background:var(--pr);color:#fff;border:none;border-radius:10px;width:100%;padding:16px;font-family:'Lora',Georgia,serif;font-size:14px;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:10px;min-height:60px}
.cam-sub{font-size:10px;color:var(--im);text-align:center;margin-top:9px;line-height:1.5}
.cam-link{color:var(--pr);text-decoration:underline;cursor:pointer}
.strip-zone{padding:12px 14px;background:var(--sf);border-bottom:1px solid var(--ln)}
.strip-hdr{display:flex;align-items:center;justify-content:space-between;margin-bottom:9px}
.strip-ttl{font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:var(--im)}
.strip-hint{font-size:8.5px;color:#AAA;font-style:italic}
.strip-row{display:flex;gap:7px;overflow-x:auto}
.sthumb{width:68px;height:68px;border-radius:8px;flex-shrink:0;position:relative}
.sthumb .badge{position:absolute;top:4px;left:4px;background:var(--ac);color:#fff;font-size:7px;font-weight:800;padding:2px 5px;border-radius:3px}
.sthumb .rm{position:absolute;top:4px;right:4px;background:rgba(0,0,0,.55);color:#fff;width:17px;height:17px;border-radius:50%;font-size:9px;font-weight:800;display:flex;align-items:center;justify-content:center;cursor:pointer}
.st-add{width:68px;height:68px;border-radius:8px;border:2px dashed var(--ln);flex-shrink:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;background:var(--cv);cursor:pointer}
.form-sec{padding:14px;display:flex;flex-direction:column;gap:12px;background:var(--sf)}
.f-lbl{font-size:9.5px;font-weight:800;text-transform:uppercase;letter-spacing:.4px;color:var(--im);margin-bottom:4px;display:flex;align-items:center;gap:5px}
.f-req{color:var(--tk);font-size:9px}
.f-opt{font-size:9px;font-weight:400;color:#BBB;font-style:italic;letter-spacing:0;text-transform:none}
.f-sel{width:100%;height:46px;border:1.5px solid var(--pr);border-radius:8px;background:var(--sf);font-size:13px;color:var(--ink);padding:0 12px;display:flex;align-items:center;justify-content:space-between}
.f-in{width:100%;height:46px;border:1.5px solid var(--ln);border-radius:8px;background:#FAFAF7;font-size:13px;color:var(--ink);padding:0 12px;display:flex;align-items:center}
.f-in.focus{border-color:var(--pr);box-shadow:0 0 0 3px rgba(91,122,102,.15)}
.f-in.empty{color:#AAA;font-style:italic}
.f-ta{width:100%;min-height:72px;border:1.5px solid var(--ln);border-radius:8px;background:#FAFAF7;font-size:13px;color:var(--im);padding:10px 12px;font-style:italic;display:flex;align-items:flex-start}
.save-bar{background:var(--sf);border-top:1.5px solid var(--ln);padding:10px 14px;display:flex;gap:9px;flex-shrink:0;box-shadow:0 -2px 10px rgba(0,0,0,.06)}
.s-cancel{flex:1;height:46px;border:1.5px solid var(--ln);border-radius:8px;background:transparent;font-family:'Inter',sans-serif;font-size:12px;font-weight:700;color:var(--im);cursor:pointer}
.s-save{flex:2;height:46px;border:none;border-radius:8px;background:var(--pr);font-family:'Inter',sans-serif;font-size:13px;font-weight:700;color:#fff;cursor:pointer}
.s-save.off{opacity:.4;cursor:not-allowed}
/* ── Impl table ── */
.impl{background:#fff;border:1px solid #E0D8CC;border-radius:6px;padding:22px 26px;margin-top:0}
.impl h3{font-size:9px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase;color:#5B7A66;margin-bottom:14px}
.impl-table{width:100%;border-collapse:collapse;font-size:11.5px}
.impl-table th{text-align:left;font-size:9px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:#5B7A66;padding:7px 9px;background:#F7F4EF;border-bottom:2px solid #E0D8CC}
.impl-table td{padding:8px 9px;border-bottom:1px solid #EDE8E0;vertical-align:top;line-height:1.5;color:#444}
.impl-table td:first-child{font-weight:700;color:#1C2820;width:22%}
.impl-table td code{font-family:monospace;font-size:10.5px;background:#F2EDE4;padding:1px 5px;border-radius:2px;color:#5B7A66}
.impl-table td.px{color:#777;font-size:11px;width:9%}
.impl-table td.note{color:#888;font-size:11px;font-style:italic}
.notes{background:#F9F7F3;border-left:3px solid #C4874A;padding:14px 18px;border-radius:0 4px 4px 0;margin-top:20px}
.notes .nh{font-size:9px;font-weight:800;letter-spacing:1px;text-transform:uppercase;color:#C4874A;margin-bottom:7px}
.notes ul{list-style:none;display:flex;flex-direction:column;gap:6px}
.notes li{font-size:12px;color:#333;padding-left:16px;position:relative;line-height:1.7}
.notes li::before{content:"•";position:absolute;left:0;color:#C4874A;font-weight:800}
.notes li code{font-family:monospace;font-size:11px;background:#F2EDE4;padding:1px 4px;border-radius:2px;color:#5B7A66}
</style>
</head>
<body>
<div class="doc">
<!-- MASTHEAD -->
<div class="mh">
<div class="kicker">UI/UX Spec · Implementation-ready</div>
<h1>Erbstücke Wannsee — Views</h1>
<p>9 Views: Gate Screen · Galerie · Artikel-Modal · Admin Login · Admin Inventar · Artikel hinzufügen/bearbeiten · Codes · Reservierungen · Übersicht. Alle Mockups auf ~55 % skaliert. Impl-Ref-Tabellen enthalten exakte Tailwind-Klassen und Pixel-Werte.</p>
<div class="byline">Leonie Voss · 2026-05-05 · Final · Verweis: 2026-05-05-erbstuecke-wannsee-design-system.html</div>
<div class="tag-row">
<span class="tag">9 views</span>
<span class="tag amber">familie + admin</span>
<span class="tag outline">320px+</span>
<span class="tag outline">WCAG AA</span>
<span class="tag gray">kein dark mode</span>
</div>
</div>
<!-- ══════════════════════════════════════ VIEW 01 — GATE ══ -->
<div class="section">
<span class="section-kicker">View 01 — Route: /</span>
<h2>Gate Screen — Code-Eingabe</h2>
<p class="section-desc">Einzige öffentlich sichtbare Seite. Kein Inhalt ohne gültigen Code. Der <code>?code=</code>-URL-Parameter wird serverseitig in <code>+page.server.ts</code> abgefangen — Cookie setzen → redirect <code>/galerie</code>. Manuelle Eingabe als Fallback.</p>
<div style="max-width:320px;margin-bottom:28px">
<div class="vp-label">📱 Alle Viewports — zentrierte Karte</div>
<div class="frame">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="gate-body">
<div class="gate-card">
<div class="gate-icon">🏡</div>
<div class="gate-title">Erbstücke Wannsee</div>
<div class="gate-sub">Gib deinen persönlichen Code ein, um die Artikel zu sehen:</div>
<div class="gate-input">AB3K · · · ·</div>
<button class="btn btn-prim" style="min-height:48px;font-size:14px">Weiter →</button>
<div class="gate-hint">Noch kein Code? Wende dich an Marcel oder Tante.</div>
</div>
</div>
</div>
</div>
<div class="impl">
<h3>Implementierungs-Referenz — Gate Screen</h3>
<table class="impl-table">
<thead><tr><th>Element</th><th>Tailwind-Klassen</th><th class="px">Px</th><th class="note">Notiz</th></tr></thead>
<tbody>
<tr><td>Seite</td><td><code>min-h-screen bg-canvas flex items-center justify-center p-6</code></td><td class="px"></td><td class="note">Canvas-Hintergrund, vertikal zentriert</td></tr>
<tr><td>Karte</td><td><code>bg-surface border border-line rounded-xl p-7 w-full max-w-sm text-center shadow-sm</code></td><td class="px">28px pad</td><td class="note">max-width 384px</td></tr>
<tr><td>Icon</td><td><code>w-[52px] h-[52px] rounded-full bg-[#DFF0E6] flex items-center justify-center text-2xl mx-auto mb-3.5</code></td><td class="px">52px</td><td class="note">Dekorativ — aria-hidden</td></tr>
<tr><td>Titel</td><td><code>font-serif text-xl font-bold text-ink mb-1.5</code></td><td class="px">20px/700</td><td class="note">Lora</td></tr>
<tr><td>Subtext</td><td><code>text-sm text-ink-muted leading-relaxed mb-5</code></td><td class="px">14px</td><td class="note">Inter</td></tr>
<tr><td>Code-Input</td><td><code>font-mono text-base font-semibold tracking-[4px] uppercase text-center w-full h-12 border-1.5 border-line rounded-lg bg-[#FAFAF7] mb-3 focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/30</code></td><td class="px">48px</td><td class="note">inputmode="text" autocomplete="off" spellcheck="false"</td></tr>
<tr><td>Weiter-Button</td><td><code>bg-primary hover:bg-primary-dark text-white w-full min-h-[48px] rounded-lg font-bold text-sm</code></td><td class="px">48px</td><td class="note">SvelteKit Form Action — funktioniert ohne JS</td></tr>
<tr><td>Fehler-Text</td><td><code>text-xs text-status-taken mt-2 flex items-center gap-1.5</code></td><td class="px">12px</td><td class="note">⚠ Icon + Text — kein Farbe allein</td></tr>
<tr><td>Hinweis</td><td><code>text-[10px] text-ink-muted leading-relaxed mt-3</code></td><td class="px">10px</td><td class="note"></td></tr>
</tbody>
</table>
<div class="notes">
<div class="nh">Verhalten</div>
<ul>
<li>GET <code>/?code=AB3K7MN2</code> → Server liest <code>url.searchParams.get('code')</code>, prüft gegen DB, setzt HTTP-only Cookie <code>family_code</code>, leitet zu <code>/galerie</code></li>
<li>Manuell falscher Code → Fehlermeldung: „Code nicht bekannt — bitte prüfe die Eingabe." mit ⚠-Icon</li>
<li>Gültiger Cookie vorhanden → Redirect zu <code>/galerie</code> ohne Gate Screen zu zeigen (<code>hooks.server.ts</code>)</li>
</ul>
</div>
</div>
</div>
<!-- ══════════════════════════════════════ VIEW 02 — GALERIE ══ -->
<div class="section">
<span class="section-kicker">View 02 — Route: /galerie</span>
<h2>Galerie — Artikel-Übersicht</h2>
<p class="section-desc">Hauptansicht für Familienmitglieder. Telefon: 1-Spalte-Liste (horizontale Karten, Foto 72 px links). Tablet/Desktop: 2-Spalten-Grid (quadratische Karten). Kategorie-Filter als scrollbare Pill-Leiste. Kein Artikel-Titel in der Galerie — nur Kategorie + Status.</p>
<div class="vp-grid">
<div>
<div class="vp-label">📱 Telefon ≤ 767 px — Liste</div>
<div class="frame">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="app-nav"><span class="app-logo">Erbstücke Wannsee</span><span class="app-user">Markus</span></div>
<div class="filter-bar">
<button class="pill on">Alle</button>
<button class="pill">Möbel</button>
<button class="pill">Bücher</button>
<button class="pill">Schmuck</button>
<button class="pill">Werkzeug</button>
</div>
<div class="screen-body">
<div class="gcard"><div class="gcard-img"></div><div class="gcard-body"><div class="gcard-cat">Möbel</div><div class="gcard-title">Schreibtisch Eiche</div><div class="sf">✓ Frei</div></div></div>
<div class="gcard"><div class="gcard-img" style="background:#B8AA98"></div><div class="gcard-body"><div class="gcard-cat">Schmuck</div><div class="gcard-title">Goldbrosche</div><div class="st">● Renate</div></div></div>
<div class="gcard"><div class="gcard-img" style="background:#CBBFB0"></div><div class="gcard-body"><div class="gcard-cat">Bücher</div><div class="gcard-title">Goethe Gesamtausgabe</div><div class="sm">✓ Meine Reservierung</div></div></div>
<div class="gcard"><div class="gcard-img" style="background:#C0B4A4"></div><div class="gcard-body"><div class="gcard-cat">Kunstwerke</div><div class="gcard-title">Aquarell Wannsee</div><div class="sf">✓ Frei</div></div></div>
</div>
</div>
</div>
<div>
<div class="vp-label">💻 Tablet ≥ 768 px — 2-Spalten-Grid</div>
<div class="frame">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="app-nav"><span class="app-logo">Erbstücke Wannsee</span><span class="app-user">Markus</span></div>
<div class="filter-bar">
<button class="pill on">Alle</button>
<button class="pill">Möbel</button>
<button class="pill">Bücher</button>
<button class="pill">Schmuck</button>
</div>
<div class="screen-body">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">
<div class="gcard-sq"><div class="gcard-sq-img"></div><div class="gcard-sq-body"><div class="gcard-cat" style="font-size:8px">Möbel</div><div class="sf" style="font-size:9px;margin-top:2px">✓ Frei</div></div></div>
<div class="gcard-sq"><div class="gcard-sq-img" style="background:#B8AA98"></div><div class="gcard-sq-body"><div class="gcard-cat" style="font-size:8px">Schmuck</div><div class="st" style="font-size:9px;margin-top:2px">● Renate</div></div></div>
<div class="gcard-sq"><div class="gcard-sq-img" style="background:#CBBFB0"></div><div class="gcard-sq-body"><div class="gcard-cat" style="font-size:8px">Bücher</div><div class="sm" style="font-size:8px;margin-top:2px">✓ Meine Res.</div></div></div>
<div class="gcard-sq"><div class="gcard-sq-img" style="background:#C0B4A4"></div><div class="gcard-sq-body"><div class="gcard-cat" style="font-size:8px">Kunstwerke</div><div class="sf" style="font-size:9px;margin-top:2px">✓ Frei</div></div></div>
</div>
</div>
</div>
</div>
</div>
<div class="impl">
<h3>Implementierungs-Referenz — Galerie</h3>
<table class="impl-table">
<thead><tr><th>Element</th><th>Tailwind-Klassen</th><th class="px">Px</th><th class="note">Notiz</th></tr></thead>
<tbody>
<tr><td>Nav</td><td><code>h-11 bg-primary sticky top-0 z-10 flex items-center px-3.5</code></td><td class="px">44px</td><td class="note">Sticky — immer sichtbar</td></tr>
<tr><td>App-Logo</td><td><code>font-serif text-sm font-bold text-white</code></td><td class="px">14px/700</td><td class="note">Lora</td></tr>
<tr><td>Nutzername</td><td><code>ml-auto text-[10px] font-semibold text-white/75</code></td><td class="px">10px</td><td class="note">„Angemeldet als: Markus" — Display-Name aus Cookie</td></tr>
<tr><td>Filter-Leiste</td><td><code>bg-canvas border-b border-line px-2.5 py-2 flex gap-1.5 overflow-x-auto scrollbar-hide sticky top-11 z-10</code></td><td class="px"></td><td class="note">Sticky unter Nav</td></tr>
<tr><td>Pill aktiv</td><td><code>bg-primary text-white border-primary text-[10px] font-bold px-2.5 py-1.5 rounded-full min-h-[30px]</code></td><td class="px">30px min.</td><td class="note"></td></tr>
<tr><td>Pill inaktiv</td><td><code>bg-transparent text-ink-muted border-1.5 border-line text-[10px] font-semibold px-2.5 py-1.5 rounded-full min-h-[30px]</code></td><td class="px"></td><td class="note"></td></tr>
<tr><td>Grid (Telefon)</td><td><code>flex flex-col gap-2 p-2.5</code></td><td class="px">≤ 767px</td><td class="note">1-Spalte-Liste</td></tr>
<tr><td>Grid (Tablet+)</td><td><code>grid grid-cols-2 gap-2 p-2.5</code> via <code>md:grid md:grid-cols-2</code></td><td class="px">≥ 768px</td><td class="note">2-Spalten-Grid</td></tr>
<tr><td>Karte (Liste)</td><td><code>bg-surface border border-line rounded-lg overflow-hidden flex cursor-pointer hover:shadow-sm transition-shadow</code></td><td class="px"></td><td class="note">Klick öffnet Modal</td></tr>
<tr><td>Karte Foto (Liste)</td><td><code>w-[72px] h-[72px] object-cover flex-shrink-0</code></td><td class="px">72×72</td><td class="note">alt="" für dekorative Fotos; aria-describedby auf Karte für SR</td></tr>
<tr><td>Karte Foto (Grid)</td><td><code>aspect-square w-full object-cover</code></td><td class="px">quadratisch</td><td class="note"></td></tr>
<tr><td>Kategorie-Label</td><td><code>text-[9px] font-extrabold uppercase tracking-wider text-ink-muted</code></td><td class="px">9px</td><td class="note"></td></tr>
<tr><td>Status Frei</td><td><code>text-[10px] font-bold text-status-free</code></td><td class="px">10px</td><td class="note">„✓ Frei"</td></tr>
<tr><td>Status Reserviert</td><td><code>text-[10px] font-bold text-status-taken</code></td><td class="px">10px</td><td class="note">„● [Display-Name]"</td></tr>
<tr><td>Status Meins</td><td><code>text-[9px] font-bold bg-[#E8F5EC] text-[#2E6645] px-2 py-0.5 rounded-full border border-[#A8D5B8] mt-0.5 inline-block</code></td><td class="px">Badge</td><td class="note">„✓ Meine Reservierung"</td></tr>
</tbody>
</table>
</div>
</div>
<!-- ══════════════════════════════════════ VIEW 03 — MODAL ══ -->
<div class="section">
<span class="section-kicker">View 03 — Route: /galerie (Modal über Galerie)</span>
<h2>Artikel-Modal — 3 Zustände</h2>
<p class="section-desc">Bottom Sheet öffnet sich beim Antippen einer Galerie-Karte. Foto-Galerie mit Wisch-Geste (touch-action: pan-x) und Seitenzähler. Reservierungsbereich passt sich an Status an. Schließen durch Tippen auf Overlay oder Handle-Swipe nach unten.</p>
<div class="vp-grid">
<div>
<div class="vp-label">Zustand A — Frei</div>
<div class="frame">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="modal-overlay" style="min-height:340px;padding-top:50px">
<div class="modal-sheet">
<div class="modal-handle"></div>
<div class="modal-gallery"><div class="modal-counter">1 / 3 →</div></div>
<div class="modal-body">
<span class="modal-cat">Möbel</span>
<div class="modal-title">Schreibtisch aus Eichenholz</div>
<div class="modal-note">Kleine Schramme oben links.</div>
<div class="modal-div"></div>
<div class="modal-status"><button class="btn btn-prim" style="min-height:48px;font-size:14px">Reservieren</button></div>
</div>
</div>
</div>
</div>
</div>
<div>
<div class="vp-label">Zustand B — Meine Reservierung</div>
<div class="frame">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="modal-overlay" style="min-height:340px;padding-top:50px">
<div class="modal-sheet">
<div class="modal-handle"></div>
<div class="modal-gallery" style="background:#CBBFB0"></div>
<div class="modal-body">
<span class="modal-cat">Bücher</span>
<div class="modal-title">Goethe Gesamtausgabe</div>
<div class="modal-div"></div>
<div class="modal-status">
<div class="sm" style="font-size:12px;padding:7px 14px;border-radius:8px;text-align:center;margin-bottom:7px">✓ Meine Reservierung</div>
<button class="btn btn-danger" style="min-height:44px;font-size:13px">Reservierung aufheben</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div class="vp-label">Zustand C — Fremde Reservierung</div>
<div class="frame">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="modal-overlay" style="min-height:340px;padding-top:50px">
<div class="modal-sheet">
<div class="modal-handle"></div>
<div class="modal-gallery" style="background:#B8AA98"></div>
<div class="modal-body">
<span class="modal-cat">Schmuck</span>
<div class="modal-title">Goldbrosche mit Granat</div>
<div class="modal-div"></div>
<div class="modal-status">
<div style="font-size:13px;font-weight:700;color:var(--tk);margin-bottom:7px">● Reserviert von Renate</div>
<button class="btn btn-dis" disabled style="min-height:44px;font-size:13px">Nicht verfügbar</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="impl">
<h3>Implementierungs-Referenz — Artikel-Modal</h3>
<table class="impl-table">
<thead><tr><th>Element</th><th>Tailwind-Klassen</th><th class="px">Px</th><th class="note">Notiz</th></tr></thead>
<tbody>
<tr><td>Overlay</td><td><code>fixed inset-0 bg-black/45 flex items-end z-50</code></td><td class="px"></td><td class="note">Klick schließt Modal · role="dialog" aria-modal="true"</td></tr>
<tr><td>Sheet</td><td><code>bg-surface rounded-t-xl w-full max-h-[88dvh] overflow-y-auto</code></td><td class="px">88dvh max</td><td class="note">SvelteKit slide-Transition von unten</td></tr>
<tr><td>Handle</td><td><code>w-9 h-1 bg-line rounded mx-auto mt-2.5 mb-3.5</code></td><td class="px">4px</td><td class="note">Swipe-Down schließt Modal</td></tr>
<tr><td>Foto-Galerie</td><td><code>aspect-[4/3] w-full object-cover bg-canvas touch-pan-x overflow-hidden</code></td><td class="px">4:3</td><td class="note">Einfaches Swipe — kein JS-Framework nötig</td></tr>
<tr><td>Foto-Zähler</td><td><code>absolute bottom-2 right-2.5 bg-black/45 text-white text-[9px] font-bold px-1.5 py-0.5 rounded-full</code></td><td class="px"></td><td class="note">„1 / 3 →" — ausblenden bei nur 1 Foto</td></tr>
<tr><td>Modal-Body</td><td><code>p-4</code></td><td class="px">16px</td><td class="note"></td></tr>
<tr><td>Kategorie-Badge</td><td><code>inline-block text-[9px] font-extrabold uppercase tracking-wider bg-[#DFF0E6] text-[#2E6645] px-2.5 py-0.5 rounded-full border border-[#A8D5B8] mb-1.5</code></td><td class="px"></td><td class="note"></td></tr>
<tr><td>Artikel-Titel</td><td><code>font-serif text-[18px] font-bold text-ink leading-snug mb-1.5</code></td><td class="px">18px/700</td><td class="note">Lora</td></tr>
<tr><td>Notiz</td><td><code>text-sm text-ink-muted italic leading-relaxed mb-3</code></td><td class="px">14px</td><td class="note">Nur rendern wenn <code>artikel.notiz</code> vorhanden</td></tr>
<tr><td>Reservieren (A)</td><td><code>bg-primary hover:bg-primary-dark text-white w-full min-h-[48px] rounded-lg font-bold text-sm</code></td><td class="px">48px</td><td class="note">Form Action POST</td></tr>
<tr><td>Aufheben (B)</td><td><code>bg-[#FBF0F0] border-1.5 border-status-taken text-status-taken w-full min-h-[44px] rounded-lg font-bold text-sm</code></td><td class="px">44px</td><td class="note">Nur wenn <code>reservierung.code_id === locals.familyCode.id</code></td></tr>
<tr><td>Gesperrt (C)</td><td><code>bg-canvas border-1.5 border-line text-[#AAA] w-full min-h-[44px] rounded-lg font-bold text-sm cursor-not-allowed</code></td><td class="px">44px</td><td class="note">disabled-Attribut setzen</td></tr>
</tbody>
</table>
<div class="notes">
<div class="nh">Reservierungslogik</div>
<ul>
<li>Zustand wird serverseitig bestimmt: <code>reservierungen</code>-Row existiert? → Taken. <code>code_id === locals.familyCode.id</code>? → Mine.</li>
<li>POST Reservieren → UNIQUE-Constraint auf <code>reservierungen.artikel_id</code> verhindert Doppel-Reservierung atomar</li>
<li>Constraint-Fehler → Galerie neu laden, Modal zeigt Zustand C</li>
</ul>
</div>
</div>
</div>
<!-- ══════════════════════════════════════ VIEW 04 — ADMIN LOGIN ══ -->
<div class="section">
<span class="section-kicker">View 04 — Route: /admin/login</span>
<h2>Admin — Login</h2>
<p class="section-desc">Eigenständige Seite mit dunklem Hintergrund (Admin-BG). Vollständig getrennt von der Familien-Ansicht — kein Code-Zugang. Benutzername (Marcel / Renate / Berit) + Passwort. Kein User-Enumeration bei falschen Credentials.</p>
<div style="max-width:320px;margin-bottom:28px">
<div class="frame">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div style="background:var(--ab);min-height:380px;display:flex;align-items:center;justify-content:center;padding:24px">
<div style="background:var(--sf);border:1px solid var(--ln);border-radius:10px;padding:22px;width:100%">
<div style="font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:1.5px;color:#AAA;margin-bottom:5px">Admin-Zugang</div>
<div style="font-family:'Lora',Georgia,serif;font-size:17px;font-weight:700;color:var(--ink);margin-bottom:18px">Erbstücke Wannsee</div>
<div style="margin-bottom:10px">
<div style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--im);margin-bottom:4px">Benutzername</div>
<div style="height:40px;border:1.5px solid var(--ln);border-radius:6px;background:#FAFAF7;font-size:13px;color:#AAA;padding:0 10px;display:flex;align-items:center">Marcel</div>
</div>
<div style="margin-bottom:14px">
<div style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--im);margin-bottom:4px">Passwort</div>
<div style="height:40px;border:1.5px solid var(--ln);border-radius:6px;background:#FAFAF7;font-size:13px;color:#AAA;padding:0 10px;display:flex;align-items:center">••••••••</div>
</div>
<button style="background:var(--ab);color:#fff;width:100%;min-height:44px;font-family:'Inter',sans-serif;font-size:12px;font-weight:700;border:none;border-radius:6px;cursor:pointer">Anmelden</button>
</div>
</div>
</div>
</div>
<div class="impl">
<h3>Implementierungs-Referenz — Admin Login</h3>
<table class="impl-table">
<thead><tr><th>Element</th><th>Tailwind-Klassen</th><th class="px">Px</th><th class="note">Notiz</th></tr></thead>
<tbody>
<tr><td>Seite</td><td><code>min-h-screen bg-admin flex items-center justify-center p-6</code></td><td class="px"></td><td class="note">Admin-Dunkelgrün als Background</td></tr>
<tr><td>Karte</td><td><code>bg-surface border border-line rounded-xl p-[22px] w-full max-w-xs</code></td><td class="px">22px pad</td><td class="note">Kein Shadow — wirkt geerdet auf dunklem BG</td></tr>
<tr><td>Form-Label</td><td><code>text-[9px] font-extrabold uppercase tracking-wide text-ink-muted mb-1 block</code></td><td class="px">9px</td><td class="note">Immer <code>&lt;label for="…"&gt;</code> verknüpfen</td></tr>
<tr><td>Input</td><td><code>w-full h-10 border-1.5 border-line rounded-md bg-[#FAFAF7] px-2.5 text-[13px] focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/30</code></td><td class="px">40px</td><td class="note"></td></tr>
<tr><td>Anmelden-Button</td><td><code>bg-admin hover:bg-[#1E2C24] text-white w-full min-h-[44px] rounded-md font-bold text-[13px]</code></td><td class="px">44px</td><td class="note">bcrypt.compare → Cookie <code>admin_session</code></td></tr>
<tr><td>Fehler</td><td><code>text-xs text-status-taken mt-2</code></td><td class="px">12px</td><td class="note">Identische Meldung für falschen User + falsches Passwort — kein User-Enumeration</td></tr>
</tbody>
</table>
</div>
</div>
<!-- ══════════════════════════════════════ VIEWS 0508 — ADMIN ══ -->
<div class="section">
<span class="section-kicker">Views 0508 — Route: /admin/*</span>
<h2>Admin-Bereich — Sidebar-Layout</h2>
<p class="section-desc">Gemeinsames Layout-Gerüst für alle vier Admin-Bereiche. Desktop: feste Sidebar (176 px) links, Content-Bereich rechts. Mobil: Sidebar klappt zu Hamburger-Drawer. Amber-Linie markiert den aktiven Bereich.</p>
<!-- Inventar -->
<div style="margin-bottom:36px">
<div class="vp-label">View 05 — /admin/inventar</div>
<div class="frame" style="max-width:680px">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="admin-layout">
<div class="admin-sidebar">
<div class="admin-logo"><span>Admin</span>Erbstücke Wannsee</div>
<div class="slink on">📦 Inventar</div>
<div class="slink">🔑 Codes</div>
<div class="slink">📋 Reservierungen</div>
<div class="slink">📊 Übersicht</div>
</div>
<div class="admin-content">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:11px">
<div class="page-title" style="margin:0">Inventar</div>
<button style="background:var(--ac);color:#fff;border:none;border-radius:5px;font-family:'Inter',sans-serif;font-size:10px;font-weight:700;padding:0 10px;min-height:32px;cursor:pointer">+ Hinzufügen</button>
</div>
<table class="atable">
<thead><tr><th></th><th>Artikel</th><th>Kategorie</th><th>Status</th><th></th></tr></thead>
<tbody>
<tr><td><div class="thumb-sm"></div></td><td style="font-weight:600;font-size:11px">Schreibtisch Eiche</td><td style="color:var(--im)">Möbel</td><td><span class="sf" style="font-size:9.5px">✓ Frei</span></td><td style="text-align:right;display:flex;gap:3px;justify-content:flex-end"><button class="act-btn act-edit"></button><button class="act-btn act-del"></button></td></tr>
<tr><td><div class="thumb-sm" style="background:#B8AA98"></div></td><td style="font-weight:600;font-size:11px">Goldbrosche</td><td style="color:var(--im)">Schmuck</td><td><span class="st" style="font-size:9.5px">● Renate</span></td><td style="text-align:right;display:flex;gap:3px;justify-content:flex-end"><button class="act-btn act-edit"></button><button class="act-btn act-del" style="opacity:.35;cursor:not-allowed"></button></td></tr>
<tr><td><div class="thumb-sm" style="background:#CBBFB0"></div></td><td style="font-weight:600;font-size:11px">Goethe Gesamtausgabe</td><td style="color:var(--im)">Bücher</td><td><span class="sm" style="font-size:8.5px">✓ Markus</span></td><td style="text-align:right;display:flex;gap:3px;justify-content:flex-end"><button class="act-btn act-edit"></button><button class="act-btn act-del" style="opacity:.35;cursor:not-allowed"></button></td></tr>
</tbody>
</table>
<div style="font-size:9px;color:#AAA;margin-top:6px">Löschen nur wenn nicht reserviert.</div>
</div>
</div>
</div>
</div>
<!-- Artikel hinzufügen -->
<div style="margin-bottom:36px">
<div class="vp-label">View 05b — /admin/inventar/neu &amp; /admin/inventar/[id]/bearbeiten — Vollbild Mobile</div>
<div class="vp-grid" style="margin-bottom:0">
<div>
<div style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:#AAA;margin-bottom:6px">Zustand A — Kein Foto</div>
<div class="frame" style="max-width:300px">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="mobile-shell">
<div class="topbar"><div class="topbar-back">← Inventar</div><div class="topbar-title">Hinzufügen</div><button class="topbar-save off">Speichern</button></div>
<div class="scroll-body">
<div class="cam-zone">
<button class="cam-btn"><span style="font-size:18px">📷</span> Foto aufnehmen</button>
<div class="cam-sub">Öffnet die Kamera. Erstes Foto = Galerie-Thumbnail.<br><span class="cam-link">Oder Datei wählen</span></div>
</div>
<div class="form-sec" style="opacity:.4;pointer-events:none">
<div class="field"><div class="f-lbl">Kategorie <span class="f-req">*</span></div><div class="f-in empty">Erst Foto aufnehmen…</div></div>
<div class="field"><div class="f-lbl">Titel <span class="f-opt">(optional)</span></div><div class="f-in empty">Erst Foto aufnehmen…</div></div>
<div class="field"><div class="f-lbl">Notiz <span class="f-opt">(optional)</span></div><div class="f-ta">Erst Foto aufnehmen…</div></div>
</div>
</div>
<div class="save-bar"><button class="s-cancel">Abbrechen</button><button class="s-save off">Speichern</button></div>
</div>
</div>
</div>
<div>
<div style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:#AAA;margin-bottom:6px">Zustand B — Mit Fotos</div>
<div class="frame" style="max-width:300px">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="mobile-shell">
<div class="topbar"><div class="topbar-back">← Inventar</div><div class="topbar-title">Hinzufügen</div><button class="topbar-save">Speichern</button></div>
<div class="scroll-body">
<div class="strip-zone">
<div class="strip-hdr"><span class="strip-ttl">Fotos (3)</span><span class="strip-hint">Ziehen = sortieren</span></div>
<div class="strip-row">
<div class="sthumb" style="background:#C4B8A8"><div class="badge">Thumbnail</div><div class="rm"></div></div>
<div class="sthumb" style="background:#B8AA98"><div class="rm"></div></div>
<div class="sthumb" style="background:#CBBFB0"><div class="rm"></div></div>
<div class="st-add"><span style="font-size:20px;color:var(--im);opacity:.5">📷</span><span style="font-size:7.5px;font-weight:700;color:var(--im);opacity:.5;text-transform:uppercase;letter-spacing:.3px">Mehr</span></div>
</div>
</div>
<div class="form-sec">
<div class="field"><div class="f-lbl">Kategorie <span class="f-req">*</span></div><div class="f-sel"><span>Möbel</span><span style="color:var(--im);font-size:10px"></span></div></div>
<div class="field"><div class="f-lbl">Titel <span class="f-opt">(optional)</span></div><div class="f-in focus">Schreibtisch Eiche</div></div>
<div class="field"><div class="f-lbl">Notiz <span class="f-opt">(optional)</span></div><div class="f-ta" style="color:var(--ink);font-style:normal">Kleine Schramme oben links.</div></div>
</div>
</div>
<div class="save-bar"><button class="s-cancel">Abbrechen</button><button class="s-save">Speichern</button></div>
</div>
</div>
</div>
</div>
</div>
<!-- Codes -->
<div style="margin-bottom:36px">
<div class="vp-label">View 06 — /admin/codes</div>
<div class="frame" style="max-width:680px">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="admin-layout">
<div class="admin-sidebar">
<div class="admin-logo"><span>Admin</span>Erbstücke Wannsee</div>
<div class="slink">📦 Inventar</div>
<div class="slink on">🔑 Codes</div>
<div class="slink">📋 Reservierungen</div>
<div class="slink">📊 Übersicht</div>
</div>
<div class="admin-content">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:11px">
<div class="page-title" style="margin:0">Zugangscodes</div>
<button style="background:var(--ac);color:#fff;border:none;border-radius:5px;font-family:'Inter',sans-serif;font-size:10px;font-weight:700;padding:0 10px;min-height:32px;cursor:pointer">+ Neuer Code</button>
</div>
<table class="atable">
<thead><tr><th>Name</th><th>Code</th><th>Res.</th><th></th></tr></thead>
<tbody>
<tr><td style="font-weight:700">Markus</td><td><span class="code-mono">AB3K7MN2</span></td><td style="color:var(--im)">3</td><td style="display:flex;gap:3px;justify-content:flex-end"><button class="act-btn act-link">🔗 Link</button><button class="act-btn act-del"></button></td></tr>
<tr><td style="font-weight:700">Renate</td><td><span class="code-mono">XP9QRT4W</span></td><td style="color:var(--im)">1</td><td style="display:flex;gap:3px;justify-content:flex-end"><button class="act-btn act-link">🔗 Link</button><button class="act-btn act-del"></button></td></tr>
<tr><td style="font-weight:700">Berit</td><td><span class="code-mono">LM2J6VH8</span></td><td style="color:#CCC;font-style:italic">0</td><td style="display:flex;gap:3px;justify-content:flex-end"><button class="act-btn act-link">🔗 Link</button><button class="act-btn act-del"></button></td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Reservierungen -->
<div style="margin-bottom:36px">
<div class="vp-label">View 07 — /admin/reservierungen</div>
<div class="frame" style="max-width:680px">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="admin-layout">
<div class="admin-sidebar">
<div class="admin-logo"><span>Admin</span>Erbstücke Wannsee</div>
<div class="slink">📦 Inventar</div>
<div class="slink">🔑 Codes</div>
<div class="slink on">📋 Reservierungen</div>
<div class="slink">📊 Übersicht</div>
</div>
<div class="admin-content">
<div class="page-title">Reservierungen</div>
<div class="res-head">Markus — 3 Artikel</div>
<div class="res-body">
<div class="res-item"><div class="res-thumb"></div><div><div style="font-weight:600;font-size:10.5px">Schreibtisch Eiche</div><div style="font-size:8.5px;color:var(--im);text-transform:uppercase;font-weight:700;letter-spacing:.4px">Möbel</div></div></div>
<div class="res-item"><div class="res-thumb" style="background:#CBBFB0"></div><div><div style="font-weight:600;font-size:10.5px">Goethe Gesamtausgabe</div><div style="font-size:8.5px;color:var(--im);text-transform:uppercase;font-weight:700;letter-spacing:.4px">Bücher</div></div></div>
</div>
<div class="res-head" style="margin-top:8px">Renate — 1 Artikel</div>
<div class="res-body">
<div class="res-item"><div class="res-thumb" style="background:#B8AA98"></div><div><div style="font-weight:600;font-size:10.5px">Goldbrosche mit Granat</div><div style="font-size:8.5px;color:var(--im);text-transform:uppercase;font-weight:700;letter-spacing:.4px">Schmuck</div></div></div>
</div>
<div class="res-head" style="margin-top:8px;opacity:.4">Berit — 0 Artikel</div>
<div class="res-body" style="opacity:.4"><div class="res-item" style="font-style:italic;color:#AAA;font-size:10px">Noch keine Reservierungen</div></div>
</div>
</div>
</div>
</div>
<!-- Übersicht -->
<div>
<div class="vp-label">View 08 — /admin/uebersicht</div>
<div class="frame" style="max-width:680px">
<div class="bar"><div class="d"></div><div class="d"></div><div class="d"></div></div>
<div class="admin-layout">
<div class="admin-sidebar">
<div class="admin-logo"><span>Admin</span>Erbstücke Wannsee</div>
<div class="slink">📦 Inventar</div>
<div class="slink">🔑 Codes</div>
<div class="slink">📋 Reservierungen</div>
<div class="slink on">📊 Übersicht</div>
</div>
<div class="admin-content">
<div class="page-title">Übersicht</div>
<div class="stat-row">
<div class="stat-card"><div class="stat-n">47</div><div class="stat-l">Gesamt</div></div>
<div class="stat-card"><div class="stat-n" style="color:var(--tk)">12</div><div class="stat-l">Reserviert</div></div>
<div class="stat-card"><div class="stat-n" style="color:var(--fr)">35</div><div class="stat-l">Frei</div></div>
</div>
<table class="atable">
<thead><tr><th>Kategorie</th><th>Gesamt</th><th>Reserviert</th><th>Frei</th></tr></thead>
<tbody>
<tr><td style="font-weight:600">Möbel</td><td>14</td><td style="color:var(--tk);font-weight:700">5</td><td style="color:var(--fr);font-weight:700">9</td></tr>
<tr><td style="font-weight:600">Bücher</td><td>18</td><td style="color:var(--tk);font-weight:700">4</td><td style="color:var(--fr);font-weight:700">14</td></tr>
<tr><td style="font-weight:600">Schmuck</td><td>7</td><td style="color:var(--tk);font-weight:700">2</td><td style="color:var(--fr);font-weight:700">5</td></tr>
<tr><td style="font-weight:600">Kunstwerke</td><td>4</td><td style="color:var(--tk);font-weight:700">1</td><td style="color:var(--fr);font-weight:700">3</td></tr>
<tr><td style="font-weight:600">Küche</td><td>4</td><td style="color:#CCC">0</td><td style="color:var(--fr);font-weight:700">4</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="impl" style="margin-top:32px">
<h3>Implementierungs-Referenz — Admin-Layout &amp; Artikel hinzufügen</h3>
<table class="impl-table">
<thead><tr><th>Element</th><th>Tailwind-Klassen</th><th class="px">Px</th><th class="note">Notiz</th></tr></thead>
<tbody>
<tr><td>Outer Layout</td><td><code>flex min-h-screen</code></td><td class="px"></td><td class="note">+layout.svelte unter /admin/</td></tr>
<tr><td>Sidebar Desktop</td><td><code>w-44 bg-admin flex-shrink-0 hidden md:flex flex-col py-3</code></td><td class="px">176px</td><td class="note">Dunkelgrün, ab md: sichtbar</td></tr>
<tr><td>Sidebar Mobil</td><td>Overlay-Drawer — Toggle via Svelte <code>$state(false)</code></td><td class="px"></td><td class="note">Hamburger-Icon in Mobile-TopBar</td></tr>
<tr><td>Sidebar-Link aktiv</td><td><code>flex items-center gap-2 px-3 py-1.5 text-[10px] font-semibold text-white bg-white/10 border-l-[3px] border-accent</code></td><td class="px"></td><td class="note">Amber-Linie links</td></tr>
<tr><td>Sidebar-Link inaktiv</td><td><code>flex items-center gap-2 px-3 py-1.5 text-[10px] font-semibold text-white/45 border-l-[3px] border-transparent hover:text-white/75</code></td><td class="px"></td><td class="note"></td></tr>
<tr><td>Content-Bereich</td><td><code>flex-1 bg-canvas p-3.5 overflow-auto</code></td><td class="px">14px</td><td class="note"></td></tr>
<tr><td>Stat-Karten-Grid</td><td><code>grid grid-cols-3 gap-1.5 mb-3</code></td><td class="px"></td><td class="note"></td></tr>
<tr><td>Kamera-Button</td><td><code>font-serif text-sm font-bold bg-primary text-white w-full min-h-[60px] rounded-xl flex items-center justify-center gap-2.5</code></td><td class="px">60px</td><td class="note">Triggert <code>&lt;input capture="environment"&gt;</code></td></tr>
<tr><td>Thumbnail-Strip</td><td><code>flex gap-1.5 overflow-x-auto pb-0.5</code></td><td class="px"></td><td class="note">Drag &amp; Drop für Reihenfolge</td></tr>
<tr><td>Thumbnail</td><td><code>w-[68px] h-[68px] rounded-lg object-cover flex-shrink-0 relative cursor-grab</code></td><td class="px">68×68</td><td class="note"></td></tr>
<tr><td>Thumbnail Badge</td><td><code>absolute top-1 left-1 bg-accent text-white text-[7px] font-extrabold px-1.5 py-0.5 rounded-sm</code></td><td class="px"></td><td class="note">Text: „Thumbnail" — nur auf Index 0</td></tr>
<tr><td>Kategorie (native select)</td><td><code>w-full h-[46px] border-1.5 border-primary rounded-lg px-3 text-[13px] text-ink bg-surface appearance-none</code></td><td class="px">46px</td><td class="note">Native <code>&lt;select&gt;</code> für Mobile-Kompatibilität</td></tr>
<tr><td>Sticky Save Bar</td><td><code>bg-surface border-t border-line p-2.5 flex gap-2 shadow-[0_-2px_10px_rgba(0,0,0,.06)] flex-shrink-0</code></td><td class="px"></td><td class="note">Sticky unten — immer erreichbar</td></tr>
<tr><td>Code löschen (Bestätigung)</td><td>Native <code>confirm()</code> oder Svelte Dialog-Komponente</td><td class="px"></td><td class="note">Warnung: „Alle Reservierungen werden gelöscht"</td></tr>
</tbody>
</table>
<div class="notes">
<div class="nh">Admin — Implementierungshinweise</div>
<ul>
<li>Sidebar-Navigation: <code>+layout.svelte</code> unter <code>src/routes/admin/</code> — rendert Sidebar + <code>&lt;slot /&gt;</code></li>
<li>Auth-Guard: <code>+layout.server.ts</code> prüft <code>locals.admin</code> → redirect zu <code>/admin/login</code> bei fehlendem Cookie</li>
<li>Artikel-Form: gleiche Svelte-Komponente für /neu und /[id]/bearbeiten — Edit füllt Props vor</li>
<li>Foto-Upload: verstecktes <code>&lt;input type="file" accept="image/*" capture="environment" multiple&gt;</code> — Kamera-Button triggert via <code>.click()</code></li>
<li>Fotos als <code>createObjectURL()</code>-Preview im Strip, Upload erst beim Speichern via Multipart Form Action → sharp → WebP</li>
<li>Code „🔗 Link"-Button: <code>navigator.clipboard.writeText(url)</code> mit visueller Bestätigung (Button-Text → „Kopiert ✓")</li>
<li>Löschen von Codes: Bestätigungs-Dialog mit expliziter Warnung über kaskadierte Reservierungs-Löschung</li>
</ul>
</div>
</div>
</div>
</div><!-- /doc -->
</body>
</html>