init: extract dsgvo-trainer from qognio-bot-widget-template@d2c816f

Source files (src/) and rendered bundle (www/) extracted on 2026-04-29T01:35:46+02:00.
Adds nginx:alpine Dockerfile + docker-compose.yml (Caddy-labels) so the bot
runs stand-alone or as a per-customer template clone.

Parent monorepo commit: d2c816f3edbc9760802a11b29ff4151c7aad4b46
Bot version: 2026-04-21
This commit is contained in:
Qognio Bot Extract 2026-04-29 01:35:47 +02:00
commit 20e069df3b
16 changed files with 3953 additions and 0 deletions

7
.dockerignore Normal file
View file

@ -0,0 +1,7 @@
.git
.gitignore
README.md
bot.json
src/
docker-compose.yml
*.md

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.DS_Store
*.log
*.tmp
node_modules/

13
Dockerfile Normal file
View file

@ -0,0 +1,13 @@
# Static-bundle bot — nginx:alpine serves www/ on port 80.
FROM nginx:1.27-alpine
# nginx config: gzip + cache headers + index.html no-store
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Static bundle
COPY www/ /usr/share/nginx/html/
# Run as non-root via nginx's built-in unprivileged image features
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -q --spider http://127.0.0.1/index.html || exit 1

67
README.md Normal file
View file

@ -0,0 +1,67 @@
# Cora — DSGVO Compliance Companion
Cora — dein DSGVO-Sparringspartner. Gamifiziertes Compliance-Training für HR, IT und Führungskräfte. Läuft im deutschen Bunker.
```
slug : dsgvo-trainer
version : 2026-04-21
accent : #059669
runtime : nginx:alpine (static bundle)
template : qognio-bot-template-core (former qognio-bot-widget-template)
```
## Layout
```
.
├── src/ source — config.yaml, welcome.html, curricula.json, etc.
├── www/ rendered, directly servable static bundle
├── Dockerfile nginx:alpine + www/ → port 80
├── docker-compose.yml bot-host pattern (Caddy-labels, restart unless-stopped)
├── nginx.conf gzip + cache + SPA fallback
└── bot.json metadata + parent_core_commit
```
## Run locally
```bash
docker compose up --build
# → http://localhost (you'll need to tweak ports for local-only use)
```
## Re-render after upstream core changes
This repo only stores src + rendered output; the rendering engine lives in
`qognio-bot-template-core`. To pull in core changes:
```bash
cd /path/to/qognio-bot-template-core
./scripts/render.sh dsgvo-trainer --bot-repo /path/to/this/repo
git -C /path/to/this/repo commit -am "render: refresh from core@<sha>"
```
## Per-customer copy (template usage)
This repo is a **template**. To clone for a customer:
```bash
git clone <this-repo> my-customer-dsgvo-trainer
cd my-customer-dsgvo-trainer
# tweak src/config.yaml (slug, bot_key_value, accent), src/welcome.html, src/curricula.json
docker compose -f docker-compose.yml up --build
```
## Deploy to qognio bot-host (.42 LXC pattern — legacy)
The bot-manager spawns LXC containers named after the slug. Push www/ via:
```bash
ssh fmh@46.243.203.42
sudo lxc file push /tmp/www/* dsgvo-trainer/var/www/html/
```
(Or run the docker-compose pattern on a Docker host — same network as Caddy.)
---
Generated by `qognio-bot-template-core/scripts/extract-to-repo.sh` on 2026-04-29T01:35:46+02:00.

14
bot.json Normal file
View file

@ -0,0 +1,14 @@
{
"slug": "dsgvo-trainer",
"name": "Cora",
"title": "DSGVO Compliance Companion",
"tagline": "Compliance-Companion",
"description": "Cora — dein DSGVO-Sparringspartner. Gamifiziertes Compliance-Training für HR, IT und Führungskräfte. Läuft im deutschen Bunker.",
"version": "2026-04-21",
"accent": "#059669",
"extracted_from": "qognio-bot-widget-template",
"parent_core_commit": "d2c816f3edbc9760802a11b29ff4151c7aad4b46",
"extracted_at": "2026-04-29T01:35:46+02:00",
"runtime": "nginx:alpine",
"default_port": 80
}

20
docker-compose.yml Normal file
View file

@ -0,0 +1,20 @@
# Stand-alone bot container.
# Designed for the "caddy" external network on the bot host (qognio pattern).
# Override the hostname via SLUG env var if you reuse this template per customer.
services:
bot:
build: .
image: qognio/bot-dsgvo-trainer:${TAG:-latest}
container_name: bot-dsgvo-trainer
restart: unless-stopped
networks:
- caddy
labels:
caddy: "dsgvo-trainer.on.qognio.com"
caddy.reverse_proxy: "{{upstreams 80}}"
qognio.bot.slug: "dsgvo-trainer"
qognio.bot.version: "2026-04-21"
networks:
caddy:
external: true

27
nginx.conf Normal file
View file

@ -0,0 +1,27 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# gzip
gzip on;
gzip_vary on;
gzip_types text/css application/javascript application/json image/svg+xml text/plain;
gzip_min_length 512;
# index.html: never cache (so welcome screen / wiring updates land instantly)
location = /index.html {
add_header Cache-Control "no-store, must-revalidate" always;
}
# static assets: cache 1h
location ~* \.(?:css|js|json|svg|png|jpe?g|webp|gif|ico|woff2?)$ {
add_header Cache-Control "public, max-age=3600" always;
try_files $uri =404;
}
location / {
try_files $uri $uri/ /index.html;
}
}

19
src/check-badges.js Normal file
View file

@ -0,0 +1,19 @@
// Erste Löschanfrage — 1 korrekte Antwort im Lösch-Modul
if ((state.moduleCorrect && state.moduleCorrect['loeschung'] >= 1)) unlockBadge('erste_loeschanfrage');
// AVV-Detektiv — 10 AVV-Fragen korrekt
if ((state.moduleCorrect && state.moduleCorrect['avv'] >= 10)) unlockBadge('avv_detektiv');
// 72-Stunden-Held — alle Datenpannen-Flashcards bestanden
if ((state.modulePassedFlash && state.modulePassedFlash['datenpannen'])) unlockBadge('meldepflicht');
// Betriebsrat-Flüsterer — HR-Module komplett (3 Quiz korrekt in jedem)
if ((state.moduleCorrect &&
(state.moduleCorrect['bewerbung'] || 0) >= 3 &&
(state.moduleCorrect['personalakte'] || 0) >= 3 &&
(state.moduleCorrect['betriebsrat'] || 0) >= 3)) unlockBadge('betriebsrat');
// DSGVO-Master — 20 von 26 Modulen mit ≥80% Quiz-Score abgeschlossen (~77% Master-Coverage)
if ((state.completedCurricula || []).length >= 20) unlockBadge('dsgvo_master');
// Compliance-Disziplin — 30-Tage-Streak
if (state.maxStreak >= 30) unlockBadge('streak_30');
// Night Owl & Early Bird (beibehalten)
const h = new Date().getHours();
if (h >= 22) unlockBadge('night_owl');
if (h < 7) unlockBadge('early_bird');

38
src/config.yaml Normal file
View file

@ -0,0 +1,38 @@
slug: dsgvo-trainer
bot_name: Cora
bot_title: DSGVO Compliance Companion
brand_letter: C
title: "Cora · DSGVO-Compliance-Companion"
tagline: DSGVO-Compliance-Companion
tagline_short: Compliance-Companion
meta_description: "Cora — dein DSGVO-Sparringspartner. Gamifiziertes Compliance-Training für HR, IT und Führungskräfte. Läuft im deutschen Bunker."
bot_key_var: __CORA_KEY__
bot_key_value: qb_wie4qzjrjwuh
ls_prefix: cora
bot_version: "2026-04-21"
# Color theme
accent: "#059669"
accent_2: "#10b981"
accent_dark: "#047857"
accent_rgb: "5, 150, 105"
accent_rgb_compact: "5,150,105"
success_color: "#10b981"
msg_strong_color: "#a7f3d0"
# UI Labels
tab_flash_label: Karten
tab_curriculum_label: Artikel
curriculum_long_label: Artikel-Library
# Bot-personality strings
quiz_intro_hint: "Wähle ein Modul — Cora generiert Szenario-Fragen aus dem HR-Alltag."
quiz_verb: erstellt
quiz_noun: "Szenario-Fragen"
flash_intro_hint: "Cora erstellt Karteikarten zu einem Thema. Bewerte dein Erinnerungsvermögen — das System wiederholt schwere Karten öfter (SM-2)."
flash_verb: erstellt
# Levels-fallback, welcome.html, check-badges.js are sibling files
# in this directory — render.sh injects their full contents into the
# corresponding {{LEVELS_FALLBACK}}, {{WELCOME_HTML}}, {{CHECK_BADGES_BODY}}
# placeholders verbatim.

403
src/curricula.json Normal file
View file

@ -0,0 +1,403 @@
{
"version": "2026-04-24",
"updated": "2026-04-24",
"curricula": [
{
"id": "grundlagen",
"title": "1 · Grundlagen",
"short": "Personenbezogene Daten, Art. 6, Zweckbindung",
"icon": "shield",
"color": "#059669",
"description": "Was sind personenbezogene Daten, welche Rechtsgrundlagen gibt es nach Art. 6 DSGVO, und wie laufen die 7 Grundsätze im Alltag auf.",
"source_md": "00-grundlagen.md",
"modules": [
{
"id": "pb-daten",
"title": "Personenbezogene Daten (Art. 4 Nr. 1)",
"objectives": [
"Erkennen, wann ein Datum personenbezogen ist",
"Pseudonymisiert vs. anonymisiert abgrenzen",
"Typische Grauzonen (IP, Personalnr, Kombinationen)"
],
"topics": ["Art. 4 Nr. 1", "Pseudonym", "Anonym", "Identifizierbarkeit"],
"difficulty": "einfach",
"source_heading": "Was ist ein personenbezogenes Datum?"
},
{
"id": "grundsaetze",
"title": "Die 7 Grundsätze (Art. 5)",
"objectives": [
"Alle 7 Grundsätze benennen und im HR-Kontext anwenden",
"Rechenschaftspflicht praktisch umsetzen",
"Zweckbindung vs. Datenminimierung unterscheiden"
],
"topics": ["Rechtmäßigkeit", "Zweckbindung", "Datenminimierung", "Speicherbegrenzung", "Rechenschaft"],
"difficulty": "mittel",
"source_heading": "Die 7 Grundsätze (Art. 5 DSGVO)"
},
{
"id": "rechtsgrundlagen",
"title": "Rechtsgrundlagen (Art. 6)",
"objectives": [
"Die 6 Rechtsgrundlagen a-f sicher zuordnen",
"Beispiele aus HR: Vertrag, Einwilligung, berechtigtes Interesse",
"Kombination mit Art. 9 bei Gesundheitsdaten"
],
"topics": ["Art. 6 Abs. 1 lit. a-f", "Einwilligung", "Vertrag", "Berechtigtes Interesse", "Art. 9"],
"difficulty": "mittel",
"source_heading": "Rechtsgrundlagen für die Verarbeitung"
},
{
"id": "einwilligung",
"title": "Einwilligung (Art. 7)",
"objectives": [
"Freiwilligkeit, Spezifität, Informiertheit, Widerruflichkeit",
"Kritische Punkte im Beschäftigungsverhältnis",
"Kopplungsverbot kennen"
],
"topics": ["Art. 7", "Freiwilligkeit", "Widerruf", "Kopplungsverbot"],
"difficulty": "schwer",
"source_heading": "Einwilligung — die heikelste Grundlage"
}
]
},
{
"id": "betroffenenrechte",
"title": "2 · Betroffenenrechte",
"short": "Art. 12-22, Fristen, Formate",
"icon": "handshake",
"color": "#10b981",
"description": "Die Rechte der Betroffenen: Auskunft, Berichtigung, Löschung, Einschränkung, Datenportabilität, Widerspruch — mit Fristen und Ablehnungsgründen.",
"source_md": "01-betroffenenrechte.md",
"modules": [
{
"id": "auskunft",
"title": "Auskunftsrecht (Art. 15)",
"objectives": [
"1-Monats-Frist und Verlängerung sicher anwenden",
"Umfang der Auskunftspflicht abgrenzen (Art. 15 Abs. 4)",
"Kopie vs. Personalakte unterscheiden"
],
"topics": ["Art. 15", "Monatsfrist", "Kopie", "Ex-Mitarbeiter"],
"difficulty": "mittel",
"source_heading": "Art. 15 — Auskunftsrecht"
},
{
"id": "loeschung",
"title": "Recht auf Löschung (Art. 17)",
"objectives": [
"Löschgründe nach Abs. 1 aufzählen",
"Ausnahmen (Aufbewahrungspflicht, Rechtsverfolgung) erkennen",
"Einschränkung (Art. 18) als Alternative prüfen"
],
"topics": ["Art. 17", "Aufbewahrungspflicht", "Art. 18", "Vergessenwerden"],
"difficulty": "mittel",
"source_heading": "Art. 17 — Recht auf Löschung"
},
{
"id": "berichtigung",
"title": "Berichtigung & Widerspruch (Art. 16, 21)",
"objectives": [
"Berichtigung vs. Meinungsäußerung trennen",
"Widerspruchsrecht bei Art. 6 Abs. 1 lit. f",
"Informationspflicht an Empfänger (Art. 19)"
],
"topics": ["Art. 16", "Art. 21", "Art. 19", "Zeugnis"],
"difficulty": "mittel",
"source_heading": "Art. 16 — Berichtigung"
},
{
"id": "portabilitaet",
"title": "Datenportabilität (Art. 20)",
"objectives": [
"Anwendungsbereich: nur Einwilligung/Vertrag + automatisiert",
"Strukturiertes, maschinenlesbares Format",
"Abgrenzung zur Auskunft"
],
"topics": ["Art. 20", "Format", "Automatisierung"],
"difficulty": "einfach",
"source_heading": "Art. 20 — Datenübertragbarkeit"
},
{
"id": "identitaetspruefung",
"title": "Identitätsprüfung bei Anträgen",
"objectives": [
"Art. 12 Abs. 6 sinnvoll anwenden",
"Nicht automatisch Ausweiskopie verlangen",
"Balance: Beweisbarkeit vs. Datenminimierung"
],
"topics": ["Art. 12 Abs. 6", "Ausweiskopie", "Authentifizierung"],
"difficulty": "einfach",
"source_heading": "Identitätsprüfung"
}
]
},
{
"id": "verarbeiter",
"title": "3 · Verarbeiter-Governance",
"short": "AVV, TOMs, Verzeichnis",
"icon": "shield",
"color": "#0ea5e9",
"description": "Auftragsverarbeiter, TOMs nach Art. 32, Verzeichnis von Verarbeitungstätigkeiten — wer wann welche Daten wie schützt.",
"source_md": "02-avv.md, 03-toms.md, 08-werkzeuge.md",
"modules": [
{
"id": "avv",
"title": "Auftragsverarbeitung (Art. 28)",
"objectives": [
"AV erkennen vs. eigene Verantwortliche",
"12 Pflicht-Inhalte des AVV kennen",
"Unterauftragsverarbeiter-Regelung"
],
"topics": ["Art. 28", "AVV", "Weisungsabhängigkeit", "Subunternehmer"],
"difficulty": "mittel",
"source_heading": "Pflicht-Inhalt eines AVV"
},
{
"id": "toms",
"title": "TOMs (Art. 32)",
"objectives": [
"4 Schutzziele: Vertraulichkeit, Integrität, Verfügbarkeit, Belastbarkeit",
"Risikoadaptiv argumentieren",
"Typische Maßnahmen Zutritts-/Zugangs-/Zugriffskontrolle"
],
"topics": ["Art. 32", "Verschlüsselung", "Backup", "Zugriffskontrolle"],
"difficulty": "mittel",
"source_heading": "Die vier Schutzziele"
},
{
"id": "vvt",
"title": "Verzeichnis der Verarbeitungstätigkeiten (Art. 30)",
"objectives": [
"Pflicht auch <250 MA bei HR-Daten",
"8 Pflicht-Inhalte je Eintrag",
"Pflegeprozess + Eigentümer"
],
"topics": ["Art. 30", "VVT", "Löschfristen"],
"difficulty": "einfach",
"source_heading": "Verzeichnis von Verarbeitungstätigkeiten"
},
{
"id": "dsfa",
"title": "DSFA (Art. 35)",
"objectives": [
"Wann ist eine DSFA verpflichtend?",
"7 Bewertungskriterien der WP248",
"KI im Recruiting als Trigger"
],
"topics": ["Art. 35", "DSFA", "Hochrisiko", "AI Act"],
"difficulty": "schwer",
"source_heading": "DSFA"
},
{
"id": "drittlaender",
"title": "Drittland-Transfer (Kap. V)",
"objectives": [
"Angemessenheitsbeschluss, SCC, BCR",
"Transfer Impact Assessment (TIA)",
"USA nach EU-US Data Privacy Framework"
],
"topics": ["SCC", "TIA", "Art. 46", "EU-US DPF"],
"difficulty": "schwer",
"source_heading": "Drittland"
}
]
},
{
"id": "incident",
"title": "4 · Incident Response",
"short": "Datenpannen, Eskalation",
"icon": "clock",
"color": "#ef4444",
"description": "Datenpannen erkennen, melden, dokumentieren — die 72-Stunden-Uhr, Art. 33/34 und sauberes Eskalieren an den DSB.",
"source_md": "04-datenpannen.md, 09-eskalation.md",
"modules": [
{
"id": "datenpannen",
"title": "Datenpanne erkennen (Art. 4 Nr. 12)",
"objectives": [
"Meldepflichtige vs. nicht-meldepflichtige Vorfälle",
"Typische HR-Szenarien (Fehlversand, verlorener Laptop)",
"Dokumentationspflicht auch ohne Meldung"
],
"topics": ["Art. 4 Nr. 12", "Risikobewertung", "Fehlversand"],
"difficulty": "mittel",
"source_heading": "Was ist eine Verletzung"
},
{
"id": "meldepflicht",
"title": "72-Stunden-Meldung (Art. 33)",
"objectives": [
"Meldeformulare der Aufsichtsbehörden",
"6 Pflicht-Inhalte der Meldung",
"Verspätete Meldung mit Begründung"
],
"topics": ["Art. 33", "Aufsichtsbehörde", "72h", "Meldeinhalt"],
"difficulty": "mittel",
"source_heading": "Die 72-Stunden-Uhr"
},
{
"id": "benachrichtigung",
"title": "Betroffenen-Benachrichtigung (Art. 34)",
"objectives": [
"Wann? Bei hohem Risiko für Rechte und Freiheiten",
"Verständliche Sprache (Klartext)",
"Drei Ausnahmen kennen"
],
"topics": ["Art. 34", "Hohes Risiko", "Klartext", "Ausnahmen"],
"difficulty": "mittel",
"source_heading": "Benachrichtigung"
},
{
"id": "eskalation",
"title": "Eskalationsmatrix: Wann zum DSB?",
"objectives": [
"Sofort-Fälle (Datenpanne, Behördenpost) erkennen",
"Vor-Umsetzungs-Fälle (neues System, KI) erkennen",
"Saubere Eskalations-Mail schreiben"
],
"topics": ["DSB", "Aufsichtsbehörde", "Rechtsanwalt", "BR-Einbindung"],
"difficulty": "mittel",
"source_heading": "Zwingend an den DSB"
}
]
},
{
"id": "hr",
"title": "5 · HR-Datenschutz",
"short": "Bewerbung, Personalakte, Betriebsrat",
"icon": "handshake",
"color": "#a855f7",
"description": "HR-spezifische Anwendung der DSGVO nach dem BAG-Urteil vom 8.5.2025: Bewerbung, Personalakte, Gesundheitsdaten, Betriebsrat.",
"source_md": "05-hr-bewerbung.md, 06-hr-personalakte.md, 07-hr-betriebsrat.md",
"modules": [
{
"id": "bewerbung",
"title": "Bewerbungsprozess",
"objectives": [
"6-Monats-Aufbewahrungsregel nach Absage kennen",
"Talentpool nur mit expliziter Einwilligung",
"Social-Media-Recherche korrekt abgrenzen"
],
"topics": ["Art. 6 lit. b", "AGG", "ArbGG", "Talentpool"],
"difficulty": "einfach",
"source_heading": "Aufbewahrung nach Absage"
},
{
"id": "personalakte",
"title": "Personalakte & Gesundheitsdaten",
"objectives": [
"Was gehört rein, was nicht (Diagnose, BEM, SchwbG)",
"Art. 15 + § 83 BetrVG richtig umsetzen",
"BAG 8.5.2025: § 26 BDSG unanwendbar"
],
"topics": ["Personalakte", "BEM", "AU", "§ 83 BetrVG", "Art. 9"],
"difficulty": "mittel",
"source_heading": "Umfang der Personalakte"
},
{
"id": "betriebsrat",
"title": "Betriebsrat & Mitbestimmung",
"objectives": [
"§ 87 Abs. 1 Nr. 6 BetrVG sicher anwenden",
"EuGH C-65/23 Betriebsvereinbarungen als Rechtsgrundlage",
"BR-Informationsanspruch vs. Schutz Einzelner"
],
"topics": ["§ 87 Nr. 6", "EuGH C-65/23", "§ 80 BetrVG", "§ 83 BetrVG"],
"difficulty": "schwer",
"source_heading": "Mitbestimmung des Betriebsrats"
},
{
"id": "zeugnis",
"title": "Zeugnis & Offboarding",
"objectives": [
"Art. 16 Grenzen bei Zeugnissen",
"Löschkonzept nach Austritt",
"Aufbewahrungspflicht Lohn-/Steuerdaten"
],
"topics": ["Zeugnis", "Offboarding", "§ 147 AO", "Löschkonzept"],
"difficulty": "einfach",
"source_heading": "Zeugnis"
}
]
},
{
"id": "tools",
"title": "6 · Tools & FAQ",
"short": "Werkzeugkasten + Alltags-Fragen",
"icon": "medal",
"color": "#f59e0b",
"description": "Der Werkzeugkasten: VVT, DSFA, TIA, Vorlagen — und 20 häufige Alltagsfragen lokaler Datenschutzkoordinatoren.",
"source_md": "08-werkzeuge.md, 10-faq.md",
"modules": [
{
"id": "werkzeugkasten",
"title": "Der Werkzeugkasten",
"objectives": [
"VVT-Pflege und Eigentümer",
"DSFA-Template-Struktur",
"TIA für Drittland-Transfers"
],
"topics": ["VVT", "DSFA", "TIA", "Templates"],
"difficulty": "mittel",
"source_heading": "Verzeichnis von Verarbeitungstätigkeiten"
},
{
"id": "faq-rechte",
"title": "FAQ: Betroffenenrechte",
"objectives": [
"Auskunftsantrag Ex-Mitarbeiter",
"Löschung der Personalakte",
"Berichtigung vs. Meinungen"
],
"topics": ["Auskunft", "Löschung", "Berichtigung"],
"difficulty": "einfach",
"source_heading": "Betroffenenrechte"
},
{
"id": "faq-alltag",
"title": "FAQ: HR-Alltag",
"objectives": [
"AU ohne Diagnose",
"BEM-Akten trennen",
"Private vs. berufliche Profile"
],
"topics": ["AU", "BEM", "Social Media", "Einsicht"],
"difficulty": "einfach",
"source_heading": "Bewerbung"
},
{
"id": "faq-incident",
"title": "FAQ: Datenpanne & KI",
"objectives": [
"Fehlversand-Triage (melden oder nicht)",
"KI-Screening: immer DSB + BR",
"Webcam im Homeoffice: grundsätzlich nein"
],
"topics": ["Fehlversand", "KI-Recruiting", "Monitoring", "Homeoffice"],
"difficulty": "mittel",
"source_heading": "Datenpanne"
}
]
}
],
"badges": [
{"id": "erste_loeschanfrage", "title": "Art. 17 Reflex", "icon": "award", "description": "1. Quiz zu Löschpflicht erfolgreich."},
{"id": "avv_detektiv", "title": "AVV-Detektiv", "icon": "detective", "description": "10 AVV-Fragen korrekt beantwortet."},
{"id": "meldepflicht", "title": "72-Stunden-Held", "icon": "clock", "description": "Meldepflicht-Flashcards alle mit Gut/Leicht bestanden."},
{"id": "betriebsrat", "title": "Betriebsrat-Flüsterer", "icon": "handshake", "description": "HR-Modul (Bewerbung + Personalakte + BR) je 3 richtig."},
{"id": "dsgvo_master", "title": "DSGVO-Master", "icon": "crown", "description": "6 oder mehr Module mit ≥80% abgeschlossen."},
{"id": "streak_30", "title": "Compliance-Disziplin", "icon": "flame", "description": "30 Tage in Folge aktiv gewesen."},
{"id": "night_owl", "title": "Nachteule", "icon": "moon", "description": "Nach 22 Uhr gelernt."},
{"id": "early_bird", "title": "Frühaufsteher", "icon": "sun", "description": "Vor 7 Uhr gelernt."}
],
"levels": [
{"min": 0, "title": "Azubi"},
{"min": 50, "title": "Mitarbeiter:in"},
{"min": 200, "title": "Key-User:in"},
{"min": 500, "title": "Compliance-Expert:in"},
{"min": 1250, "title": "DSB-Kandidat:in"},
{"min": 2500, "title": "Datenschutzbeauftragte:r"},
{"min": 5000, "title": "Senior-DSB"}
]
}

4
src/levels-fallback.js Normal file
View file

@ -0,0 +1,4 @@
{ min: 0, title: 'Azubi' }, { min: 50, title: 'Mitarbeiter:in' },
{ min: 200, title: 'Key-User:in' }, { min: 500, title: 'Compliance-Expert:in' },
{ min: 1250, title: 'DSB-Kandidat:in' }, { min: 2500, title: 'Datenschutzbeauftragte:r' },
{ min: 5000, title: 'Senior-DSB' }

25
src/welcome.html Normal file
View file

@ -0,0 +1,25 @@
<h2>Willkommen bei Cora!</h2>
<p>Ich bin dein:e DSGVO-Sparringspartner:in — pragmatisch, trocken-humorvoll, präzise. Warum das Ganze? <strong>Bußgelder vermeiden</strong>, <strong>Mitarbeiter:innen empowern</strong>, <strong>Rechtssicherheit</strong>. Alles läuft im deutschen Bunker — keine Daten verlassen dein Unternehmen.</p>
<div class="mode-grid">
<button class="mode-card" data-goto="chat">
<strong>Chat</strong>
<span>Frag mich alles zu DSGVO, BDSG, HR-Datenschutz.</span>
</button>
<button class="mode-card" data-goto="quiz">
<strong>Quiz</strong>
<span>Szenario-Fragen aus dem HR-Alltag, mit XP.</span>
</button>
<button class="mode-card" data-goto="flash">
<strong>Flashcards</strong>
<span>Artikel, Begriffe, TOMs — mit Spaced-Repetition.</span>
</button>
<button class="mode-card" data-goto="progress">
<strong>Fortschritt</strong>
<span>XP, Streaks, Badges, Level.</span>
</button>
<button class="mode-card" data-goto="curriculum">
<strong>Artikel</strong>
<span>6 Curricula / 26 Module: von Grundlagen bis Eskalation.</span>
</button>
</div>
<p style="font-size:.82rem;color:var(--text-mute)">In 3 Sätzen: Chat zum Verstehen → Quiz zum Testen → Flashcards zum Merken. Fortschritt zeigt dir, wo du stehst; die Artikel-Library gibt dir 26 kuratierte Module in 6 Themen-Säulen.</p>

1750
www/app.js Normal file

File diff suppressed because it is too large Load diff

403
www/curricula.json Normal file
View file

@ -0,0 +1,403 @@
{
"version": "2026-04-24",
"updated": "2026-04-24",
"curricula": [
{
"id": "grundlagen",
"title": "1 · Grundlagen",
"short": "Personenbezogene Daten, Art. 6, Zweckbindung",
"icon": "shield",
"color": "#059669",
"description": "Was sind personenbezogene Daten, welche Rechtsgrundlagen gibt es nach Art. 6 DSGVO, und wie laufen die 7 Grundsätze im Alltag auf.",
"source_md": "00-grundlagen.md",
"modules": [
{
"id": "pb-daten",
"title": "Personenbezogene Daten (Art. 4 Nr. 1)",
"objectives": [
"Erkennen, wann ein Datum personenbezogen ist",
"Pseudonymisiert vs. anonymisiert abgrenzen",
"Typische Grauzonen (IP, Personalnr, Kombinationen)"
],
"topics": ["Art. 4 Nr. 1", "Pseudonym", "Anonym", "Identifizierbarkeit"],
"difficulty": "einfach",
"source_heading": "Was ist ein personenbezogenes Datum?"
},
{
"id": "grundsaetze",
"title": "Die 7 Grundsätze (Art. 5)",
"objectives": [
"Alle 7 Grundsätze benennen und im HR-Kontext anwenden",
"Rechenschaftspflicht praktisch umsetzen",
"Zweckbindung vs. Datenminimierung unterscheiden"
],
"topics": ["Rechtmäßigkeit", "Zweckbindung", "Datenminimierung", "Speicherbegrenzung", "Rechenschaft"],
"difficulty": "mittel",
"source_heading": "Die 7 Grundsätze (Art. 5 DSGVO)"
},
{
"id": "rechtsgrundlagen",
"title": "Rechtsgrundlagen (Art. 6)",
"objectives": [
"Die 6 Rechtsgrundlagen a-f sicher zuordnen",
"Beispiele aus HR: Vertrag, Einwilligung, berechtigtes Interesse",
"Kombination mit Art. 9 bei Gesundheitsdaten"
],
"topics": ["Art. 6 Abs. 1 lit. a-f", "Einwilligung", "Vertrag", "Berechtigtes Interesse", "Art. 9"],
"difficulty": "mittel",
"source_heading": "Rechtsgrundlagen für die Verarbeitung"
},
{
"id": "einwilligung",
"title": "Einwilligung (Art. 7)",
"objectives": [
"Freiwilligkeit, Spezifität, Informiertheit, Widerruflichkeit",
"Kritische Punkte im Beschäftigungsverhältnis",
"Kopplungsverbot kennen"
],
"topics": ["Art. 7", "Freiwilligkeit", "Widerruf", "Kopplungsverbot"],
"difficulty": "schwer",
"source_heading": "Einwilligung — die heikelste Grundlage"
}
]
},
{
"id": "betroffenenrechte",
"title": "2 · Betroffenenrechte",
"short": "Art. 12-22, Fristen, Formate",
"icon": "handshake",
"color": "#10b981",
"description": "Die Rechte der Betroffenen: Auskunft, Berichtigung, Löschung, Einschränkung, Datenportabilität, Widerspruch — mit Fristen und Ablehnungsgründen.",
"source_md": "01-betroffenenrechte.md",
"modules": [
{
"id": "auskunft",
"title": "Auskunftsrecht (Art. 15)",
"objectives": [
"1-Monats-Frist und Verlängerung sicher anwenden",
"Umfang der Auskunftspflicht abgrenzen (Art. 15 Abs. 4)",
"Kopie vs. Personalakte unterscheiden"
],
"topics": ["Art. 15", "Monatsfrist", "Kopie", "Ex-Mitarbeiter"],
"difficulty": "mittel",
"source_heading": "Art. 15 — Auskunftsrecht"
},
{
"id": "loeschung",
"title": "Recht auf Löschung (Art. 17)",
"objectives": [
"Löschgründe nach Abs. 1 aufzählen",
"Ausnahmen (Aufbewahrungspflicht, Rechtsverfolgung) erkennen",
"Einschränkung (Art. 18) als Alternative prüfen"
],
"topics": ["Art. 17", "Aufbewahrungspflicht", "Art. 18", "Vergessenwerden"],
"difficulty": "mittel",
"source_heading": "Art. 17 — Recht auf Löschung"
},
{
"id": "berichtigung",
"title": "Berichtigung & Widerspruch (Art. 16, 21)",
"objectives": [
"Berichtigung vs. Meinungsäußerung trennen",
"Widerspruchsrecht bei Art. 6 Abs. 1 lit. f",
"Informationspflicht an Empfänger (Art. 19)"
],
"topics": ["Art. 16", "Art. 21", "Art. 19", "Zeugnis"],
"difficulty": "mittel",
"source_heading": "Art. 16 — Berichtigung"
},
{
"id": "portabilitaet",
"title": "Datenportabilität (Art. 20)",
"objectives": [
"Anwendungsbereich: nur Einwilligung/Vertrag + automatisiert",
"Strukturiertes, maschinenlesbares Format",
"Abgrenzung zur Auskunft"
],
"topics": ["Art. 20", "Format", "Automatisierung"],
"difficulty": "einfach",
"source_heading": "Art. 20 — Datenübertragbarkeit"
},
{
"id": "identitaetspruefung",
"title": "Identitätsprüfung bei Anträgen",
"objectives": [
"Art. 12 Abs. 6 sinnvoll anwenden",
"Nicht automatisch Ausweiskopie verlangen",
"Balance: Beweisbarkeit vs. Datenminimierung"
],
"topics": ["Art. 12 Abs. 6", "Ausweiskopie", "Authentifizierung"],
"difficulty": "einfach",
"source_heading": "Identitätsprüfung"
}
]
},
{
"id": "verarbeiter",
"title": "3 · Verarbeiter-Governance",
"short": "AVV, TOMs, Verzeichnis",
"icon": "shield",
"color": "#0ea5e9",
"description": "Auftragsverarbeiter, TOMs nach Art. 32, Verzeichnis von Verarbeitungstätigkeiten — wer wann welche Daten wie schützt.",
"source_md": "02-avv.md, 03-toms.md, 08-werkzeuge.md",
"modules": [
{
"id": "avv",
"title": "Auftragsverarbeitung (Art. 28)",
"objectives": [
"AV erkennen vs. eigene Verantwortliche",
"12 Pflicht-Inhalte des AVV kennen",
"Unterauftragsverarbeiter-Regelung"
],
"topics": ["Art. 28", "AVV", "Weisungsabhängigkeit", "Subunternehmer"],
"difficulty": "mittel",
"source_heading": "Pflicht-Inhalt eines AVV"
},
{
"id": "toms",
"title": "TOMs (Art. 32)",
"objectives": [
"4 Schutzziele: Vertraulichkeit, Integrität, Verfügbarkeit, Belastbarkeit",
"Risikoadaptiv argumentieren",
"Typische Maßnahmen Zutritts-/Zugangs-/Zugriffskontrolle"
],
"topics": ["Art. 32", "Verschlüsselung", "Backup", "Zugriffskontrolle"],
"difficulty": "mittel",
"source_heading": "Die vier Schutzziele"
},
{
"id": "vvt",
"title": "Verzeichnis der Verarbeitungstätigkeiten (Art. 30)",
"objectives": [
"Pflicht auch <250 MA bei HR-Daten",
"8 Pflicht-Inhalte je Eintrag",
"Pflegeprozess + Eigentümer"
],
"topics": ["Art. 30", "VVT", "Löschfristen"],
"difficulty": "einfach",
"source_heading": "Verzeichnis von Verarbeitungstätigkeiten"
},
{
"id": "dsfa",
"title": "DSFA (Art. 35)",
"objectives": [
"Wann ist eine DSFA verpflichtend?",
"7 Bewertungskriterien der WP248",
"KI im Recruiting als Trigger"
],
"topics": ["Art. 35", "DSFA", "Hochrisiko", "AI Act"],
"difficulty": "schwer",
"source_heading": "DSFA"
},
{
"id": "drittlaender",
"title": "Drittland-Transfer (Kap. V)",
"objectives": [
"Angemessenheitsbeschluss, SCC, BCR",
"Transfer Impact Assessment (TIA)",
"USA nach EU-US Data Privacy Framework"
],
"topics": ["SCC", "TIA", "Art. 46", "EU-US DPF"],
"difficulty": "schwer",
"source_heading": "Drittland"
}
]
},
{
"id": "incident",
"title": "4 · Incident Response",
"short": "Datenpannen, Eskalation",
"icon": "clock",
"color": "#ef4444",
"description": "Datenpannen erkennen, melden, dokumentieren — die 72-Stunden-Uhr, Art. 33/34 und sauberes Eskalieren an den DSB.",
"source_md": "04-datenpannen.md, 09-eskalation.md",
"modules": [
{
"id": "datenpannen",
"title": "Datenpanne erkennen (Art. 4 Nr. 12)",
"objectives": [
"Meldepflichtige vs. nicht-meldepflichtige Vorfälle",
"Typische HR-Szenarien (Fehlversand, verlorener Laptop)",
"Dokumentationspflicht auch ohne Meldung"
],
"topics": ["Art. 4 Nr. 12", "Risikobewertung", "Fehlversand"],
"difficulty": "mittel",
"source_heading": "Was ist eine Verletzung"
},
{
"id": "meldepflicht",
"title": "72-Stunden-Meldung (Art. 33)",
"objectives": [
"Meldeformulare der Aufsichtsbehörden",
"6 Pflicht-Inhalte der Meldung",
"Verspätete Meldung mit Begründung"
],
"topics": ["Art. 33", "Aufsichtsbehörde", "72h", "Meldeinhalt"],
"difficulty": "mittel",
"source_heading": "Die 72-Stunden-Uhr"
},
{
"id": "benachrichtigung",
"title": "Betroffenen-Benachrichtigung (Art. 34)",
"objectives": [
"Wann? Bei hohem Risiko für Rechte und Freiheiten",
"Verständliche Sprache (Klartext)",
"Drei Ausnahmen kennen"
],
"topics": ["Art. 34", "Hohes Risiko", "Klartext", "Ausnahmen"],
"difficulty": "mittel",
"source_heading": "Benachrichtigung"
},
{
"id": "eskalation",
"title": "Eskalationsmatrix: Wann zum DSB?",
"objectives": [
"Sofort-Fälle (Datenpanne, Behördenpost) erkennen",
"Vor-Umsetzungs-Fälle (neues System, KI) erkennen",
"Saubere Eskalations-Mail schreiben"
],
"topics": ["DSB", "Aufsichtsbehörde", "Rechtsanwalt", "BR-Einbindung"],
"difficulty": "mittel",
"source_heading": "Zwingend an den DSB"
}
]
},
{
"id": "hr",
"title": "5 · HR-Datenschutz",
"short": "Bewerbung, Personalakte, Betriebsrat",
"icon": "handshake",
"color": "#a855f7",
"description": "HR-spezifische Anwendung der DSGVO nach dem BAG-Urteil vom 8.5.2025: Bewerbung, Personalakte, Gesundheitsdaten, Betriebsrat.",
"source_md": "05-hr-bewerbung.md, 06-hr-personalakte.md, 07-hr-betriebsrat.md",
"modules": [
{
"id": "bewerbung",
"title": "Bewerbungsprozess",
"objectives": [
"6-Monats-Aufbewahrungsregel nach Absage kennen",
"Talentpool nur mit expliziter Einwilligung",
"Social-Media-Recherche korrekt abgrenzen"
],
"topics": ["Art. 6 lit. b", "AGG", "ArbGG", "Talentpool"],
"difficulty": "einfach",
"source_heading": "Aufbewahrung nach Absage"
},
{
"id": "personalakte",
"title": "Personalakte & Gesundheitsdaten",
"objectives": [
"Was gehört rein, was nicht (Diagnose, BEM, SchwbG)",
"Art. 15 + § 83 BetrVG richtig umsetzen",
"BAG 8.5.2025: § 26 BDSG unanwendbar"
],
"topics": ["Personalakte", "BEM", "AU", "§ 83 BetrVG", "Art. 9"],
"difficulty": "mittel",
"source_heading": "Umfang der Personalakte"
},
{
"id": "betriebsrat",
"title": "Betriebsrat & Mitbestimmung",
"objectives": [
"§ 87 Abs. 1 Nr. 6 BetrVG sicher anwenden",
"EuGH C-65/23 Betriebsvereinbarungen als Rechtsgrundlage",
"BR-Informationsanspruch vs. Schutz Einzelner"
],
"topics": ["§ 87 Nr. 6", "EuGH C-65/23", "§ 80 BetrVG", "§ 83 BetrVG"],
"difficulty": "schwer",
"source_heading": "Mitbestimmung des Betriebsrats"
},
{
"id": "zeugnis",
"title": "Zeugnis & Offboarding",
"objectives": [
"Art. 16 Grenzen bei Zeugnissen",
"Löschkonzept nach Austritt",
"Aufbewahrungspflicht Lohn-/Steuerdaten"
],
"topics": ["Zeugnis", "Offboarding", "§ 147 AO", "Löschkonzept"],
"difficulty": "einfach",
"source_heading": "Zeugnis"
}
]
},
{
"id": "tools",
"title": "6 · Tools & FAQ",
"short": "Werkzeugkasten + Alltags-Fragen",
"icon": "medal",
"color": "#f59e0b",
"description": "Der Werkzeugkasten: VVT, DSFA, TIA, Vorlagen — und 20 häufige Alltagsfragen lokaler Datenschutzkoordinatoren.",
"source_md": "08-werkzeuge.md, 10-faq.md",
"modules": [
{
"id": "werkzeugkasten",
"title": "Der Werkzeugkasten",
"objectives": [
"VVT-Pflege und Eigentümer",
"DSFA-Template-Struktur",
"TIA für Drittland-Transfers"
],
"topics": ["VVT", "DSFA", "TIA", "Templates"],
"difficulty": "mittel",
"source_heading": "Verzeichnis von Verarbeitungstätigkeiten"
},
{
"id": "faq-rechte",
"title": "FAQ: Betroffenenrechte",
"objectives": [
"Auskunftsantrag Ex-Mitarbeiter",
"Löschung der Personalakte",
"Berichtigung vs. Meinungen"
],
"topics": ["Auskunft", "Löschung", "Berichtigung"],
"difficulty": "einfach",
"source_heading": "Betroffenenrechte"
},
{
"id": "faq-alltag",
"title": "FAQ: HR-Alltag",
"objectives": [
"AU ohne Diagnose",
"BEM-Akten trennen",
"Private vs. berufliche Profile"
],
"topics": ["AU", "BEM", "Social Media", "Einsicht"],
"difficulty": "einfach",
"source_heading": "Bewerbung"
},
{
"id": "faq-incident",
"title": "FAQ: Datenpanne & KI",
"objectives": [
"Fehlversand-Triage (melden oder nicht)",
"KI-Screening: immer DSB + BR",
"Webcam im Homeoffice: grundsätzlich nein"
],
"topics": ["Fehlversand", "KI-Recruiting", "Monitoring", "Homeoffice"],
"difficulty": "mittel",
"source_heading": "Datenpanne"
}
]
}
],
"badges": [
{"id": "erste_loeschanfrage", "title": "Art. 17 Reflex", "icon": "award", "description": "1. Quiz zu Löschpflicht erfolgreich."},
{"id": "avv_detektiv", "title": "AVV-Detektiv", "icon": "detective", "description": "10 AVV-Fragen korrekt beantwortet."},
{"id": "meldepflicht", "title": "72-Stunden-Held", "icon": "clock", "description": "Meldepflicht-Flashcards alle mit Gut/Leicht bestanden."},
{"id": "betriebsrat", "title": "Betriebsrat-Flüsterer", "icon": "handshake", "description": "HR-Modul (Bewerbung + Personalakte + BR) je 3 richtig."},
{"id": "dsgvo_master", "title": "DSGVO-Master", "icon": "crown", "description": "6 oder mehr Module mit ≥80% abgeschlossen."},
{"id": "streak_30", "title": "Compliance-Disziplin", "icon": "flame", "description": "30 Tage in Folge aktiv gewesen."},
{"id": "night_owl", "title": "Nachteule", "icon": "moon", "description": "Nach 22 Uhr gelernt."},
{"id": "early_bird", "title": "Frühaufsteher", "icon": "sun", "description": "Vor 7 Uhr gelernt."}
],
"levels": [
{"min": 0, "title": "Azubi"},
{"min": 50, "title": "Mitarbeiter:in"},
{"min": 200, "title": "Key-User:in"},
{"min": 500, "title": "Compliance-Expert:in"},
{"min": 1250, "title": "DSB-Kandidat:in"},
{"min": 2500, "title": "Datenschutzbeauftragte:r"},
{"min": 5000, "title": "Senior-DSB"}
]
}

121
www/index.html Normal file
View file

@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Cora · DSGVO-Compliance-Companion</title>
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<meta name="theme-color" content="#0a0a0f">
<meta name="description" content="Cora — dein DSGVO-Sparringspartner. Gamifiziertes Compliance-Training für HR, IT und Führungskräfte. Läuft im deutschen Bunker.">
<link rel="stylesheet" href="styles.css">
<script>window.__CORA_KEY__ = 'qb_wie4qzjrjwuh';</script>
</head>
<body>
<div class="app" role="application" aria-label="Cora DSGVO-Compliance-Companion">
<header class="topbar">
<div class="brand">
<span class="brand-icon" aria-hidden="true">C</span>
<span>Cora <small>Compliance-Companion</small></span>
</div>
<div class="spacer"></div>
<span class="status" role="status" aria-live="polite">Online</span>
</header>
<nav class="tabbar" role="tablist" aria-label="Modi">
<button class="tab" role="tab" aria-selected="true" aria-controls="view-chat" data-mode="chat">
Chat
<span class="tab-kbd">⌃1</span>
</button>
<button class="tab" role="tab" aria-selected="false" aria-controls="view-quiz" data-mode="quiz">
Quiz
<span class="tab-kbd">⌃2</span>
</button>
<button class="tab" role="tab" aria-selected="false" aria-controls="view-flash" data-mode="flash">
Karten
<span class="tab-kbd">⌃3</span>
</button>
<button class="tab" role="tab" aria-selected="false" aria-controls="view-progress" data-mode="progress">
Fortschritt
<span class="tab-kbd">⌃4</span>
</button>
<button class="tab" role="tab" aria-selected="false" aria-controls="view-curriculum" data-mode="curriculum">
Artikel
<span class="tab-kbd">⌃5</span>
</button>
</nav>
<main class="main">
<!-- CHAT -->
<section id="view-chat" class="view" role="tabpanel" aria-labelledby="tab-chat" data-active="true">
<div id="welcome-screen" class="welcome hidden" aria-hidden="true">
<h2>Willkommen bei Cora!</h2>
<p>Ich bin dein:e DSGVO-Sparringspartner:in — pragmatisch, trocken-humorvoll, präzise. Warum das Ganze? <strong>Bußgelder vermeiden</strong>, <strong>Mitarbeiter:innen empowern</strong>, <strong>Rechtssicherheit</strong>. Alles läuft im deutschen Bunker — keine Daten verlassen dein Unternehmen.</p>
<div class="mode-grid">
<button class="mode-card" data-goto="chat">
<strong>Chat</strong>
<span>Frag mich alles zu DSGVO, BDSG, HR-Datenschutz.</span>
</button>
<button class="mode-card" data-goto="quiz">
<strong>Quiz</strong>
<span>Szenario-Fragen aus dem HR-Alltag, mit XP.</span>
</button>
<button class="mode-card" data-goto="flash">
<strong>Flashcards</strong>
<span>Artikel, Begriffe, TOMs — mit Spaced-Repetition.</span>
</button>
<button class="mode-card" data-goto="progress">
<strong>Fortschritt</strong>
<span>XP, Streaks, Badges, Level.</span>
</button>
<button class="mode-card" data-goto="curriculum">
<strong>Artikel</strong>
<span>6 Curricula / 26 Module: von Grundlagen bis Eskalation.</span>
</button>
</div>
<p style="font-size:.82rem;color:var(--text-mute)">In 3 Sätzen: Chat zum Verstehen → Quiz zum Testen → Flashcards zum Merken. Fortschritt zeigt dir, wo du stehst; die Artikel-Library gibt dir 26 kuratierte Module in 6 Themen-Säulen.</p>
</div>
<div id="chat-box" class="chat-box" aria-live="polite" aria-label="Gespräch"></div>
</section>
<!-- QUIZ -->
<section id="view-quiz" class="view" role="tabpanel" aria-labelledby="tab-quiz">
<div id="quiz-host"></div>
</section>
<!-- FLASHCARDS -->
<section id="view-flash" class="view" role="tabpanel" aria-labelledby="tab-flash">
<div id="flash-host"></div>
</section>
<!-- PROGRESS -->
<section id="view-progress" class="view" role="tabpanel" aria-labelledby="tab-progress">
<div id="progress-host"></div>
</section>
<!-- CURRICULUM -->
<section id="view-curriculum" class="view" role="tabpanel" aria-labelledby="tab-curr">
<div id="curr-host"></div>
</section>
</main>
<form id="composer-form" class="composer" aria-label="Nachricht verfassen">
<div id="attach-strip" class="attach-strip" aria-live="polite"></div>
<div class="composer-row">
<button type="button" class="btn-attach" id="composer-attach" aria-label="Datei anhängen" title="Datei anhängen (PDF, Bild, Text — max 5 Dateien, 8 MB)">📎</button>
<input type="file" id="composer-file" multiple accept=".pdf,.txt,.md,.csv,.json,.xml,.yaml,.yml,.log,.png,.jpg,.jpeg,.webp,.gif" hidden>
<textarea id="composer" rows="1" placeholder="Frag Cora — Enter zum Senden, Shift+Enter für Zeilenumbruch" aria-label="Nachricht"></textarea>
<button type="submit" class="btn-primary" id="composer-send">Senden</button>
</div>
</form>
<footer class="footer">
Sovereign AI · Deutscher Bunker · <a href="https://qognio.com">Qognio</a> &nbsp;·&nbsp; DSGVO-konform · Keine externen Fonts · Keine Cookies
</footer>
</div>
<div id="toast-stack" class="toast-stack" aria-live="polite"></div>
<script src="app.js"></script>
</body>
</html>

1038
www/styles.css Normal file

File diff suppressed because it is too large Load diff