init: extract ki-kennzahlen-coach from qognio-bot-widget-template@d2c816f
Source files (src/) and rendered bundle (www/) extracted on 2026-04-29T01:35:47+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-25
This commit is contained in:
commit
1e3703ac4b
22 changed files with 8040 additions and 0 deletions
7
.dockerignore
Normal file
7
.dockerignore
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
bot.json
|
||||
src/
|
||||
docker-compose.yml
|
||||
*.md
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.DS_Store
|
||||
*.log
|
||||
*.tmp
|
||||
node_modules/
|
||||
13
Dockerfile
Normal file
13
Dockerfile
Normal 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
67
README.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# Kai — KI-Kennzahlen & Methodik-Coach
|
||||
|
||||
Kai — dein KI-Kennzahlen-Coach. Methodik, Modell-Bewertung, Bias/Fairness, AI-Reifegrad-Messung für CIOs, CDOs, AI-Leads. Im deutschen Bunker.
|
||||
|
||||
```
|
||||
slug : ki-kennzahlen-coach
|
||||
version : 2026-04-25
|
||||
accent : #0891b2
|
||||
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 ki-kennzahlen-coach --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-ki-kennzahlen-coach
|
||||
cd my-customer-ki-kennzahlen-coach
|
||||
# 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/* ki-kennzahlen-coach/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:47+02:00.
|
||||
14
bot.json
Normal file
14
bot.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"slug": "ki-kennzahlen-coach",
|
||||
"name": "Kai",
|
||||
"title": "KI-Kennzahlen & Methodik-Coach",
|
||||
"tagline": "KI-Kennzahlen",
|
||||
"description": "Kai — dein KI-Kennzahlen-Coach. Methodik, Modell-Bewertung, Bias/Fairness, AI-Reifegrad-Messung für CIOs, CDOs, AI-Leads. Im deutschen Bunker.",
|
||||
"version": "2026-04-25",
|
||||
"accent": "#0891b2",
|
||||
"extracted_from": "qognio-bot-widget-template",
|
||||
"parent_core_commit": "d2c816f3edbc9760802a11b29ff4151c7aad4b46",
|
||||
"extracted_at": "2026-04-29T01:35:47+02:00",
|
||||
"runtime": "nginx:alpine",
|
||||
"default_port": 80
|
||||
}
|
||||
20
docker-compose.yml
Normal file
20
docker-compose.yml
Normal 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-ki-kennzahlen-coach:${TAG:-latest}
|
||||
container_name: bot-ki-kennzahlen-coach
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- caddy
|
||||
labels:
|
||||
caddy: "ki-kennzahlen-coach.on.qognio.com"
|
||||
caddy.reverse_proxy: "{{upstreams 80}}"
|
||||
qognio.bot.slug: "ki-kennzahlen-coach"
|
||||
qognio.bot.version: "2026-04-25"
|
||||
|
||||
networks:
|
||||
caddy:
|
||||
external: true
|
||||
27
nginx.conf
Normal file
27
nginx.conf
Normal 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;
|
||||
}
|
||||
}
|
||||
22
src/check-badges.js
Normal file
22
src/check-badges.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// Erste Metrik-Analyse — 1 korrekte Antwort im Klassifikations-Modul
|
||||
if ((state.moduleCorrect && state.moduleCorrect['klassifikation-metriken'] >= 1)) unlockBadge('erste_metrik');
|
||||
// Metrik-Master — 10 Klassifikations-Metriken-Antworten korrekt
|
||||
if ((state.moduleCorrect && state.moduleCorrect['klassifikation-metriken'] >= 10)) unlockBadge('metrik_master');
|
||||
// Bias-Hunter — 5 korrekte Antworten im Fairness-Metriken-Modul
|
||||
if ((state.moduleCorrect && state.moduleCorrect['fairness-metriken'] >= 5)) unlockBadge('bias_hunter');
|
||||
// EU-AI-Act-Navigator — 5 korrekte Antworten im Risikoklassen-Modul
|
||||
if ((state.moduleCorrect && state.moduleCorrect['risikoklassen'] >= 5)) unlockBadge('ai_act_navigator');
|
||||
// NIST-Praktiker — 5 korrekte Antworten in den 4 NIST-Kernfunktionen
|
||||
if ((state.moduleCorrect && state.moduleCorrect['vier-funktionen'] >= 5)) unlockBadge('nist_practitioner');
|
||||
// ISO-42001-Expert — 5 korrekte Antworten im AIMS-Modul
|
||||
if ((state.moduleCorrect && state.moduleCorrect['iso-42001-aims'] >= 5)) unlockBadge('iso_expert');
|
||||
// Scorecard-Architekt — Scorecard-Design-Modul Flashcards bestanden
|
||||
if ((state.modulePassedFlash && state.modulePassedFlash['scorecard-design'])) unlockBadge('scorecard_architect');
|
||||
// Reifegrad-Lead — 5 korrekte Antworten im Self-Assessment-Modul
|
||||
if ((state.moduleCorrect && state.moduleCorrect['self-assessment'] >= 5)) unlockBadge('reifegrad_lead');
|
||||
// KPI-Master — 22 von 28 Modulen mit ≥80% Quiz-Score abgeschlossen (~79% Master-Coverage)
|
||||
if ((state.completedCurricula || []).length >= 22) unlockBadge('kpi_master');
|
||||
// Night Owl & Early Bird (beibehalten)
|
||||
const h = new Date().getHours();
|
||||
if (h >= 22) unlockBadge('night_owl');
|
||||
if (h < 7) unlockBadge('early_bird');
|
||||
705
src/cockpit-overlay/cockpit.css
Normal file
705
src/cockpit-overlay/cockpit.css
Normal file
|
|
@ -0,0 +1,705 @@
|
|||
/* Kai Cockpit — vanilla CSS, no external fonts/CDN.
|
||||
Extends Kai widget theme: teal #0891b2/#06b6d4, bg #0a0a0f, text #f1f0f5. */
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg: #0a0a0f;
|
||||
--bg-elev: #12121a;
|
||||
--bg-elev2: #191924;
|
||||
--bg-elev3: #22222f;
|
||||
--line: #2a2a38;
|
||||
--line-soft: #1f1f2b;
|
||||
--text: #f1f0f5;
|
||||
--text-mute: #a0a0b5;
|
||||
--text-dim: #707088;
|
||||
--teal: #0891b2;
|
||||
--teal-b: #06b6d4;
|
||||
--teal-c: #22d3ee;
|
||||
--teal-dim: rgba(8,145,178,.15);
|
||||
--ok: #10b981;
|
||||
--warn: #f59e0b;
|
||||
--err: #ef4444;
|
||||
--violet: #a78bfa;
|
||||
--dock-w: 300px;
|
||||
--nav-w: 220px;
|
||||
--radius: 10px;
|
||||
--radius-sm: 6px;
|
||||
--shadow: 0 8px 24px rgba(0,0,0,.45);
|
||||
--t: 0.2s ease;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
min-height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a { color: var(--teal-c); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
button { font-family: inherit; font-size: inherit; }
|
||||
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: #0d0d14; padding: 1px 5px; border-radius: 4px; border: 1px solid var(--line-soft); font-size: .9em; }
|
||||
|
||||
/* ============ LAYOUT ============ */
|
||||
.cockpit {
|
||||
display: grid;
|
||||
grid-template-columns: var(--nav-w) minmax(0, 1fr) var(--dock-w);
|
||||
grid-template-rows: auto 1fr auto;
|
||||
grid-template-areas:
|
||||
"top top top"
|
||||
"nav main dock"
|
||||
"foot foot foot";
|
||||
min-height: 100vh;
|
||||
background: radial-gradient(ellipse at top, rgba(8,145,178,.08), transparent 60%), var(--bg);
|
||||
}
|
||||
.topbar { grid-area: top; }
|
||||
.nav { grid-area: nav; }
|
||||
.main { grid-area: main; }
|
||||
.chat-dock { grid-area: dock; }
|
||||
.footer { grid-area: foot; }
|
||||
|
||||
/* ============ TOPBAR ============ */
|
||||
.topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 14px 22px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: linear-gradient(180deg, rgba(18,18,26,.92), rgba(10,10,15,.92));
|
||||
backdrop-filter: blur(6px);
|
||||
position: sticky; top: 0; z-index: 50;
|
||||
}
|
||||
.brand { display: flex; align-items: center; gap: 12px; }
|
||||
.brand-icon {
|
||||
width: 36px; height: 36px; border-radius: 9px;
|
||||
background: linear-gradient(135deg, var(--teal), var(--teal-b));
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-weight: 700; font-size: 18px; color: #001013;
|
||||
box-shadow: 0 0 0 1px rgba(34,211,238,.25), 0 6px 14px rgba(8,145,178,.35);
|
||||
}
|
||||
.brand-text { display: flex; flex-direction: column; line-height: 1.1; }
|
||||
.brand-title { font-weight: 600; font-size: 16px; letter-spacing: -.2px; }
|
||||
.brand-title small { font-weight: 400; color: var(--text-mute); font-size: 13px; margin-left: 2px; }
|
||||
.brand-sub { color: var(--text-dim); font-size: 11px; letter-spacing: .04em; text-transform: uppercase; }
|
||||
.spacer { flex: 1; }
|
||||
|
||||
.xp-wrap { display: flex; flex-direction: column; gap: 4px; min-width: 170px; }
|
||||
.xp-row { display: flex; justify-content: space-between; font-size: 12px; color: var(--text-mute); }
|
||||
.xp-level { color: var(--teal-c); font-weight: 500; }
|
||||
.xp-score { color: var(--text-mute); }
|
||||
.xp-bar { height: 5px; background: var(--bg-elev2); border-radius: 99px; overflow: hidden; border: 1px solid var(--line-soft); }
|
||||
.xp-bar-fill { height: 100%; width: 0%; background: linear-gradient(90deg, var(--teal), var(--teal-c)); transition: width .4s ease; }
|
||||
|
||||
.back-link {
|
||||
padding: 7px 14px; border: 1px solid var(--line);
|
||||
border-radius: 8px; font-size: 13px; color: var(--text-mute);
|
||||
transition: var(--t);
|
||||
}
|
||||
.back-link:hover { color: var(--text); border-color: var(--teal); background: var(--teal-dim); text-decoration: none; }
|
||||
|
||||
/* ============ NAV ============ */
|
||||
.nav {
|
||||
border-right: 1px solid var(--line);
|
||||
padding: 22px 14px;
|
||||
display: flex; flex-direction: column; gap: 4px;
|
||||
background: rgba(18,18,26,.5);
|
||||
}
|
||||
.nav-item {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
color: var(--text-mute);
|
||||
padding: 12px 14px;
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: var(--t);
|
||||
font-size: 14px;
|
||||
}
|
||||
.nav-item:hover { background: var(--bg-elev); color: var(--text); }
|
||||
.nav-item[aria-selected="true"] {
|
||||
background: linear-gradient(90deg, rgba(8,145,178,.2), rgba(8,145,178,.06));
|
||||
border-color: rgba(34,211,238,.35);
|
||||
color: var(--text);
|
||||
box-shadow: inset 2px 0 0 var(--teal-c);
|
||||
}
|
||||
.nav-num { font-variant-numeric: tabular-nums; font-size: 11px; color: var(--text-dim); letter-spacing: .08em; }
|
||||
.nav-item[aria-selected="true"] .nav-num { color: var(--teal-c); }
|
||||
.nav-label { font-weight: 500; letter-spacing: -.2px; }
|
||||
.nav-kbd { font-size: 10px; color: var(--text-dim); background: var(--bg-elev2); padding: 2px 5px; border-radius: 4px; border: 1px solid var(--line-soft); }
|
||||
|
||||
/* ============ MAIN ============ */
|
||||
.main { padding: 28px 36px 40px; min-width: 0; }
|
||||
.module { display: none; max-width: 1100px; margin: 0 auto; }
|
||||
.module[data-active="true"] { display: block; animation: fadeIn .25s ease; }
|
||||
@keyframes fadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
|
||||
|
||||
.mod-head { margin-bottom: 28px; }
|
||||
.mod-head h1 {
|
||||
margin: 0 0 8px; font-size: 28px; font-weight: 600; letter-spacing: -0.5px;
|
||||
background: linear-gradient(90deg, var(--text), var(--teal-c));
|
||||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||||
}
|
||||
.mod-head p { margin: 0; color: var(--text-mute); max-width: 720px; font-size: 14px; }
|
||||
.mod-badge {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
font-size: 11px; font-weight: 500; letter-spacing: .05em; text-transform: uppercase;
|
||||
color: var(--teal-c); background: var(--teal-dim);
|
||||
border: 1px solid rgba(34,211,238,.25);
|
||||
padding: 4px 10px; border-radius: 99px; margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* Cards + surfaces */
|
||||
.card {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 20px;
|
||||
transition: var(--t);
|
||||
}
|
||||
.card-hover:hover { border-color: rgba(34,211,238,.4); transform: translateY(-1px); cursor: pointer; }
|
||||
|
||||
.btn {
|
||||
padding: 9px 16px; border-radius: 8px;
|
||||
font-weight: 500; cursor: pointer;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--bg-elev2); color: var(--text);
|
||||
transition: var(--t); font-size: 14px;
|
||||
}
|
||||
.btn:hover:not(:disabled) { border-color: var(--teal); color: var(--teal-c); }
|
||||
.btn:disabled { opacity: .4; cursor: not-allowed; }
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--teal), var(--teal-b));
|
||||
border: none; color: #001013; font-weight: 600;
|
||||
}
|
||||
.btn-primary:hover:not(:disabled) { filter: brightness(1.15); color: #001013; }
|
||||
.btn-ghost { background: transparent; border: 1px solid var(--line); }
|
||||
.btn-sm { padding: 6px 12px; font-size: 13px; }
|
||||
.btn-ask-kai {
|
||||
background: transparent;
|
||||
border: 1px dashed rgba(34,211,238,.4);
|
||||
color: var(--teal-c);
|
||||
padding: 8px 14px; font-size: 13px;
|
||||
border-radius: 8px; cursor: pointer; transition: var(--t);
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.btn-ask-kai:hover { background: var(--teal-dim); border-style: solid; }
|
||||
|
||||
/* ============ REIFEGRAD ============ */
|
||||
.reifegrad-intro {
|
||||
display: grid; gap: 10px; margin-bottom: 20px;
|
||||
padding: 16px 20px;
|
||||
background: linear-gradient(135deg, rgba(8,145,178,.08), rgba(18,18,26,.6));
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
.reifegrad-intro .intro-stats {
|
||||
display: flex; gap: 16px; flex-wrap: wrap;
|
||||
font-size: 12px; color: var(--text-mute);
|
||||
}
|
||||
.reifegrad-intro .intro-stats span strong { color: var(--teal-c); font-variant-numeric: tabular-nums; margin-right: 4px; }
|
||||
|
||||
.dim-grid {
|
||||
display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 14px; margin-bottom: 24px;
|
||||
}
|
||||
.dim-card {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 16px 18px;
|
||||
cursor: pointer; transition: var(--t);
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.dim-card:hover { border-color: rgba(34,211,238,.35); transform: translateY(-1px); }
|
||||
.dim-card.done { border-color: rgba(16,185,129,.45); }
|
||||
.dim-card.done::after {
|
||||
content: "✓"; position: absolute; top: 10px; right: 12px;
|
||||
color: var(--ok); font-size: 14px; font-weight: 700;
|
||||
}
|
||||
.dim-card h3 { margin: 0 0 6px; font-size: 15px; font-weight: 600; color: var(--text); }
|
||||
.dim-card p { margin: 0; color: var(--text-mute); font-size: 12.5px; line-height: 1.4; }
|
||||
.dim-card .dim-score {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
margin-top: 10px; font-size: 12px;
|
||||
color: var(--teal-c); font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.dim-card .dim-score .mini-bar {
|
||||
flex: 1; height: 4px; background: var(--bg-elev3); border-radius: 2px; overflow: hidden;
|
||||
}
|
||||
.dim-card .dim-score .mini-bar-fill {
|
||||
height: 100%; background: linear-gradient(90deg, var(--teal), var(--teal-c));
|
||||
transition: width .3s ease;
|
||||
}
|
||||
|
||||
/* Assessment question view */
|
||||
.assessment {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 24px 28px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.assessment-nav {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
margin-bottom: 20px; font-size: 12px; color: var(--text-mute);
|
||||
}
|
||||
.assessment-nav button.linklike {
|
||||
background: transparent; border: none; color: var(--teal-c); cursor: pointer; font-size: 13px;
|
||||
}
|
||||
.assessment-nav button.linklike:hover { text-decoration: underline; }
|
||||
.assessment-progress {
|
||||
height: 4px; background: var(--bg-elev3); border-radius: 99px; overflow: hidden; margin-bottom: 22px;
|
||||
}
|
||||
.assessment-progress-fill { height: 100%; background: linear-gradient(90deg, var(--teal), var(--teal-c)); transition: width .3s ease; }
|
||||
.question-block h2 { margin: 0 0 6px; font-size: 20px; font-weight: 600; letter-spacing: -.3px; }
|
||||
.question-block .question-hint { color: var(--text-mute); font-size: 13px; margin-bottom: 22px; }
|
||||
.likert {
|
||||
display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.likert-btn {
|
||||
background: var(--bg-elev2);
|
||||
border: 1px solid var(--line);
|
||||
color: var(--text); padding: 14px 10px;
|
||||
border-radius: 8px; cursor: pointer; transition: var(--t);
|
||||
display: flex; flex-direction: column; gap: 4px; text-align: center;
|
||||
}
|
||||
.likert-btn:hover { border-color: var(--teal); }
|
||||
.likert-btn[aria-pressed="true"] {
|
||||
background: linear-gradient(135deg, rgba(8,145,178,.3), rgba(8,145,178,.1));
|
||||
border-color: var(--teal-c); color: var(--teal-c);
|
||||
}
|
||||
.likert-btn .likert-n { font-size: 20px; font-weight: 600; font-variant-numeric: tabular-nums; }
|
||||
.likert-btn .likert-t { font-size: 11px; color: var(--text-mute); }
|
||||
.likert-btn[aria-pressed="true"] .likert-t { color: var(--teal-c); }
|
||||
|
||||
.q-nav { display: flex; justify-content: space-between; margin-top: 10px; gap: 10px; }
|
||||
|
||||
/* Radar + results */
|
||||
.radar-wrap {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 24px;
|
||||
display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 30px;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.radar-legend { display: flex; flex-direction: column; gap: 10px; }
|
||||
.radar-legend .legend-row { display: flex; align-items: center; gap: 10px; font-size: 13px; }
|
||||
.radar-legend .legend-swatch { width: 14px; height: 14px; border-radius: 3px; flex-shrink: 0; }
|
||||
.radar-score-big { font-size: 36px; font-weight: 600; color: var(--teal-c); margin: 4px 0 2px; font-variant-numeric: tabular-nums; }
|
||||
.radar-score-hint { color: var(--text-mute); font-size: 12px; margin-bottom: 16px; }
|
||||
|
||||
.recos { display: grid; gap: 12px; margin-top: 14px; }
|
||||
.reco-item {
|
||||
display: grid; grid-template-columns: auto 1fr; gap: 14px;
|
||||
background: var(--bg-elev); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 16px 18px; align-items: start;
|
||||
}
|
||||
.reco-num { width: 30px; height: 30px; border-radius: 50%; background: var(--teal-dim); color: var(--teal-c); display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 14px; }
|
||||
.reco-item h4 { margin: 0 0 4px; font-size: 14px; font-weight: 600; }
|
||||
.reco-item p { margin: 0 0 10px; color: var(--text-mute); font-size: 13px; }
|
||||
|
||||
/* ============ AI ACT CLASSIFIER ============ */
|
||||
.wizard {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 28px; max-width: 720px; margin: 0 auto;
|
||||
}
|
||||
.wizard .progress-dots {
|
||||
display: flex; gap: 6px; justify-content: center; margin-bottom: 22px;
|
||||
}
|
||||
.wizard .progress-dots span {
|
||||
width: 8px; height: 8px; border-radius: 50%; background: var(--bg-elev3); transition: var(--t);
|
||||
}
|
||||
.wizard .progress-dots span.active { background: var(--teal-c); }
|
||||
.wizard .progress-dots span.done { background: var(--teal); }
|
||||
|
||||
.wizard-q h2 { margin: 0 0 8px; font-size: 22px; font-weight: 600; letter-spacing: -.3px; }
|
||||
.wizard-q p { margin: 0 0 22px; color: var(--text-mute); font-size: 14px; }
|
||||
|
||||
.yesno-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||||
.yesno-btn {
|
||||
padding: 22px; border-radius: var(--radius);
|
||||
background: var(--bg-elev2);
|
||||
border: 1px solid var(--line);
|
||||
color: var(--text); cursor: pointer; transition: var(--t);
|
||||
font-size: 16px; font-weight: 500;
|
||||
}
|
||||
.yesno-btn:hover { border-color: var(--teal); transform: translateY(-1px); }
|
||||
.yesno-btn.yes:hover { border-color: var(--err); color: var(--err); }
|
||||
.yesno-btn.no:hover { border-color: var(--teal-c); color: var(--teal-c); }
|
||||
|
||||
.verdict {
|
||||
padding: 28px; border-radius: var(--radius);
|
||||
border: 1px solid var(--line);
|
||||
background: var(--bg-elev);
|
||||
}
|
||||
.verdict .verdict-label {
|
||||
display: inline-block; padding: 4px 12px; border-radius: 99px;
|
||||
font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.verdict.prohibited { border-color: var(--err); background: rgba(239,68,68,.06); }
|
||||
.verdict.prohibited .verdict-label { background: rgba(239,68,68,.18); color: var(--err); }
|
||||
.verdict.high { border-color: var(--warn); background: rgba(245,158,11,.06); }
|
||||
.verdict.high .verdict-label { background: rgba(245,158,11,.18); color: var(--warn); }
|
||||
.verdict.limited { border-color: var(--teal); background: rgba(8,145,178,.06); }
|
||||
.verdict.limited .verdict-label { background: rgba(8,145,178,.2); color: var(--teal-c); }
|
||||
.verdict.minimal { border-color: var(--ok); background: rgba(16,185,129,.06); }
|
||||
.verdict.minimal .verdict-label { background: rgba(16,185,129,.18); color: var(--ok); }
|
||||
.verdict h2 { margin: 0 0 10px; font-size: 26px; font-weight: 600; }
|
||||
.verdict p.verdict-summary { margin: 0 0 18px; color: var(--text-mute); }
|
||||
.verdict .obligation-list {
|
||||
list-style: none; padding: 0; margin: 0 0 22px; display: grid; gap: 10px;
|
||||
}
|
||||
.verdict .obligation-list li {
|
||||
padding: 10px 14px; background: rgba(255,255,255,.02);
|
||||
border-left: 3px solid var(--teal-c);
|
||||
border-radius: 4px; font-size: 13px; color: var(--text);
|
||||
}
|
||||
.verdict .obligation-list li strong { color: var(--teal-c); margin-right: 8px; }
|
||||
.verdict-run-counter { color: var(--text-dim); font-size: 12px; margin-top: 14px; }
|
||||
|
||||
/* ============ DASHBOARD ============ */
|
||||
.dash-group { margin-bottom: 26px; }
|
||||
.dash-group-title {
|
||||
font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
|
||||
color: var(--text-dim); margin-bottom: 10px;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.dash-group-title::after { content: ""; flex: 1; height: 1px; background: var(--line); }
|
||||
.dash-grid { display: grid; gap: 14px; }
|
||||
.dash-grid.exec { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
|
||||
.dash-grid.ops { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
|
||||
.dash-grid.tech { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
|
||||
.tile {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 18px 20px; cursor: pointer; transition: var(--t);
|
||||
text-align: left; display: flex; flex-direction: column; gap: 6px;
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.tile:hover { border-color: rgba(34,211,238,.4); transform: translateY(-2px); box-shadow: var(--shadow); }
|
||||
.tile.seen { border-color: rgba(34,211,238,.25); }
|
||||
.tile.seen::before {
|
||||
content: ""; position: absolute; top: 10px; right: 12px;
|
||||
width: 6px; height: 6px; border-radius: 50%; background: var(--teal-c);
|
||||
}
|
||||
.tile.exec { padding: 22px 24px; }
|
||||
.tile-label { font-size: 12px; color: var(--text-mute); letter-spacing: .02em; }
|
||||
.tile-value { font-size: 26px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; letter-spacing: -0.5px; }
|
||||
.tile.exec .tile-value { font-size: 34px; }
|
||||
.tile-trend {
|
||||
font-size: 11px; display: flex; align-items: center; gap: 4px;
|
||||
color: var(--text-mute); font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.tile-trend.up { color: var(--ok); }
|
||||
.tile-trend.down { color: var(--err); }
|
||||
.tile-trend.neutral { color: var(--text-mute); }
|
||||
|
||||
/* ============ MODAL ============ */
|
||||
.modal-root {
|
||||
position: fixed; inset: 0; z-index: 200;
|
||||
display: none; align-items: center; justify-content: center;
|
||||
padding: 20px; background: rgba(0,0,0,.7); backdrop-filter: blur(6px);
|
||||
}
|
||||
.modal-root.open { display: flex; animation: fadeIn .2s; }
|
||||
.modal {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
padding: 26px 28px;
|
||||
max-width: 560px; width: 100%; max-height: 90vh; overflow-y: auto;
|
||||
box-shadow: var(--shadow);
|
||||
animation: modalIn .2s ease;
|
||||
}
|
||||
@keyframes modalIn { from { transform: translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
|
||||
.modal-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 14px; gap: 16px; }
|
||||
.modal-head h3 { margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -.3px; }
|
||||
.modal-head .modal-close { background: transparent; border: none; color: var(--text-mute); font-size: 22px; cursor: pointer; line-height: 1; }
|
||||
.modal-head .modal-close:hover { color: var(--text); }
|
||||
.modal-value {
|
||||
font-size: 36px; font-weight: 600; color: var(--teal-c);
|
||||
margin-bottom: 4px; font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.modal-value-sub { color: var(--text-mute); font-size: 13px; margin-bottom: 18px; }
|
||||
.modal p { margin: 0 0 14px; font-size: 13.5px; color: var(--text-mute); line-height: 1.5; }
|
||||
.modal p strong { color: var(--text); }
|
||||
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; flex-wrap: wrap; }
|
||||
|
||||
/* ============ CHAT DOCK ============ */
|
||||
.chat-dock {
|
||||
border-left: 1px solid var(--line);
|
||||
background: rgba(18,18,26,.6);
|
||||
display: flex; flex-direction: column;
|
||||
position: sticky; top: 64px; height: calc(100vh - 64px - 44px);
|
||||
overflow: hidden;
|
||||
}
|
||||
.dock-head {
|
||||
display: flex; align-items: baseline; gap: 8px;
|
||||
padding: 14px 18px; border-bottom: 1px solid var(--line);
|
||||
background: rgba(8,145,178,.06);
|
||||
}
|
||||
.dock-title { font-weight: 600; color: var(--teal-c); }
|
||||
.dock-sub { font-size: 11px; color: var(--text-dim); flex: 1; }
|
||||
.dock-reset, .dock-collapse {
|
||||
background: transparent; border: 1px solid var(--line);
|
||||
color: var(--text-mute); width: 26px; height: 26px;
|
||||
border-radius: 6px; cursor: pointer; font-size: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: var(--t);
|
||||
}
|
||||
.dock-reset:hover, .dock-collapse:hover { color: var(--text); border-color: var(--teal); }
|
||||
.dock-box {
|
||||
flex: 1; overflow-y: auto; padding: 14px;
|
||||
display: flex; flex-direction: column; gap: 10px;
|
||||
scrollbar-width: thin; scrollbar-color: var(--line) transparent;
|
||||
}
|
||||
.dock-box::-webkit-scrollbar { width: 6px; }
|
||||
.dock-box::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }
|
||||
.dock-msg {
|
||||
padding: 10px 12px; border-radius: 10px;
|
||||
font-size: 13px; line-height: 1.5; max-width: 95%;
|
||||
word-wrap: break-word; overflow-wrap: break-word;
|
||||
}
|
||||
.dock-msg.user {
|
||||
align-self: flex-end;
|
||||
background: linear-gradient(135deg, var(--teal), var(--teal-b));
|
||||
color: #001013;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
.dock-msg.bot {
|
||||
align-self: flex-start;
|
||||
background: var(--bg-elev2);
|
||||
border: 1px solid var(--line);
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
.dock-msg.bot p { margin: 0 0 8px; }
|
||||
.dock-msg.bot p:last-child { margin-bottom: 0; }
|
||||
.dock-msg.bot ul, .dock-msg.bot ol { margin: 6px 0; padding-left: 20px; }
|
||||
.dock-msg.bot li { margin-bottom: 2px; }
|
||||
.dock-msg.bot code { font-size: .88em; }
|
||||
.dock-msg.bot pre { background: #0d0d14; border: 1px solid var(--line-soft); padding: 10px; border-radius: 6px; overflow-x: auto; font-size: .85em; margin: 8px 0; }
|
||||
.dock-msg.err {
|
||||
background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); color: #fca5a5;
|
||||
}
|
||||
.dock-msg.sys {
|
||||
font-size: 12px; color: var(--text-dim);
|
||||
background: transparent; border: 1px dashed var(--line); align-self: center; text-align: center;
|
||||
}
|
||||
|
||||
.dots { display: inline-flex; gap: 4px; padding: 4px 0; }
|
||||
.dots span {
|
||||
width: 6px; height: 6px; border-radius: 50%; background: var(--teal-c);
|
||||
animation: bounce 1.2s infinite ease-in-out;
|
||||
}
|
||||
.dots span:nth-child(2) { animation-delay: .15s; }
|
||||
.dots span:nth-child(3) { animation-delay: .3s; }
|
||||
@keyframes bounce { 0%, 80%, 100% { transform: translateY(0); opacity: .4; } 40% { transform: translateY(-4px); opacity: 1; } }
|
||||
|
||||
.dock-form {
|
||||
display: grid; grid-template-columns: 1fr auto;
|
||||
gap: 8px; padding: 12px; border-top: 1px solid var(--line);
|
||||
background: var(--bg-elev);
|
||||
}
|
||||
.dock-form textarea {
|
||||
resize: none; min-height: 38px; max-height: 140px;
|
||||
background: var(--bg-elev2);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px; color: var(--text);
|
||||
padding: 9px 12px; font-family: inherit; font-size: 13px;
|
||||
transition: var(--t);
|
||||
}
|
||||
.dock-form textarea:focus { outline: none; border-color: var(--teal-c); box-shadow: 0 0 0 3px rgba(34,211,238,.15); }
|
||||
.btn-send {
|
||||
background: linear-gradient(135deg, var(--teal), var(--teal-b));
|
||||
border: none; color: #001013; font-weight: 700;
|
||||
width: 40px; height: 40px; border-radius: 8px; cursor: pointer;
|
||||
font-size: 18px; transition: var(--t);
|
||||
}
|
||||
.btn-send:hover:not(:disabled) { filter: brightness(1.15); }
|
||||
.btn-send:disabled { opacity: .4; cursor: not-allowed; }
|
||||
.dock-footer-link {
|
||||
display: block; padding: 8px 14px; font-size: 11px; color: var(--text-dim);
|
||||
text-align: center; border-top: 1px solid var(--line); background: var(--bg-elev);
|
||||
}
|
||||
.dock-footer-link:hover { color: var(--teal-c); text-decoration: none; }
|
||||
|
||||
/* Floating open button (mobile / collapsed state) */
|
||||
.dock-open {
|
||||
display: none;
|
||||
position: fixed; bottom: 24px; right: 24px; z-index: 60;
|
||||
padding: 12px 18px; border-radius: 99px;
|
||||
background: linear-gradient(135deg, var(--teal), var(--teal-b));
|
||||
color: #001013; font-weight: 600; border: none;
|
||||
box-shadow: 0 8px 24px rgba(8,145,178,.5);
|
||||
cursor: pointer; font-size: 14px;
|
||||
align-items: center; gap: 8px;
|
||||
}
|
||||
.dock-open-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #001013; animation: pulse 1.4s infinite; }
|
||||
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
|
||||
.chat-dock.collapsed { display: none; }
|
||||
.chat-dock.collapsed + .dock-open { display: inline-flex; }
|
||||
|
||||
/* Full chat module (⌃4) — reuses dock style but takes full width */
|
||||
.chat-full-wrap { max-width: 780px; margin: 0 auto; }
|
||||
.chat-full-wrap p { color: var(--text-mute); font-size: 13px; margin-bottom: 18px; }
|
||||
.chat-full-wrap .chat-full-empty {
|
||||
display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 10px; margin: 16px 0 22px;
|
||||
}
|
||||
.chat-full-wrap .chip {
|
||||
background: var(--bg-elev); border: 1px solid var(--line);
|
||||
color: var(--text-mute); padding: 10px 14px; border-radius: 8px;
|
||||
cursor: pointer; transition: var(--t); font-size: 13px; text-align: left;
|
||||
}
|
||||
.chat-full-wrap .chip:hover { border-color: var(--teal); color: var(--text); }
|
||||
|
||||
/* ============ FOOTER ============ */
|
||||
.footer {
|
||||
grid-area: foot;
|
||||
padding: 12px 22px;
|
||||
border-top: 1px solid var(--line);
|
||||
text-align: center;
|
||||
font-size: 12px; color: var(--text-dim);
|
||||
background: var(--bg-elev);
|
||||
}
|
||||
.footer a { color: var(--text-mute); }
|
||||
|
||||
/* ============ TOASTS ============ */
|
||||
.toast-stack {
|
||||
position: fixed; top: 16px; right: 16px; z-index: 500;
|
||||
display: flex; flex-direction: column; gap: 8px; pointer-events: none;
|
||||
}
|
||||
.toast {
|
||||
padding: 10px 16px; border-radius: 8px;
|
||||
background: var(--bg-elev2); border: 1px solid var(--line);
|
||||
color: var(--text); font-size: 13px; min-width: 220px;
|
||||
box-shadow: var(--shadow); animation: slideIn .3s ease;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.toast.success { border-color: var(--ok); color: #86efac; }
|
||||
.toast.err { border-color: var(--err); color: #fca5a5; }
|
||||
.toast.info { border-color: var(--teal-c); color: var(--teal-c); }
|
||||
@keyframes slideIn { from { transform: translateX(20px); opacity: 0; } to { transform: none; opacity: 1; } }
|
||||
|
||||
/* ============ BADGES (completion) ============ */
|
||||
.badges-row {
|
||||
display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px;
|
||||
}
|
||||
.badge-chip {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
padding: 6px 12px; border-radius: 99px;
|
||||
background: var(--bg-elev2); border: 1px solid var(--line);
|
||||
font-size: 12px; color: var(--text-mute);
|
||||
}
|
||||
.badge-chip.earned { border-color: var(--teal-c); color: var(--teal-c); background: var(--teal-dim); }
|
||||
.badge-chip .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-dim); }
|
||||
.badge-chip.earned .dot { background: var(--teal-c); box-shadow: 0 0 6px var(--teal-c); }
|
||||
|
||||
/* ============ RESPONSIVE ============ */
|
||||
@media (max-width: 1160px) {
|
||||
.cockpit { grid-template-columns: 180px minmax(0, 1fr) 260px; --nav-w: 180px; --dock-w: 260px; }
|
||||
.main { padding: 22px 24px 36px; }
|
||||
}
|
||||
@media (max-width: 960px) {
|
||||
.cockpit {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas: "top" "nav" "main" "foot";
|
||||
}
|
||||
.nav {
|
||||
flex-direction: row; overflow-x: auto; padding: 12px;
|
||||
border-right: none; border-bottom: 1px solid var(--line);
|
||||
position: sticky; top: 70px; z-index: 30;
|
||||
background: rgba(10,10,15,.95); backdrop-filter: blur(8px);
|
||||
}
|
||||
.nav-item { flex: 1 0 auto; grid-template-columns: 1fr; gap: 2px; text-align: center; padding: 8px 12px; }
|
||||
.nav-num, .nav-kbd { display: none; }
|
||||
.nav-label { font-size: 13px; }
|
||||
.chat-dock {
|
||||
position: fixed; top: 0; right: 0; bottom: 0;
|
||||
width: 100%; max-width: 360px;
|
||||
height: 100vh; z-index: 100;
|
||||
box-shadow: -10px 0 40px rgba(0,0,0,.6);
|
||||
transform: translateX(100%); transition: transform .3s ease;
|
||||
}
|
||||
.chat-dock.open { transform: translateX(0); }
|
||||
.dock-open { display: inline-flex; }
|
||||
.radar-wrap { grid-template-columns: 1fr; }
|
||||
.yesno-row { grid-template-columns: 1fr; }
|
||||
.topbar { flex-wrap: wrap; padding: 10px 14px; }
|
||||
.xp-wrap { min-width: 140px; order: 3; }
|
||||
.back-link { order: 2; }
|
||||
.main { padding: 18px 14px 30px; }
|
||||
.mod-head h1 { font-size: 22px; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.likert { grid-template-columns: repeat(5, 1fr); gap: 4px; }
|
||||
.likert-btn { padding: 10px 4px; }
|
||||
.likert-btn .likert-n { font-size: 16px; }
|
||||
.likert-btn .likert-t { font-size: 9px; }
|
||||
.brand-sub { display: none; }
|
||||
}
|
||||
|
||||
/* ============ A11Y ============ */
|
||||
.sr-only {
|
||||
position: absolute; width: 1px; height: 1px;
|
||||
padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); border: 0;
|
||||
}
|
||||
button:focus-visible, .nav-item:focus-visible, textarea:focus-visible, a:focus-visible {
|
||||
outline: 2px solid var(--teal-c); outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* SVG */
|
||||
.radar-svg { width: 100%; height: auto; display: block; }
|
||||
.sparkline-svg { width: 100%; height: 80px; display: block; }
|
||||
|
||||
/* GFM table (added 2026-04-24) */
|
||||
.md-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: .6rem 0;
|
||||
font-size: .9em;
|
||||
background: rgba(255,255,255,0.02);
|
||||
border: 1px solid rgba(255,255,255,0.08);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.md-table thead {
|
||||
background: rgba(8,145,178,0.12);
|
||||
}
|
||||
.md-table th,
|
||||
.md-table td {
|
||||
padding: .5rem .7rem;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
.md-table th {
|
||||
color: #06b6d4;
|
||||
font-weight: 600;
|
||||
font-size: .78em;
|
||||
letter-spacing: .04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.md-table tbody tr:last-child td { border-bottom: none; }
|
||||
.md-table tbody tr:hover { background: rgba(255,255,255,0.03); }
|
||||
.md-table code { font-size: .92em; padding: 1px 5px; }
|
||||
.chat-dock .md-table,
|
||||
.dock-body .md-table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
font-size: .82em;
|
||||
}
|
||||
1249
src/cockpit-overlay/cockpit.js
Normal file
1249
src/cockpit-overlay/cockpit.js
Normal file
File diff suppressed because it is too large
Load diff
102
src/cockpit-overlay/index.html
Normal file
102
src/cockpit-overlay/index.html
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Kai Cockpit · KI-Governance, Reifegrad, EU AI Act</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||||
<meta name="theme-color" content="#0a0a0f">
|
||||
<meta name="description" content="Kai Cockpit — Reifegrad-Assessment, EU AI Act Risk-Classifier, KPI-Dashboard und integrierter Coach-Chat. Sovereign AI aus dem deutschen Bunker.">
|
||||
<link rel="stylesheet" href="cockpit.css">
|
||||
<script>window.__KAI_KEY__ = 'qb_qj0ahcfv6coz';</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cockpit" role="application" aria-label="Kai Cockpit">
|
||||
|
||||
<header class="topbar">
|
||||
<div class="brand">
|
||||
<span class="brand-icon" aria-hidden="true">K</span>
|
||||
<div class="brand-text">
|
||||
<span class="brand-title">Kai <small>Cockpit</small></span>
|
||||
<span class="brand-sub">KI-Governance · Reifegrad · AI Act</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="xp-wrap" title="Deine Erfahrung — geteilt mit dem Kai-Widget">
|
||||
<div class="xp-row">
|
||||
<span id="xp-level" class="xp-level">Lvl 1 · AI-Trainee</span>
|
||||
<span id="xp-score" class="xp-score">0 XP</span>
|
||||
</div>
|
||||
<div class="xp-bar"><div id="xp-bar-fill" class="xp-bar-fill"></div></div>
|
||||
</div>
|
||||
<a class="back-link" href="/" title="Zurück zum Kai-Widget">Widget ›</a>
|
||||
</header>
|
||||
|
||||
<nav class="nav" role="tablist" aria-label="Cockpit-Module">
|
||||
<button class="nav-item" role="tab" aria-selected="true" data-module="reifegrad">
|
||||
<span class="nav-num">01</span>
|
||||
<span class="nav-label">Reifegrad</span>
|
||||
<span class="nav-kbd">⌃1</span>
|
||||
</button>
|
||||
<button class="nav-item" role="tab" aria-selected="false" data-module="aiact">
|
||||
<span class="nav-num">02</span>
|
||||
<span class="nav-label">AI-Act-Classifier</span>
|
||||
<span class="nav-kbd">⌃2</span>
|
||||
</button>
|
||||
<button class="nav-item" role="tab" aria-selected="false" data-module="dashboard">
|
||||
<span class="nav-num">03</span>
|
||||
<span class="nav-label">KPI-Dashboard</span>
|
||||
<span class="nav-kbd">⌃3</span>
|
||||
</button>
|
||||
<button class="nav-item" role="tab" aria-selected="false" data-module="chat">
|
||||
<span class="nav-num">04</span>
|
||||
<span class="nav-label">Kai-Chat</span>
|
||||
<span class="nav-kbd">⌃4</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<main class="main">
|
||||
<section id="mod-reifegrad" class="module" data-active="true" aria-labelledby="h-reifegrad">
|
||||
<div id="reifegrad-host"></div>
|
||||
</section>
|
||||
<section id="mod-aiact" class="module" aria-labelledby="h-aiact">
|
||||
<div id="aiact-host"></div>
|
||||
</section>
|
||||
<section id="mod-dashboard" class="module" aria-labelledby="h-dashboard">
|
||||
<div id="dashboard-host"></div>
|
||||
</section>
|
||||
<section id="mod-chat" class="module" aria-labelledby="h-chatfull">
|
||||
<div id="chat-full-host"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<aside id="chat-dock" class="chat-dock" aria-label="Kai Chat-Dock">
|
||||
<header class="dock-head">
|
||||
<span class="dock-title">Kai</span>
|
||||
<span class="dock-sub">dein Coach</span>
|
||||
<button class="dock-reset" type="button" id="dock-reset" title="Chat zurücksetzen" aria-label="Chat zurücksetzen">↺</button>
|
||||
<button class="dock-collapse" type="button" id="dock-collapse" title="Einklappen" aria-label="Einklappen">✕</button>
|
||||
</header>
|
||||
<div id="dock-box" class="dock-box" aria-live="polite"></div>
|
||||
<form id="dock-form" class="dock-form" aria-label="Kai fragen">
|
||||
<textarea id="dock-input" rows="1" placeholder="Frag Kai — Ctrl+K fokussiert" aria-label="Nachricht"></textarea>
|
||||
<button type="submit" class="btn-send" id="dock-send" aria-label="Senden">→</button>
|
||||
</form>
|
||||
<a class="dock-footer-link" href="/">Zum vollen Widget →</a>
|
||||
</aside>
|
||||
|
||||
<button id="dock-open" class="dock-open" type="button" aria-label="Kai-Chat öffnen" title="Kai-Chat öffnen">
|
||||
<span class="dock-open-dot"></span>Kai fragen
|
||||
</button>
|
||||
|
||||
<footer class="footer">
|
||||
Sovereign AI · Deutscher Bunker · <a href="https://qognio.com">Qognio</a> · DSGVO-konform · Keine externen CDNs
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="modal-root" class="modal-root" aria-hidden="true"></div>
|
||||
<div id="toast-stack" class="toast-stack" aria-live="polite"></div>
|
||||
|
||||
<script src="cockpit.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
33
src/config.yaml
Normal file
33
src/config.yaml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
slug: ki-kennzahlen-coach
|
||||
bot_name: Kai
|
||||
bot_title: "KI-Kennzahlen & Methodik-Coach"
|
||||
brand_letter: K
|
||||
title: "Kai · KI-Kennzahlen & Methodik-Coach"
|
||||
tagline: "KI-Kennzahlen & Methodik-Coach"
|
||||
tagline_short: "KI-Kennzahlen"
|
||||
meta_description: "Kai — dein KI-Kennzahlen-Coach. Methodik, Modell-Bewertung, Bias/Fairness, AI-Reifegrad-Messung für CIOs, CDOs, AI-Leads. Im deutschen Bunker."
|
||||
bot_key_var: __KAI_KEY__
|
||||
bot_key_value: qb_qj0ahcfv6coz
|
||||
ls_prefix: kai
|
||||
bot_version: "2026-04-25"
|
||||
|
||||
# Color theme — cyan/teal (Brücke zwischen DSGVO-grün/Cora und AI-Act-violett/KURT)
|
||||
accent: "#0891b2"
|
||||
accent_2: "#06b6d4"
|
||||
accent_dark: "#0e7490"
|
||||
accent_rgb: "8, 145, 178"
|
||||
accent_rgb_compact: "8,145,178"
|
||||
success_color: "#22c55e"
|
||||
msg_strong_color: "#a5f3fc"
|
||||
|
||||
# UI Labels
|
||||
tab_flash_label: Karten
|
||||
tab_curriculum_label: Module
|
||||
curriculum_long_label: KI-Methodik-Atlas
|
||||
|
||||
# Bot-personality strings
|
||||
quiz_intro_hint: "Wähle ein Modul — Kai generiert Szenario-Fragen aus dem KI-Projekt-Alltag (Modell-Bewertung, Bias, Reifegrad)."
|
||||
quiz_verb: erstellt
|
||||
quiz_noun: "KI-Methodik-Fragen"
|
||||
flash_intro_hint: "Karteikarten zu Klassifikations-Metriken, Fairness, AI-Reifegrad — Spaced-Repetition."
|
||||
flash_verb: generiert
|
||||
386
src/curricula.json
Normal file
386
src/curricula.json
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
{
|
||||
"version": "2026-04-24",
|
||||
"updated": "2026-04-24",
|
||||
"curricula": [
|
||||
{
|
||||
"id": "klassische-ml-metriken",
|
||||
"title": "Klassische ML-Metriken",
|
||||
"short": "ML-Metriken",
|
||||
"icon": "chart",
|
||||
"color": "#0891b2",
|
||||
"description": "Accuracy, Precision, Recall, F1, ROC-AUC, Cohen's Kappa, MCC — welche Metrik wann, und warum",
|
||||
"modules": [
|
||||
{
|
||||
"id": "klassifikation-metriken",
|
||||
"title": "Klassifikations-Metriken",
|
||||
"subtopics": [
|
||||
{"id": "acc-prec-rec", "title": "Accuracy vs Precision vs Recall", "objectives": ["Formeln erklären", "Wann welche priorisieren", "Class-Imbalance-Fallstricke erkennen"]},
|
||||
{"id": "f1-mcc-kappa", "title": "F1, MCC, Cohen's Kappa", "objectives": ["Harmonisches Mittel verstehen", "Wann MCC besser als F1", "Inter-Rater-Reliability"]},
|
||||
{"id": "roc-pr-auc", "title": "ROC-AUC vs PR-AUC", "objectives": ["Threshold-unabhängige Bewertung", "Imbalanced Data: warum PR-AUC besser", "Trade-offs visualisieren"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "regression-metriken",
|
||||
"title": "Regressions-Metriken",
|
||||
"subtopics": [
|
||||
{"id": "rmse-mae", "title": "RMSE vs MAE vs MAPE", "objectives": ["Skalen-Abhängigkeit", "Outlier-Sensitivität", "Relative Fehler"]},
|
||||
{"id": "r2-adjusted", "title": "R² und Adjusted R²", "objectives": ["Erklärte Varianz", "Model-Complexity-Penalty"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "nlp-metriken",
|
||||
"title": "NLP & LLM-Metriken",
|
||||
"subtopics": [
|
||||
{"id": "bleu-rouge", "title": "BLEU, ROUGE, METEOR", "objectives": ["n-gram-Matching", "Grenzen automatischer Metriken"]},
|
||||
{"id": "perplexity", "title": "Perplexity & Token-Metriken", "objectives": ["Information Theory-Grundlage", "Pro-Token-Verlust"]},
|
||||
{"id": "llm-as-judge", "title": "LLM-as-a-Judge", "objectives": ["Evaluation mit Modell-Richter", "Bias in der Evaluation"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "business-kpis",
|
||||
"title": "Business-KPIs für KI",
|
||||
"short": "Business-KPIs",
|
||||
"icon": "briefcase",
|
||||
"color": "#0891b2",
|
||||
"description": "ROI, Time-to-Value, Adoption, Kosten-pro-Inference — KI im Unternehmen messbar machen",
|
||||
"modules": [
|
||||
{
|
||||
"id": "roi-tco",
|
||||
"title": "ROI, TCO, Time-to-Value",
|
||||
"subtopics": [
|
||||
{"id": "roi-formel", "title": "ROI-Berechnung für KI-Projekte", "objectives": ["FTE-Äquivalente einrechnen", "Indirekte Effekte quantifizieren", "Amortisationsdauer"]},
|
||||
{"id": "tco-hidden", "title": "TCO & versteckte Kosten", "objectives": ["Inferenzkosten vs Trainingskosten", "Operational Overhead", "Vendor-Lock-in"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "adoption-satisfaction",
|
||||
"title": "Adoption & Zufriedenheit",
|
||||
"subtopics": [
|
||||
{"id": "adoption-rate", "title": "Adoption-Rate & Active-Users", "objectives": ["WAU/MAU-Unterschied", "Benchmarks aus Praxis"]},
|
||||
{"id": "csat-nps", "title": "CSAT, NPS für KI-Features", "objectives": ["Feature-spezifische Messung", "Anti-Patterns"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "operational",
|
||||
"title": "Operational Metrics",
|
||||
"short": "Operations",
|
||||
"icon": "activity",
|
||||
"color": "#0891b2",
|
||||
"description": "Latency, Throughput, Availability — KI-Systeme im Produktivbetrieb überwachen",
|
||||
"modules": [
|
||||
{
|
||||
"id": "slo-sla",
|
||||
"title": "SLO/SLA-Design",
|
||||
"subtopics": [
|
||||
{"id": "latency-percentiles", "title": "P50/P95/P99 Latency", "objectives": ["Warum nicht Average", "Percentile lesen"]},
|
||||
{"id": "throughput-tps", "title": "Throughput & Tokens-per-Second", "objectives": ["LLM-Durchsatz messen", "Batching-Effekte"]},
|
||||
{"id": "availability", "title": "Availability & Error Budget", "objectives": ["SLO-Definition", "Error-Budget-Policy"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "monitoring-tooling",
|
||||
"title": "Monitoring-Stack",
|
||||
"subtopics": [
|
||||
{"id": "mlflow-arize", "title": "MLflow, Arize, WhyLabs, W&B", "objectives": ["Tool-Landschaft verstehen", "Auswahl-Kriterien"]},
|
||||
{"id": "drift-alerts", "title": "Drift-Alerts & Incident-Playbook", "objectives": ["Schwellenwerte setzen", "False-Positive-Rate"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "datenqualitaet",
|
||||
"title": "Datenqualität & Drift",
|
||||
"short": "Daten",
|
||||
"icon": "database",
|
||||
"color": "#0891b2",
|
||||
"description": "DAMA-Dimensionen, PSI, KL-Divergence — Datenprobleme vor Modell-Problemen erkennen",
|
||||
"modules": [
|
||||
{
|
||||
"id": "dama-dimensions",
|
||||
"title": "DAMA-Dimensionen",
|
||||
"subtopics": [
|
||||
{"id": "completeness-accuracy", "title": "Completeness, Accuracy, Validity", "objectives": ["Jede Dimension mit Beispiel", "Messmethoden"]},
|
||||
{"id": "timeliness-uniqueness", "title": "Timeliness, Uniqueness, Consistency", "objectives": ["Stale-Data-Risiken", "Dedup-Strategien"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "drift-detection",
|
||||
"title": "Drift-Detection",
|
||||
"subtopics": [
|
||||
{"id": "psi-kl", "title": "PSI, KL- und JS-Divergence", "objectives": ["Formel und Interpretation", "Schwellenwerte"]},
|
||||
{"id": "ks-test", "title": "Kolmogorov-Smirnov-Test", "objectives": ["Nicht-parametrischer Test", "Feature-vs-Label-Drift"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "bias-fairness",
|
||||
"title": "Bias & Fairness",
|
||||
"short": "Fairness",
|
||||
"icon": "scale",
|
||||
"color": "#0891b2",
|
||||
"description": "Demographic Parity, Equal Opportunity, Disparate Impact — Diskriminierungsfreie Systeme",
|
||||
"modules": [
|
||||
{
|
||||
"id": "fairness-metriken",
|
||||
"title": "Fairness-Metriken",
|
||||
"subtopics": [
|
||||
{"id": "demographic-parity", "title": "Demographic Parity", "objectives": ["Formel", "Einschränkungen"]},
|
||||
{"id": "equal-opportunity", "title": "Equal Opportunity & Equalized Odds", "objectives": ["Unterschied zu Parity", "Praxisbeispiel HR-Tool"]},
|
||||
{"id": "disparate-impact", "title": "Disparate Impact & 80%-Regel", "objectives": ["US-EEOC-Standard", "EU-Bezug"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "fairness-tooling",
|
||||
"title": "Tooling",
|
||||
"subtopics": [
|
||||
{"id": "aif360-fairlearn", "title": "AIF360 & Fairlearn", "objectives": ["Library-Überblick", "Bias-Audits durchführen"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "explainability",
|
||||
"title": "Explainability (XAI)",
|
||||
"short": "XAI",
|
||||
"icon": "eye",
|
||||
"color": "#0891b2",
|
||||
"description": "SHAP, LIME, Counterfactuals — Modell-Entscheidungen erklärbar machen (EU AI Act Art. 13)",
|
||||
"modules": [
|
||||
{
|
||||
"id": "xai-methoden",
|
||||
"title": "XAI-Methoden",
|
||||
"subtopics": [
|
||||
{"id": "shap-lime", "title": "SHAP vs LIME", "objectives": ["Shapley-Values verstehen", "Lokale vs globale Erklärung"]},
|
||||
{"id": "counterfactuals", "title": "Counterfactual & Anchor Explanations", "objectives": ["Minimal-Änderungs-Prinzip", "Praxisnutzen"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "xai-qualitaet",
|
||||
"title": "Qualität von Erklärungen",
|
||||
"subtopics": [
|
||||
{"id": "fidelity-stability", "title": "Fidelity, Stability, Comprehensibility", "objectives": ["Messgrößen für Erklärungsqualität", "Trade-offs"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "robustheit-security",
|
||||
"title": "Robustheit & Security",
|
||||
"short": "Robustheit",
|
||||
"icon": "shield",
|
||||
"color": "#0891b2",
|
||||
"description": "Adversarial Robustness, Prompt-Injection, Data-Poisoning — Angriffsvektoren verstehen",
|
||||
"modules": [
|
||||
{
|
||||
"id": "adversarial",
|
||||
"title": "Adversarial Robustness",
|
||||
"subtopics": [
|
||||
{"id": "pgd-fgsm", "title": "PGD & FGSM-Attacks", "objectives": ["Perturbations verstehen", "L-infinity-Budget"]},
|
||||
{"id": "certified-robustness", "title": "Certified Robustness", "objectives": ["Formale Garantien vs empirische Tests"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "llm-security",
|
||||
"title": "LLM-Security",
|
||||
"subtopics": [
|
||||
{"id": "prompt-injection", "title": "Prompt-Injection & Jailbreaks", "objectives": ["OWASP LLM Top 10", "Mitigationen"]},
|
||||
{"id": "data-poisoning", "title": "Data-Poisoning-Erkennung", "objectives": ["Training-Set-Forensik", "Monitoring-Metriken"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "governance-reifegrad",
|
||||
"title": "Governance & Reifegrad",
|
||||
"short": "Reifegrad",
|
||||
"icon": "gauge",
|
||||
"color": "#0891b2",
|
||||
"description": "Gartner AI Maturity, MIT CISR, Microsoft RAI MM — wo stehst du, wo willst du hin",
|
||||
"modules": [
|
||||
{
|
||||
"id": "reifegradmodelle",
|
||||
"title": "AI-Reifegradmodelle im Vergleich",
|
||||
"subtopics": [
|
||||
{"id": "gartner-maturity", "title": "Gartner AI Maturity Model", "objectives": ["5 Stufen", "Self-Assessment"]},
|
||||
{"id": "mit-cisr", "title": "MIT CISR & Microsoft RAI MM", "objectives": ["Unterschiede", "DACH-Anwendung"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "self-assessment",
|
||||
"title": "Self-Assessment-Dimensionen",
|
||||
"subtopics": [
|
||||
{"id": "strategy-data", "title": "Strategy, Data, Technology", "objectives": ["Fragebogen-Struktur", "Reifegrad-Score berechnen"]},
|
||||
{"id": "people-processes", "title": "People, Processes", "objectives": ["Kompetenz-Matrix", "Prozess-Reife"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "eu-ai-act",
|
||||
"title": "EU AI Act Compliance",
|
||||
"short": "EU AI Act",
|
||||
"icon": "flag",
|
||||
"color": "#0891b2",
|
||||
"description": "Risikoklassen, CE-Kennzeichnung, Artikel-Pflichten — Was muss ich ab wann erfüllen",
|
||||
"modules": [
|
||||
{
|
||||
"id": "risikoklassen",
|
||||
"title": "Risikoklassen",
|
||||
"subtopics": [
|
||||
{"id": "verboten-high-risk", "title": "Verboten vs High-Risk", "objectives": ["Art. 5 vs Annex III", "Grenzfälle erkennen"]},
|
||||
{"id": "limited-minimal", "title": "Limited & Minimal Risk", "objectives": ["Transparenz-Pflichten", "Chatbots"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "high-risk-pflichten",
|
||||
"title": "High-Risk-Pflichten",
|
||||
"subtopics": [
|
||||
{"id": "risk-management", "title": "Art. 9 Risikomanagement-System", "objectives": ["Dokumentations-Anforderungen"]},
|
||||
{"id": "data-governance", "title": "Art. 10 Data Governance", "objectives": ["Trainings-, Validierungs- und Testdaten"]},
|
||||
{"id": "transparency-art13", "title": "Art. 13 Transparenz", "objectives": ["User-Information", "Logging"]},
|
||||
{"id": "human-oversight", "title": "Art. 14 Menschliche Aufsicht", "objectives": ["Override-Mechanismen"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "timeline-ce",
|
||||
"title": "Timeline & CE-Kennzeichnung",
|
||||
"subtopics": [
|
||||
{"id": "phasen-einfuehrung", "title": "Inkrafttreten-Phasen", "objectives": ["2025/2026/2027-Meilensteine"]},
|
||||
{"id": "ce-marking", "title": "CE-Kennzeichnung für High-Risk", "objectives": ["Konformitäts-Assessment", "Benannte Stellen"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "nist-ai-rmf",
|
||||
"title": "NIST AI RMF",
|
||||
"short": "NIST",
|
||||
"icon": "book",
|
||||
"color": "#0891b2",
|
||||
"description": "Govern, Map, Measure, Manage — US-Framework mit Crosswalk zu EU AI Act und ISO 42001",
|
||||
"modules": [
|
||||
{
|
||||
"id": "vier-funktionen",
|
||||
"title": "Die 4 Kernfunktionen",
|
||||
"subtopics": [
|
||||
{"id": "govern", "title": "Govern", "objectives": ["Organisations-Kultur", "Policies"]},
|
||||
{"id": "map", "title": "Map", "objectives": ["Kontext & Risiken erfassen"]},
|
||||
{"id": "measure", "title": "Measure", "objectives": ["KPIs und Tests"]},
|
||||
{"id": "manage", "title": "Manage", "objectives": ["Priorisieren, Response, Recovery"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "crosswalk",
|
||||
"title": "Crosswalk zu anderen Frameworks",
|
||||
"subtopics": [
|
||||
{"id": "nist-vs-eu-ai-act", "title": "NIST vs EU AI Act", "objectives": ["Überlappungen nutzen"]},
|
||||
{"id": "nist-vs-iso", "title": "NIST vs ISO 42001", "objectives": ["Mapping-Tabellen"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "iso-42001",
|
||||
"title": "ISO/IEC 42001 & 23894",
|
||||
"short": "ISO",
|
||||
"icon": "award",
|
||||
"color": "#0891b2",
|
||||
"description": "AI Management System, AI Risk Management — zertifizierbare Standards seit 2023",
|
||||
"modules": [
|
||||
{
|
||||
"id": "iso-42001-aims",
|
||||
"title": "ISO 42001 — AIMS",
|
||||
"subtopics": [
|
||||
{"id": "aims-struktur", "title": "Was ist ein AIMS", "objectives": ["Management-System-Struktur", "PDCA für KI"]},
|
||||
{"id": "zertifizierungspfad", "title": "Zertifizierungspfad", "objectives": ["Akkreditierte Stellen DACH", "Aufwand schätzen"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "iso-23894",
|
||||
"title": "ISO 23894 — AI Risk Management",
|
||||
"subtopics": [
|
||||
{"id": "risk-assessment", "title": "Risk Assessment Prozess", "objectives": ["Risiko identifizieren/analysieren/bewerten"]},
|
||||
{"id": "komplement-27001", "title": "Komplementarität zu ISO 27001", "objectives": ["Synergie mit ISMS"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "scorecards",
|
||||
"title": "Scorecards & Dashboards",
|
||||
"short": "Scorecards",
|
||||
"icon": "clipboard",
|
||||
"color": "#0891b2",
|
||||
"description": "KPI-Hierarchie Exec/Operational/Technical — vom Dashboard zur Entscheidung",
|
||||
"modules": [
|
||||
{
|
||||
"id": "scorecard-design",
|
||||
"title": "Scorecard-Design",
|
||||
"subtopics": [
|
||||
{"id": "kpi-hierarchie", "title": "Exec-, Operational-, Technical-Layer", "objectives": ["Welche KPI auf welcher Ebene", "Aggregations-Logik"]},
|
||||
{"id": "vanity-metrics", "title": "Vanity-Metrics erkennen", "objectives": ["Anti-Patterns", "Actionability-Test"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dashboarding-tools",
|
||||
"title": "Dashboarding-Tools",
|
||||
"subtopics": [
|
||||
{"id": "tableau-looker-superset", "title": "Tableau, Looker, Superset, Grafana", "objectives": ["Stärken/Schwächen", "Kosten"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "okr-ki",
|
||||
"title": "OKRs für KI-Teams",
|
||||
"short": "OKR",
|
||||
"icon": "target",
|
||||
"color": "#0891b2",
|
||||
"description": "Objectives & Key Results — Balance zwischen Tech-KPIs und Business-Impact",
|
||||
"modules": [
|
||||
{
|
||||
"id": "okr-grundlagen",
|
||||
"title": "OKR-Grundlagen für AI",
|
||||
"subtopics": [
|
||||
{"id": "obj-vs-kr", "title": "Objectives vs Key Results", "objectives": ["Qualitativ vs quantitativ"]},
|
||||
{"id": "balance-tech-biz", "title": "Balance Tech vs Business", "objectives": ["Anti-Pattern: Over-Tech-Indexed"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "beispiele-dach",
|
||||
"title": "Beispiele aus DACH-Mittelstand",
|
||||
"subtopics": [
|
||||
{"id": "mittelstand-cases", "title": "5 Praxisbeispiele", "objectives": ["Formulierungs-Muster"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"badges": [
|
||||
{"id": "erste_metrik", "title": "Erste Metrik-Analyse", "icon": "target", "description": "1. Quiz zu klassischen ML-Metriken bestanden"},
|
||||
{"id": "metrik_master", "title": "Metrik-Master:in", "icon": "trophy", "description": "10 Klassifikations-Metriken-Fragen korrekt"},
|
||||
{"id": "bias_hunter", "title": "Bias-Jäger:in", "icon": "scale", "description": "Bias & Fairness-Modul abgeschlossen"},
|
||||
{"id": "ai_act_navigator", "title": "EU-AI-Act-Navigator:in", "icon": "flag", "description": "EU-AI-Act-Modul abgeschlossen"},
|
||||
{"id": "nist_practitioner", "title": "NIST-Praktiker:in", "icon": "book", "description": "NIST AI RMF-Modul abgeschlossen"},
|
||||
{"id": "iso_expert", "title": "ISO-42001-Expert:in", "icon": "award", "description": "ISO 42001/23894-Modul abgeschlossen"},
|
||||
{"id": "scorecard_architect", "title": "Scorecard-Architekt:in", "icon": "clipboard", "description": "Scorecards-Flashcards bestanden"},
|
||||
{"id": "governance_lead", "title": "Governance-Lead", "icon": "crown", "description": "Reifegrad-Modul abgeschlossen"},
|
||||
{"id": "kpi_master", "title": "KI-Kennzahlen-Master", "icon": "star", "description": "Alle 13 Curricula abgeschlossen"},
|
||||
{"id": "night_owl", "title": "Nachteule", "icon": "moon", "description": "Nach 22 Uhr gelernt"},
|
||||
{"id": "early_bird", "title": "Frühaufsteher:in", "icon": "sun", "description": "Vor 7 Uhr gelernt"}
|
||||
],
|
||||
"levels": [
|
||||
{"min": 0, "title": "Einsteiger:in"},
|
||||
{"min": 50, "title": "Analyst:in"},
|
||||
{"min": 200, "title": "Data-Scientist:in"},
|
||||
{"min": 500, "title": "ML-Engineer:in"},
|
||||
{"min": 1250, "title": "AI-Program-Lead"},
|
||||
{"min": 2500, "title": "Head of AI"},
|
||||
{"min": 5000, "title": "Chief Data/AI-Officer"}
|
||||
]
|
||||
}
|
||||
4
src/levels-fallback.js
Normal file
4
src/levels-fallback.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{ min: 0, title: 'AI-Trainee' }, { min: 50, title: 'AI-Analyst' },
|
||||
{ min: 200, title: 'ML-Engineer' }, { min: 500, title: 'AI-Lead' },
|
||||
{ min: 1250, title: 'AI-Officer' }, { min: 2500, title: 'AI-Governance-Lead' },
|
||||
{ min: 5000, title: 'Chief AI Officer' }
|
||||
29
src/welcome.html
Normal file
29
src/welcome.html
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<h2>Willkommen bei Kai!</h2>
|
||||
<p>Ich bin dein:e Sparringspartner:in für KI-Kennzahlen, Modell-Bewertung, Reifegradmessung und AI-Governance — ruhig, systematisch, zahlenliebend. Warum das Ganze? <strong>Messbar steuern</strong>, <strong>Bias erkennen</strong>, <strong>ROI zeigen</strong>. Läuft im deutschen Bunker, deine Metriken bleiben hier.</p>
|
||||
<div class="mode-grid">
|
||||
<button class="mode-card" data-goto="chat">
|
||||
<strong>Chat</strong>
|
||||
<span>Frag mich zu Klassifikations-Metriken, Fairness, NIST AI RMF, ISO 42001, AI-Reifegrad.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="quiz">
|
||||
<strong>Quiz</strong>
|
||||
<span>Szenario-Fragen aus dem KI-Projekt-Alltag, mit XP.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="flash">
|
||||
<strong>Flashcards</strong>
|
||||
<span>Metriken, Bias-Tests, Reifegrad-Stufen — mit Spaced-Repetition.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="progress">
|
||||
<strong>Fortschritt</strong>
|
||||
<span>XP, Badges, Level vom AI-Analyst bis Governance-Lead.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="curriculum">
|
||||
<strong>Module</strong>
|
||||
<span>13 Themenblöcke / 28 Module — von Klassifikations-Metriken bis Scorecard-Design.</span>
|
||||
</button>
|
||||
<a class="mode-card" href="cockpit/" target="_blank" rel="noopener">
|
||||
<strong>Cockpit ↗</strong>
|
||||
<span>Reifegrad-Assessment, EU-AI-Act-Risk-Classifier, KPI-Dashboard — Spezial-App neben dem Chat.</span>
|
||||
</a>
|
||||
</div>
|
||||
<p style="font-size:.82rem;color:var(--text-mute)">In 3 Sätzen: Chat zum Verstehen → Quiz zum Testen → Flashcards zum Merken. Das Cockpit liefert dir Reifegrad-Score und AI-Act-Klassifizierung. Brauchst du operative AI-Act-Tiefe? → ruf KURT. AI-Act-Audit-Trail? → VESTIGIA.</p>
|
||||
1753
www/app.js
Normal file
1753
www/app.js
Normal file
File diff suppressed because it is too large
Load diff
705
www/cockpit/cockpit.css
Normal file
705
www/cockpit/cockpit.css
Normal file
|
|
@ -0,0 +1,705 @@
|
|||
/* Kai Cockpit — vanilla CSS, no external fonts/CDN.
|
||||
Extends Kai widget theme: teal #0891b2/#06b6d4, bg #0a0a0f, text #f1f0f5. */
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg: #0a0a0f;
|
||||
--bg-elev: #12121a;
|
||||
--bg-elev2: #191924;
|
||||
--bg-elev3: #22222f;
|
||||
--line: #2a2a38;
|
||||
--line-soft: #1f1f2b;
|
||||
--text: #f1f0f5;
|
||||
--text-mute: #a0a0b5;
|
||||
--text-dim: #707088;
|
||||
--teal: #0891b2;
|
||||
--teal-b: #06b6d4;
|
||||
--teal-c: #22d3ee;
|
||||
--teal-dim: rgba(8,145,178,.15);
|
||||
--ok: #10b981;
|
||||
--warn: #f59e0b;
|
||||
--err: #ef4444;
|
||||
--violet: #a78bfa;
|
||||
--dock-w: 300px;
|
||||
--nav-w: 220px;
|
||||
--radius: 10px;
|
||||
--radius-sm: 6px;
|
||||
--shadow: 0 8px 24px rgba(0,0,0,.45);
|
||||
--t: 0.2s ease;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
min-height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a { color: var(--teal-c); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
button { font-family: inherit; font-size: inherit; }
|
||||
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: #0d0d14; padding: 1px 5px; border-radius: 4px; border: 1px solid var(--line-soft); font-size: .9em; }
|
||||
|
||||
/* ============ LAYOUT ============ */
|
||||
.cockpit {
|
||||
display: grid;
|
||||
grid-template-columns: var(--nav-w) minmax(0, 1fr) var(--dock-w);
|
||||
grid-template-rows: auto 1fr auto;
|
||||
grid-template-areas:
|
||||
"top top top"
|
||||
"nav main dock"
|
||||
"foot foot foot";
|
||||
min-height: 100vh;
|
||||
background: radial-gradient(ellipse at top, rgba(8,145,178,.08), transparent 60%), var(--bg);
|
||||
}
|
||||
.topbar { grid-area: top; }
|
||||
.nav { grid-area: nav; }
|
||||
.main { grid-area: main; }
|
||||
.chat-dock { grid-area: dock; }
|
||||
.footer { grid-area: foot; }
|
||||
|
||||
/* ============ TOPBAR ============ */
|
||||
.topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 14px 22px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: linear-gradient(180deg, rgba(18,18,26,.92), rgba(10,10,15,.92));
|
||||
backdrop-filter: blur(6px);
|
||||
position: sticky; top: 0; z-index: 50;
|
||||
}
|
||||
.brand { display: flex; align-items: center; gap: 12px; }
|
||||
.brand-icon {
|
||||
width: 36px; height: 36px; border-radius: 9px;
|
||||
background: linear-gradient(135deg, var(--teal), var(--teal-b));
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-weight: 700; font-size: 18px; color: #001013;
|
||||
box-shadow: 0 0 0 1px rgba(34,211,238,.25), 0 6px 14px rgba(8,145,178,.35);
|
||||
}
|
||||
.brand-text { display: flex; flex-direction: column; line-height: 1.1; }
|
||||
.brand-title { font-weight: 600; font-size: 16px; letter-spacing: -.2px; }
|
||||
.brand-title small { font-weight: 400; color: var(--text-mute); font-size: 13px; margin-left: 2px; }
|
||||
.brand-sub { color: var(--text-dim); font-size: 11px; letter-spacing: .04em; text-transform: uppercase; }
|
||||
.spacer { flex: 1; }
|
||||
|
||||
.xp-wrap { display: flex; flex-direction: column; gap: 4px; min-width: 170px; }
|
||||
.xp-row { display: flex; justify-content: space-between; font-size: 12px; color: var(--text-mute); }
|
||||
.xp-level { color: var(--teal-c); font-weight: 500; }
|
||||
.xp-score { color: var(--text-mute); }
|
||||
.xp-bar { height: 5px; background: var(--bg-elev2); border-radius: 99px; overflow: hidden; border: 1px solid var(--line-soft); }
|
||||
.xp-bar-fill { height: 100%; width: 0%; background: linear-gradient(90deg, var(--teal), var(--teal-c)); transition: width .4s ease; }
|
||||
|
||||
.back-link {
|
||||
padding: 7px 14px; border: 1px solid var(--line);
|
||||
border-radius: 8px; font-size: 13px; color: var(--text-mute);
|
||||
transition: var(--t);
|
||||
}
|
||||
.back-link:hover { color: var(--text); border-color: var(--teal); background: var(--teal-dim); text-decoration: none; }
|
||||
|
||||
/* ============ NAV ============ */
|
||||
.nav {
|
||||
border-right: 1px solid var(--line);
|
||||
padding: 22px 14px;
|
||||
display: flex; flex-direction: column; gap: 4px;
|
||||
background: rgba(18,18,26,.5);
|
||||
}
|
||||
.nav-item {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
color: var(--text-mute);
|
||||
padding: 12px 14px;
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: var(--t);
|
||||
font-size: 14px;
|
||||
}
|
||||
.nav-item:hover { background: var(--bg-elev); color: var(--text); }
|
||||
.nav-item[aria-selected="true"] {
|
||||
background: linear-gradient(90deg, rgba(8,145,178,.2), rgba(8,145,178,.06));
|
||||
border-color: rgba(34,211,238,.35);
|
||||
color: var(--text);
|
||||
box-shadow: inset 2px 0 0 var(--teal-c);
|
||||
}
|
||||
.nav-num { font-variant-numeric: tabular-nums; font-size: 11px; color: var(--text-dim); letter-spacing: .08em; }
|
||||
.nav-item[aria-selected="true"] .nav-num { color: var(--teal-c); }
|
||||
.nav-label { font-weight: 500; letter-spacing: -.2px; }
|
||||
.nav-kbd { font-size: 10px; color: var(--text-dim); background: var(--bg-elev2); padding: 2px 5px; border-radius: 4px; border: 1px solid var(--line-soft); }
|
||||
|
||||
/* ============ MAIN ============ */
|
||||
.main { padding: 28px 36px 40px; min-width: 0; }
|
||||
.module { display: none; max-width: 1100px; margin: 0 auto; }
|
||||
.module[data-active="true"] { display: block; animation: fadeIn .25s ease; }
|
||||
@keyframes fadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
|
||||
|
||||
.mod-head { margin-bottom: 28px; }
|
||||
.mod-head h1 {
|
||||
margin: 0 0 8px; font-size: 28px; font-weight: 600; letter-spacing: -0.5px;
|
||||
background: linear-gradient(90deg, var(--text), var(--teal-c));
|
||||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||||
}
|
||||
.mod-head p { margin: 0; color: var(--text-mute); max-width: 720px; font-size: 14px; }
|
||||
.mod-badge {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
font-size: 11px; font-weight: 500; letter-spacing: .05em; text-transform: uppercase;
|
||||
color: var(--teal-c); background: var(--teal-dim);
|
||||
border: 1px solid rgba(34,211,238,.25);
|
||||
padding: 4px 10px; border-radius: 99px; margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* Cards + surfaces */
|
||||
.card {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 20px;
|
||||
transition: var(--t);
|
||||
}
|
||||
.card-hover:hover { border-color: rgba(34,211,238,.4); transform: translateY(-1px); cursor: pointer; }
|
||||
|
||||
.btn {
|
||||
padding: 9px 16px; border-radius: 8px;
|
||||
font-weight: 500; cursor: pointer;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--bg-elev2); color: var(--text);
|
||||
transition: var(--t); font-size: 14px;
|
||||
}
|
||||
.btn:hover:not(:disabled) { border-color: var(--teal); color: var(--teal-c); }
|
||||
.btn:disabled { opacity: .4; cursor: not-allowed; }
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--teal), var(--teal-b));
|
||||
border: none; color: #001013; font-weight: 600;
|
||||
}
|
||||
.btn-primary:hover:not(:disabled) { filter: brightness(1.15); color: #001013; }
|
||||
.btn-ghost { background: transparent; border: 1px solid var(--line); }
|
||||
.btn-sm { padding: 6px 12px; font-size: 13px; }
|
||||
.btn-ask-kai {
|
||||
background: transparent;
|
||||
border: 1px dashed rgba(34,211,238,.4);
|
||||
color: var(--teal-c);
|
||||
padding: 8px 14px; font-size: 13px;
|
||||
border-radius: 8px; cursor: pointer; transition: var(--t);
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.btn-ask-kai:hover { background: var(--teal-dim); border-style: solid; }
|
||||
|
||||
/* ============ REIFEGRAD ============ */
|
||||
.reifegrad-intro {
|
||||
display: grid; gap: 10px; margin-bottom: 20px;
|
||||
padding: 16px 20px;
|
||||
background: linear-gradient(135deg, rgba(8,145,178,.08), rgba(18,18,26,.6));
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
.reifegrad-intro .intro-stats {
|
||||
display: flex; gap: 16px; flex-wrap: wrap;
|
||||
font-size: 12px; color: var(--text-mute);
|
||||
}
|
||||
.reifegrad-intro .intro-stats span strong { color: var(--teal-c); font-variant-numeric: tabular-nums; margin-right: 4px; }
|
||||
|
||||
.dim-grid {
|
||||
display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 14px; margin-bottom: 24px;
|
||||
}
|
||||
.dim-card {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 16px 18px;
|
||||
cursor: pointer; transition: var(--t);
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.dim-card:hover { border-color: rgba(34,211,238,.35); transform: translateY(-1px); }
|
||||
.dim-card.done { border-color: rgba(16,185,129,.45); }
|
||||
.dim-card.done::after {
|
||||
content: "✓"; position: absolute; top: 10px; right: 12px;
|
||||
color: var(--ok); font-size: 14px; font-weight: 700;
|
||||
}
|
||||
.dim-card h3 { margin: 0 0 6px; font-size: 15px; font-weight: 600; color: var(--text); }
|
||||
.dim-card p { margin: 0; color: var(--text-mute); font-size: 12.5px; line-height: 1.4; }
|
||||
.dim-card .dim-score {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
margin-top: 10px; font-size: 12px;
|
||||
color: var(--teal-c); font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.dim-card .dim-score .mini-bar {
|
||||
flex: 1; height: 4px; background: var(--bg-elev3); border-radius: 2px; overflow: hidden;
|
||||
}
|
||||
.dim-card .dim-score .mini-bar-fill {
|
||||
height: 100%; background: linear-gradient(90deg, var(--teal), var(--teal-c));
|
||||
transition: width .3s ease;
|
||||
}
|
||||
|
||||
/* Assessment question view */
|
||||
.assessment {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 24px 28px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.assessment-nav {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
margin-bottom: 20px; font-size: 12px; color: var(--text-mute);
|
||||
}
|
||||
.assessment-nav button.linklike {
|
||||
background: transparent; border: none; color: var(--teal-c); cursor: pointer; font-size: 13px;
|
||||
}
|
||||
.assessment-nav button.linklike:hover { text-decoration: underline; }
|
||||
.assessment-progress {
|
||||
height: 4px; background: var(--bg-elev3); border-radius: 99px; overflow: hidden; margin-bottom: 22px;
|
||||
}
|
||||
.assessment-progress-fill { height: 100%; background: linear-gradient(90deg, var(--teal), var(--teal-c)); transition: width .3s ease; }
|
||||
.question-block h2 { margin: 0 0 6px; font-size: 20px; font-weight: 600; letter-spacing: -.3px; }
|
||||
.question-block .question-hint { color: var(--text-mute); font-size: 13px; margin-bottom: 22px; }
|
||||
.likert {
|
||||
display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.likert-btn {
|
||||
background: var(--bg-elev2);
|
||||
border: 1px solid var(--line);
|
||||
color: var(--text); padding: 14px 10px;
|
||||
border-radius: 8px; cursor: pointer; transition: var(--t);
|
||||
display: flex; flex-direction: column; gap: 4px; text-align: center;
|
||||
}
|
||||
.likert-btn:hover { border-color: var(--teal); }
|
||||
.likert-btn[aria-pressed="true"] {
|
||||
background: linear-gradient(135deg, rgba(8,145,178,.3), rgba(8,145,178,.1));
|
||||
border-color: var(--teal-c); color: var(--teal-c);
|
||||
}
|
||||
.likert-btn .likert-n { font-size: 20px; font-weight: 600; font-variant-numeric: tabular-nums; }
|
||||
.likert-btn .likert-t { font-size: 11px; color: var(--text-mute); }
|
||||
.likert-btn[aria-pressed="true"] .likert-t { color: var(--teal-c); }
|
||||
|
||||
.q-nav { display: flex; justify-content: space-between; margin-top: 10px; gap: 10px; }
|
||||
|
||||
/* Radar + results */
|
||||
.radar-wrap {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 24px;
|
||||
display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 30px;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.radar-legend { display: flex; flex-direction: column; gap: 10px; }
|
||||
.radar-legend .legend-row { display: flex; align-items: center; gap: 10px; font-size: 13px; }
|
||||
.radar-legend .legend-swatch { width: 14px; height: 14px; border-radius: 3px; flex-shrink: 0; }
|
||||
.radar-score-big { font-size: 36px; font-weight: 600; color: var(--teal-c); margin: 4px 0 2px; font-variant-numeric: tabular-nums; }
|
||||
.radar-score-hint { color: var(--text-mute); font-size: 12px; margin-bottom: 16px; }
|
||||
|
||||
.recos { display: grid; gap: 12px; margin-top: 14px; }
|
||||
.reco-item {
|
||||
display: grid; grid-template-columns: auto 1fr; gap: 14px;
|
||||
background: var(--bg-elev); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 16px 18px; align-items: start;
|
||||
}
|
||||
.reco-num { width: 30px; height: 30px; border-radius: 50%; background: var(--teal-dim); color: var(--teal-c); display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 14px; }
|
||||
.reco-item h4 { margin: 0 0 4px; font-size: 14px; font-weight: 600; }
|
||||
.reco-item p { margin: 0 0 10px; color: var(--text-mute); font-size: 13px; }
|
||||
|
||||
/* ============ AI ACT CLASSIFIER ============ */
|
||||
.wizard {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 28px; max-width: 720px; margin: 0 auto;
|
||||
}
|
||||
.wizard .progress-dots {
|
||||
display: flex; gap: 6px; justify-content: center; margin-bottom: 22px;
|
||||
}
|
||||
.wizard .progress-dots span {
|
||||
width: 8px; height: 8px; border-radius: 50%; background: var(--bg-elev3); transition: var(--t);
|
||||
}
|
||||
.wizard .progress-dots span.active { background: var(--teal-c); }
|
||||
.wizard .progress-dots span.done { background: var(--teal); }
|
||||
|
||||
.wizard-q h2 { margin: 0 0 8px; font-size: 22px; font-weight: 600; letter-spacing: -.3px; }
|
||||
.wizard-q p { margin: 0 0 22px; color: var(--text-mute); font-size: 14px; }
|
||||
|
||||
.yesno-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||||
.yesno-btn {
|
||||
padding: 22px; border-radius: var(--radius);
|
||||
background: var(--bg-elev2);
|
||||
border: 1px solid var(--line);
|
||||
color: var(--text); cursor: pointer; transition: var(--t);
|
||||
font-size: 16px; font-weight: 500;
|
||||
}
|
||||
.yesno-btn:hover { border-color: var(--teal); transform: translateY(-1px); }
|
||||
.yesno-btn.yes:hover { border-color: var(--err); color: var(--err); }
|
||||
.yesno-btn.no:hover { border-color: var(--teal-c); color: var(--teal-c); }
|
||||
|
||||
.verdict {
|
||||
padding: 28px; border-radius: var(--radius);
|
||||
border: 1px solid var(--line);
|
||||
background: var(--bg-elev);
|
||||
}
|
||||
.verdict .verdict-label {
|
||||
display: inline-block; padding: 4px 12px; border-radius: 99px;
|
||||
font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.verdict.prohibited { border-color: var(--err); background: rgba(239,68,68,.06); }
|
||||
.verdict.prohibited .verdict-label { background: rgba(239,68,68,.18); color: var(--err); }
|
||||
.verdict.high { border-color: var(--warn); background: rgba(245,158,11,.06); }
|
||||
.verdict.high .verdict-label { background: rgba(245,158,11,.18); color: var(--warn); }
|
||||
.verdict.limited { border-color: var(--teal); background: rgba(8,145,178,.06); }
|
||||
.verdict.limited .verdict-label { background: rgba(8,145,178,.2); color: var(--teal-c); }
|
||||
.verdict.minimal { border-color: var(--ok); background: rgba(16,185,129,.06); }
|
||||
.verdict.minimal .verdict-label { background: rgba(16,185,129,.18); color: var(--ok); }
|
||||
.verdict h2 { margin: 0 0 10px; font-size: 26px; font-weight: 600; }
|
||||
.verdict p.verdict-summary { margin: 0 0 18px; color: var(--text-mute); }
|
||||
.verdict .obligation-list {
|
||||
list-style: none; padding: 0; margin: 0 0 22px; display: grid; gap: 10px;
|
||||
}
|
||||
.verdict .obligation-list li {
|
||||
padding: 10px 14px; background: rgba(255,255,255,.02);
|
||||
border-left: 3px solid var(--teal-c);
|
||||
border-radius: 4px; font-size: 13px; color: var(--text);
|
||||
}
|
||||
.verdict .obligation-list li strong { color: var(--teal-c); margin-right: 8px; }
|
||||
.verdict-run-counter { color: var(--text-dim); font-size: 12px; margin-top: 14px; }
|
||||
|
||||
/* ============ DASHBOARD ============ */
|
||||
.dash-group { margin-bottom: 26px; }
|
||||
.dash-group-title {
|
||||
font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
|
||||
color: var(--text-dim); margin-bottom: 10px;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.dash-group-title::after { content: ""; flex: 1; height: 1px; background: var(--line); }
|
||||
.dash-grid { display: grid; gap: 14px; }
|
||||
.dash-grid.exec { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
|
||||
.dash-grid.ops { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
|
||||
.dash-grid.tech { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
|
||||
.tile {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 18px 20px; cursor: pointer; transition: var(--t);
|
||||
text-align: left; display: flex; flex-direction: column; gap: 6px;
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.tile:hover { border-color: rgba(34,211,238,.4); transform: translateY(-2px); box-shadow: var(--shadow); }
|
||||
.tile.seen { border-color: rgba(34,211,238,.25); }
|
||||
.tile.seen::before {
|
||||
content: ""; position: absolute; top: 10px; right: 12px;
|
||||
width: 6px; height: 6px; border-radius: 50%; background: var(--teal-c);
|
||||
}
|
||||
.tile.exec { padding: 22px 24px; }
|
||||
.tile-label { font-size: 12px; color: var(--text-mute); letter-spacing: .02em; }
|
||||
.tile-value { font-size: 26px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; letter-spacing: -0.5px; }
|
||||
.tile.exec .tile-value { font-size: 34px; }
|
||||
.tile-trend {
|
||||
font-size: 11px; display: flex; align-items: center; gap: 4px;
|
||||
color: var(--text-mute); font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.tile-trend.up { color: var(--ok); }
|
||||
.tile-trend.down { color: var(--err); }
|
||||
.tile-trend.neutral { color: var(--text-mute); }
|
||||
|
||||
/* ============ MODAL ============ */
|
||||
.modal-root {
|
||||
position: fixed; inset: 0; z-index: 200;
|
||||
display: none; align-items: center; justify-content: center;
|
||||
padding: 20px; background: rgba(0,0,0,.7); backdrop-filter: blur(6px);
|
||||
}
|
||||
.modal-root.open { display: flex; animation: fadeIn .2s; }
|
||||
.modal {
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
padding: 26px 28px;
|
||||
max-width: 560px; width: 100%; max-height: 90vh; overflow-y: auto;
|
||||
box-shadow: var(--shadow);
|
||||
animation: modalIn .2s ease;
|
||||
}
|
||||
@keyframes modalIn { from { transform: translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
|
||||
.modal-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 14px; gap: 16px; }
|
||||
.modal-head h3 { margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -.3px; }
|
||||
.modal-head .modal-close { background: transparent; border: none; color: var(--text-mute); font-size: 22px; cursor: pointer; line-height: 1; }
|
||||
.modal-head .modal-close:hover { color: var(--text); }
|
||||
.modal-value {
|
||||
font-size: 36px; font-weight: 600; color: var(--teal-c);
|
||||
margin-bottom: 4px; font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.modal-value-sub { color: var(--text-mute); font-size: 13px; margin-bottom: 18px; }
|
||||
.modal p { margin: 0 0 14px; font-size: 13.5px; color: var(--text-mute); line-height: 1.5; }
|
||||
.modal p strong { color: var(--text); }
|
||||
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; flex-wrap: wrap; }
|
||||
|
||||
/* ============ CHAT DOCK ============ */
|
||||
.chat-dock {
|
||||
border-left: 1px solid var(--line);
|
||||
background: rgba(18,18,26,.6);
|
||||
display: flex; flex-direction: column;
|
||||
position: sticky; top: 64px; height: calc(100vh - 64px - 44px);
|
||||
overflow: hidden;
|
||||
}
|
||||
.dock-head {
|
||||
display: flex; align-items: baseline; gap: 8px;
|
||||
padding: 14px 18px; border-bottom: 1px solid var(--line);
|
||||
background: rgba(8,145,178,.06);
|
||||
}
|
||||
.dock-title { font-weight: 600; color: var(--teal-c); }
|
||||
.dock-sub { font-size: 11px; color: var(--text-dim); flex: 1; }
|
||||
.dock-reset, .dock-collapse {
|
||||
background: transparent; border: 1px solid var(--line);
|
||||
color: var(--text-mute); width: 26px; height: 26px;
|
||||
border-radius: 6px; cursor: pointer; font-size: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: var(--t);
|
||||
}
|
||||
.dock-reset:hover, .dock-collapse:hover { color: var(--text); border-color: var(--teal); }
|
||||
.dock-box {
|
||||
flex: 1; overflow-y: auto; padding: 14px;
|
||||
display: flex; flex-direction: column; gap: 10px;
|
||||
scrollbar-width: thin; scrollbar-color: var(--line) transparent;
|
||||
}
|
||||
.dock-box::-webkit-scrollbar { width: 6px; }
|
||||
.dock-box::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }
|
||||
.dock-msg {
|
||||
padding: 10px 12px; border-radius: 10px;
|
||||
font-size: 13px; line-height: 1.5; max-width: 95%;
|
||||
word-wrap: break-word; overflow-wrap: break-word;
|
||||
}
|
||||
.dock-msg.user {
|
||||
align-self: flex-end;
|
||||
background: linear-gradient(135deg, var(--teal), var(--teal-b));
|
||||
color: #001013;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
.dock-msg.bot {
|
||||
align-self: flex-start;
|
||||
background: var(--bg-elev2);
|
||||
border: 1px solid var(--line);
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
.dock-msg.bot p { margin: 0 0 8px; }
|
||||
.dock-msg.bot p:last-child { margin-bottom: 0; }
|
||||
.dock-msg.bot ul, .dock-msg.bot ol { margin: 6px 0; padding-left: 20px; }
|
||||
.dock-msg.bot li { margin-bottom: 2px; }
|
||||
.dock-msg.bot code { font-size: .88em; }
|
||||
.dock-msg.bot pre { background: #0d0d14; border: 1px solid var(--line-soft); padding: 10px; border-radius: 6px; overflow-x: auto; font-size: .85em; margin: 8px 0; }
|
||||
.dock-msg.err {
|
||||
background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); color: #fca5a5;
|
||||
}
|
||||
.dock-msg.sys {
|
||||
font-size: 12px; color: var(--text-dim);
|
||||
background: transparent; border: 1px dashed var(--line); align-self: center; text-align: center;
|
||||
}
|
||||
|
||||
.dots { display: inline-flex; gap: 4px; padding: 4px 0; }
|
||||
.dots span {
|
||||
width: 6px; height: 6px; border-radius: 50%; background: var(--teal-c);
|
||||
animation: bounce 1.2s infinite ease-in-out;
|
||||
}
|
||||
.dots span:nth-child(2) { animation-delay: .15s; }
|
||||
.dots span:nth-child(3) { animation-delay: .3s; }
|
||||
@keyframes bounce { 0%, 80%, 100% { transform: translateY(0); opacity: .4; } 40% { transform: translateY(-4px); opacity: 1; } }
|
||||
|
||||
.dock-form {
|
||||
display: grid; grid-template-columns: 1fr auto;
|
||||
gap: 8px; padding: 12px; border-top: 1px solid var(--line);
|
||||
background: var(--bg-elev);
|
||||
}
|
||||
.dock-form textarea {
|
||||
resize: none; min-height: 38px; max-height: 140px;
|
||||
background: var(--bg-elev2);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px; color: var(--text);
|
||||
padding: 9px 12px; font-family: inherit; font-size: 13px;
|
||||
transition: var(--t);
|
||||
}
|
||||
.dock-form textarea:focus { outline: none; border-color: var(--teal-c); box-shadow: 0 0 0 3px rgba(34,211,238,.15); }
|
||||
.btn-send {
|
||||
background: linear-gradient(135deg, var(--teal), var(--teal-b));
|
||||
border: none; color: #001013; font-weight: 700;
|
||||
width: 40px; height: 40px; border-radius: 8px; cursor: pointer;
|
||||
font-size: 18px; transition: var(--t);
|
||||
}
|
||||
.btn-send:hover:not(:disabled) { filter: brightness(1.15); }
|
||||
.btn-send:disabled { opacity: .4; cursor: not-allowed; }
|
||||
.dock-footer-link {
|
||||
display: block; padding: 8px 14px; font-size: 11px; color: var(--text-dim);
|
||||
text-align: center; border-top: 1px solid var(--line); background: var(--bg-elev);
|
||||
}
|
||||
.dock-footer-link:hover { color: var(--teal-c); text-decoration: none; }
|
||||
|
||||
/* Floating open button (mobile / collapsed state) */
|
||||
.dock-open {
|
||||
display: none;
|
||||
position: fixed; bottom: 24px; right: 24px; z-index: 60;
|
||||
padding: 12px 18px; border-radius: 99px;
|
||||
background: linear-gradient(135deg, var(--teal), var(--teal-b));
|
||||
color: #001013; font-weight: 600; border: none;
|
||||
box-shadow: 0 8px 24px rgba(8,145,178,.5);
|
||||
cursor: pointer; font-size: 14px;
|
||||
align-items: center; gap: 8px;
|
||||
}
|
||||
.dock-open-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #001013; animation: pulse 1.4s infinite; }
|
||||
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
|
||||
.chat-dock.collapsed { display: none; }
|
||||
.chat-dock.collapsed + .dock-open { display: inline-flex; }
|
||||
|
||||
/* Full chat module (⌃4) — reuses dock style but takes full width */
|
||||
.chat-full-wrap { max-width: 780px; margin: 0 auto; }
|
||||
.chat-full-wrap p { color: var(--text-mute); font-size: 13px; margin-bottom: 18px; }
|
||||
.chat-full-wrap .chat-full-empty {
|
||||
display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 10px; margin: 16px 0 22px;
|
||||
}
|
||||
.chat-full-wrap .chip {
|
||||
background: var(--bg-elev); border: 1px solid var(--line);
|
||||
color: var(--text-mute); padding: 10px 14px; border-radius: 8px;
|
||||
cursor: pointer; transition: var(--t); font-size: 13px; text-align: left;
|
||||
}
|
||||
.chat-full-wrap .chip:hover { border-color: var(--teal); color: var(--text); }
|
||||
|
||||
/* ============ FOOTER ============ */
|
||||
.footer {
|
||||
grid-area: foot;
|
||||
padding: 12px 22px;
|
||||
border-top: 1px solid var(--line);
|
||||
text-align: center;
|
||||
font-size: 12px; color: var(--text-dim);
|
||||
background: var(--bg-elev);
|
||||
}
|
||||
.footer a { color: var(--text-mute); }
|
||||
|
||||
/* ============ TOASTS ============ */
|
||||
.toast-stack {
|
||||
position: fixed; top: 16px; right: 16px; z-index: 500;
|
||||
display: flex; flex-direction: column; gap: 8px; pointer-events: none;
|
||||
}
|
||||
.toast {
|
||||
padding: 10px 16px; border-radius: 8px;
|
||||
background: var(--bg-elev2); border: 1px solid var(--line);
|
||||
color: var(--text); font-size: 13px; min-width: 220px;
|
||||
box-shadow: var(--shadow); animation: slideIn .3s ease;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.toast.success { border-color: var(--ok); color: #86efac; }
|
||||
.toast.err { border-color: var(--err); color: #fca5a5; }
|
||||
.toast.info { border-color: var(--teal-c); color: var(--teal-c); }
|
||||
@keyframes slideIn { from { transform: translateX(20px); opacity: 0; } to { transform: none; opacity: 1; } }
|
||||
|
||||
/* ============ BADGES (completion) ============ */
|
||||
.badges-row {
|
||||
display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px;
|
||||
}
|
||||
.badge-chip {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
padding: 6px 12px; border-radius: 99px;
|
||||
background: var(--bg-elev2); border: 1px solid var(--line);
|
||||
font-size: 12px; color: var(--text-mute);
|
||||
}
|
||||
.badge-chip.earned { border-color: var(--teal-c); color: var(--teal-c); background: var(--teal-dim); }
|
||||
.badge-chip .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-dim); }
|
||||
.badge-chip.earned .dot { background: var(--teal-c); box-shadow: 0 0 6px var(--teal-c); }
|
||||
|
||||
/* ============ RESPONSIVE ============ */
|
||||
@media (max-width: 1160px) {
|
||||
.cockpit { grid-template-columns: 180px minmax(0, 1fr) 260px; --nav-w: 180px; --dock-w: 260px; }
|
||||
.main { padding: 22px 24px 36px; }
|
||||
}
|
||||
@media (max-width: 960px) {
|
||||
.cockpit {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas: "top" "nav" "main" "foot";
|
||||
}
|
||||
.nav {
|
||||
flex-direction: row; overflow-x: auto; padding: 12px;
|
||||
border-right: none; border-bottom: 1px solid var(--line);
|
||||
position: sticky; top: 70px; z-index: 30;
|
||||
background: rgba(10,10,15,.95); backdrop-filter: blur(8px);
|
||||
}
|
||||
.nav-item { flex: 1 0 auto; grid-template-columns: 1fr; gap: 2px; text-align: center; padding: 8px 12px; }
|
||||
.nav-num, .nav-kbd { display: none; }
|
||||
.nav-label { font-size: 13px; }
|
||||
.chat-dock {
|
||||
position: fixed; top: 0; right: 0; bottom: 0;
|
||||
width: 100%; max-width: 360px;
|
||||
height: 100vh; z-index: 100;
|
||||
box-shadow: -10px 0 40px rgba(0,0,0,.6);
|
||||
transform: translateX(100%); transition: transform .3s ease;
|
||||
}
|
||||
.chat-dock.open { transform: translateX(0); }
|
||||
.dock-open { display: inline-flex; }
|
||||
.radar-wrap { grid-template-columns: 1fr; }
|
||||
.yesno-row { grid-template-columns: 1fr; }
|
||||
.topbar { flex-wrap: wrap; padding: 10px 14px; }
|
||||
.xp-wrap { min-width: 140px; order: 3; }
|
||||
.back-link { order: 2; }
|
||||
.main { padding: 18px 14px 30px; }
|
||||
.mod-head h1 { font-size: 22px; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.likert { grid-template-columns: repeat(5, 1fr); gap: 4px; }
|
||||
.likert-btn { padding: 10px 4px; }
|
||||
.likert-btn .likert-n { font-size: 16px; }
|
||||
.likert-btn .likert-t { font-size: 9px; }
|
||||
.brand-sub { display: none; }
|
||||
}
|
||||
|
||||
/* ============ A11Y ============ */
|
||||
.sr-only {
|
||||
position: absolute; width: 1px; height: 1px;
|
||||
padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); border: 0;
|
||||
}
|
||||
button:focus-visible, .nav-item:focus-visible, textarea:focus-visible, a:focus-visible {
|
||||
outline: 2px solid var(--teal-c); outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* SVG */
|
||||
.radar-svg { width: 100%; height: auto; display: block; }
|
||||
.sparkline-svg { width: 100%; height: 80px; display: block; }
|
||||
|
||||
/* GFM table (added 2026-04-24) */
|
||||
.md-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: .6rem 0;
|
||||
font-size: .9em;
|
||||
background: rgba(255,255,255,0.02);
|
||||
border: 1px solid rgba(255,255,255,0.08);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.md-table thead {
|
||||
background: rgba(8,145,178,0.12);
|
||||
}
|
||||
.md-table th,
|
||||
.md-table td {
|
||||
padding: .5rem .7rem;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
.md-table th {
|
||||
color: #06b6d4;
|
||||
font-weight: 600;
|
||||
font-size: .78em;
|
||||
letter-spacing: .04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.md-table tbody tr:last-child td { border-bottom: none; }
|
||||
.md-table tbody tr:hover { background: rgba(255,255,255,0.03); }
|
||||
.md-table code { font-size: .92em; padding: 1px 5px; }
|
||||
.chat-dock .md-table,
|
||||
.dock-body .md-table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
font-size: .82em;
|
||||
}
|
||||
1249
www/cockpit/cockpit.js
Normal file
1249
www/cockpit/cockpit.js
Normal file
File diff suppressed because it is too large
Load diff
102
www/cockpit/index.html
Normal file
102
www/cockpit/index.html
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Kai Cockpit · KI-Governance, Reifegrad, EU AI Act</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||||
<meta name="theme-color" content="#0a0a0f">
|
||||
<meta name="description" content="Kai Cockpit — Reifegrad-Assessment, EU AI Act Risk-Classifier, KPI-Dashboard und integrierter Coach-Chat. Sovereign AI aus dem deutschen Bunker.">
|
||||
<link rel="stylesheet" href="cockpit.css">
|
||||
<script>window.__KAI_KEY__ = 'qb_qj0ahcfv6coz';</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cockpit" role="application" aria-label="Kai Cockpit">
|
||||
|
||||
<header class="topbar">
|
||||
<div class="brand">
|
||||
<span class="brand-icon" aria-hidden="true">K</span>
|
||||
<div class="brand-text">
|
||||
<span class="brand-title">Kai <small>Cockpit</small></span>
|
||||
<span class="brand-sub">KI-Governance · Reifegrad · AI Act</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="xp-wrap" title="Deine Erfahrung — geteilt mit dem Kai-Widget">
|
||||
<div class="xp-row">
|
||||
<span id="xp-level" class="xp-level">Lvl 1 · AI-Trainee</span>
|
||||
<span id="xp-score" class="xp-score">0 XP</span>
|
||||
</div>
|
||||
<div class="xp-bar"><div id="xp-bar-fill" class="xp-bar-fill"></div></div>
|
||||
</div>
|
||||
<a class="back-link" href="/" title="Zurück zum Kai-Widget">Widget ›</a>
|
||||
</header>
|
||||
|
||||
<nav class="nav" role="tablist" aria-label="Cockpit-Module">
|
||||
<button class="nav-item" role="tab" aria-selected="true" data-module="reifegrad">
|
||||
<span class="nav-num">01</span>
|
||||
<span class="nav-label">Reifegrad</span>
|
||||
<span class="nav-kbd">⌃1</span>
|
||||
</button>
|
||||
<button class="nav-item" role="tab" aria-selected="false" data-module="aiact">
|
||||
<span class="nav-num">02</span>
|
||||
<span class="nav-label">AI-Act-Classifier</span>
|
||||
<span class="nav-kbd">⌃2</span>
|
||||
</button>
|
||||
<button class="nav-item" role="tab" aria-selected="false" data-module="dashboard">
|
||||
<span class="nav-num">03</span>
|
||||
<span class="nav-label">KPI-Dashboard</span>
|
||||
<span class="nav-kbd">⌃3</span>
|
||||
</button>
|
||||
<button class="nav-item" role="tab" aria-selected="false" data-module="chat">
|
||||
<span class="nav-num">04</span>
|
||||
<span class="nav-label">Kai-Chat</span>
|
||||
<span class="nav-kbd">⌃4</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<main class="main">
|
||||
<section id="mod-reifegrad" class="module" data-active="true" aria-labelledby="h-reifegrad">
|
||||
<div id="reifegrad-host"></div>
|
||||
</section>
|
||||
<section id="mod-aiact" class="module" aria-labelledby="h-aiact">
|
||||
<div id="aiact-host"></div>
|
||||
</section>
|
||||
<section id="mod-dashboard" class="module" aria-labelledby="h-dashboard">
|
||||
<div id="dashboard-host"></div>
|
||||
</section>
|
||||
<section id="mod-chat" class="module" aria-labelledby="h-chatfull">
|
||||
<div id="chat-full-host"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<aside id="chat-dock" class="chat-dock" aria-label="Kai Chat-Dock">
|
||||
<header class="dock-head">
|
||||
<span class="dock-title">Kai</span>
|
||||
<span class="dock-sub">dein Coach</span>
|
||||
<button class="dock-reset" type="button" id="dock-reset" title="Chat zurücksetzen" aria-label="Chat zurücksetzen">↺</button>
|
||||
<button class="dock-collapse" type="button" id="dock-collapse" title="Einklappen" aria-label="Einklappen">✕</button>
|
||||
</header>
|
||||
<div id="dock-box" class="dock-box" aria-live="polite"></div>
|
||||
<form id="dock-form" class="dock-form" aria-label="Kai fragen">
|
||||
<textarea id="dock-input" rows="1" placeholder="Frag Kai — Ctrl+K fokussiert" aria-label="Nachricht"></textarea>
|
||||
<button type="submit" class="btn-send" id="dock-send" aria-label="Senden">→</button>
|
||||
</form>
|
||||
<a class="dock-footer-link" href="/">Zum vollen Widget →</a>
|
||||
</aside>
|
||||
|
||||
<button id="dock-open" class="dock-open" type="button" aria-label="Kai-Chat öffnen" title="Kai-Chat öffnen">
|
||||
<span class="dock-open-dot"></span>Kai fragen
|
||||
</button>
|
||||
|
||||
<footer class="footer">
|
||||
Sovereign AI · Deutscher Bunker · <a href="https://qognio.com">Qognio</a> · DSGVO-konform · Keine externen CDNs
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="modal-root" class="modal-root" aria-hidden="true"></div>
|
||||
<div id="toast-stack" class="toast-stack" aria-live="polite"></div>
|
||||
|
||||
<script src="cockpit.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
386
www/curricula.json
Normal file
386
www/curricula.json
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
{
|
||||
"version": "2026-04-24",
|
||||
"updated": "2026-04-24",
|
||||
"curricula": [
|
||||
{
|
||||
"id": "klassische-ml-metriken",
|
||||
"title": "Klassische ML-Metriken",
|
||||
"short": "ML-Metriken",
|
||||
"icon": "chart",
|
||||
"color": "#0891b2",
|
||||
"description": "Accuracy, Precision, Recall, F1, ROC-AUC, Cohen's Kappa, MCC — welche Metrik wann, und warum",
|
||||
"modules": [
|
||||
{
|
||||
"id": "klassifikation-metriken",
|
||||
"title": "Klassifikations-Metriken",
|
||||
"subtopics": [
|
||||
{"id": "acc-prec-rec", "title": "Accuracy vs Precision vs Recall", "objectives": ["Formeln erklären", "Wann welche priorisieren", "Class-Imbalance-Fallstricke erkennen"]},
|
||||
{"id": "f1-mcc-kappa", "title": "F1, MCC, Cohen's Kappa", "objectives": ["Harmonisches Mittel verstehen", "Wann MCC besser als F1", "Inter-Rater-Reliability"]},
|
||||
{"id": "roc-pr-auc", "title": "ROC-AUC vs PR-AUC", "objectives": ["Threshold-unabhängige Bewertung", "Imbalanced Data: warum PR-AUC besser", "Trade-offs visualisieren"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "regression-metriken",
|
||||
"title": "Regressions-Metriken",
|
||||
"subtopics": [
|
||||
{"id": "rmse-mae", "title": "RMSE vs MAE vs MAPE", "objectives": ["Skalen-Abhängigkeit", "Outlier-Sensitivität", "Relative Fehler"]},
|
||||
{"id": "r2-adjusted", "title": "R² und Adjusted R²", "objectives": ["Erklärte Varianz", "Model-Complexity-Penalty"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "nlp-metriken",
|
||||
"title": "NLP & LLM-Metriken",
|
||||
"subtopics": [
|
||||
{"id": "bleu-rouge", "title": "BLEU, ROUGE, METEOR", "objectives": ["n-gram-Matching", "Grenzen automatischer Metriken"]},
|
||||
{"id": "perplexity", "title": "Perplexity & Token-Metriken", "objectives": ["Information Theory-Grundlage", "Pro-Token-Verlust"]},
|
||||
{"id": "llm-as-judge", "title": "LLM-as-a-Judge", "objectives": ["Evaluation mit Modell-Richter", "Bias in der Evaluation"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "business-kpis",
|
||||
"title": "Business-KPIs für KI",
|
||||
"short": "Business-KPIs",
|
||||
"icon": "briefcase",
|
||||
"color": "#0891b2",
|
||||
"description": "ROI, Time-to-Value, Adoption, Kosten-pro-Inference — KI im Unternehmen messbar machen",
|
||||
"modules": [
|
||||
{
|
||||
"id": "roi-tco",
|
||||
"title": "ROI, TCO, Time-to-Value",
|
||||
"subtopics": [
|
||||
{"id": "roi-formel", "title": "ROI-Berechnung für KI-Projekte", "objectives": ["FTE-Äquivalente einrechnen", "Indirekte Effekte quantifizieren", "Amortisationsdauer"]},
|
||||
{"id": "tco-hidden", "title": "TCO & versteckte Kosten", "objectives": ["Inferenzkosten vs Trainingskosten", "Operational Overhead", "Vendor-Lock-in"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "adoption-satisfaction",
|
||||
"title": "Adoption & Zufriedenheit",
|
||||
"subtopics": [
|
||||
{"id": "adoption-rate", "title": "Adoption-Rate & Active-Users", "objectives": ["WAU/MAU-Unterschied", "Benchmarks aus Praxis"]},
|
||||
{"id": "csat-nps", "title": "CSAT, NPS für KI-Features", "objectives": ["Feature-spezifische Messung", "Anti-Patterns"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "operational",
|
||||
"title": "Operational Metrics",
|
||||
"short": "Operations",
|
||||
"icon": "activity",
|
||||
"color": "#0891b2",
|
||||
"description": "Latency, Throughput, Availability — KI-Systeme im Produktivbetrieb überwachen",
|
||||
"modules": [
|
||||
{
|
||||
"id": "slo-sla",
|
||||
"title": "SLO/SLA-Design",
|
||||
"subtopics": [
|
||||
{"id": "latency-percentiles", "title": "P50/P95/P99 Latency", "objectives": ["Warum nicht Average", "Percentile lesen"]},
|
||||
{"id": "throughput-tps", "title": "Throughput & Tokens-per-Second", "objectives": ["LLM-Durchsatz messen", "Batching-Effekte"]},
|
||||
{"id": "availability", "title": "Availability & Error Budget", "objectives": ["SLO-Definition", "Error-Budget-Policy"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "monitoring-tooling",
|
||||
"title": "Monitoring-Stack",
|
||||
"subtopics": [
|
||||
{"id": "mlflow-arize", "title": "MLflow, Arize, WhyLabs, W&B", "objectives": ["Tool-Landschaft verstehen", "Auswahl-Kriterien"]},
|
||||
{"id": "drift-alerts", "title": "Drift-Alerts & Incident-Playbook", "objectives": ["Schwellenwerte setzen", "False-Positive-Rate"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "datenqualitaet",
|
||||
"title": "Datenqualität & Drift",
|
||||
"short": "Daten",
|
||||
"icon": "database",
|
||||
"color": "#0891b2",
|
||||
"description": "DAMA-Dimensionen, PSI, KL-Divergence — Datenprobleme vor Modell-Problemen erkennen",
|
||||
"modules": [
|
||||
{
|
||||
"id": "dama-dimensions",
|
||||
"title": "DAMA-Dimensionen",
|
||||
"subtopics": [
|
||||
{"id": "completeness-accuracy", "title": "Completeness, Accuracy, Validity", "objectives": ["Jede Dimension mit Beispiel", "Messmethoden"]},
|
||||
{"id": "timeliness-uniqueness", "title": "Timeliness, Uniqueness, Consistency", "objectives": ["Stale-Data-Risiken", "Dedup-Strategien"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "drift-detection",
|
||||
"title": "Drift-Detection",
|
||||
"subtopics": [
|
||||
{"id": "psi-kl", "title": "PSI, KL- und JS-Divergence", "objectives": ["Formel und Interpretation", "Schwellenwerte"]},
|
||||
{"id": "ks-test", "title": "Kolmogorov-Smirnov-Test", "objectives": ["Nicht-parametrischer Test", "Feature-vs-Label-Drift"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "bias-fairness",
|
||||
"title": "Bias & Fairness",
|
||||
"short": "Fairness",
|
||||
"icon": "scale",
|
||||
"color": "#0891b2",
|
||||
"description": "Demographic Parity, Equal Opportunity, Disparate Impact — Diskriminierungsfreie Systeme",
|
||||
"modules": [
|
||||
{
|
||||
"id": "fairness-metriken",
|
||||
"title": "Fairness-Metriken",
|
||||
"subtopics": [
|
||||
{"id": "demographic-parity", "title": "Demographic Parity", "objectives": ["Formel", "Einschränkungen"]},
|
||||
{"id": "equal-opportunity", "title": "Equal Opportunity & Equalized Odds", "objectives": ["Unterschied zu Parity", "Praxisbeispiel HR-Tool"]},
|
||||
{"id": "disparate-impact", "title": "Disparate Impact & 80%-Regel", "objectives": ["US-EEOC-Standard", "EU-Bezug"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "fairness-tooling",
|
||||
"title": "Tooling",
|
||||
"subtopics": [
|
||||
{"id": "aif360-fairlearn", "title": "AIF360 & Fairlearn", "objectives": ["Library-Überblick", "Bias-Audits durchführen"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "explainability",
|
||||
"title": "Explainability (XAI)",
|
||||
"short": "XAI",
|
||||
"icon": "eye",
|
||||
"color": "#0891b2",
|
||||
"description": "SHAP, LIME, Counterfactuals — Modell-Entscheidungen erklärbar machen (EU AI Act Art. 13)",
|
||||
"modules": [
|
||||
{
|
||||
"id": "xai-methoden",
|
||||
"title": "XAI-Methoden",
|
||||
"subtopics": [
|
||||
{"id": "shap-lime", "title": "SHAP vs LIME", "objectives": ["Shapley-Values verstehen", "Lokale vs globale Erklärung"]},
|
||||
{"id": "counterfactuals", "title": "Counterfactual & Anchor Explanations", "objectives": ["Minimal-Änderungs-Prinzip", "Praxisnutzen"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "xai-qualitaet",
|
||||
"title": "Qualität von Erklärungen",
|
||||
"subtopics": [
|
||||
{"id": "fidelity-stability", "title": "Fidelity, Stability, Comprehensibility", "objectives": ["Messgrößen für Erklärungsqualität", "Trade-offs"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "robustheit-security",
|
||||
"title": "Robustheit & Security",
|
||||
"short": "Robustheit",
|
||||
"icon": "shield",
|
||||
"color": "#0891b2",
|
||||
"description": "Adversarial Robustness, Prompt-Injection, Data-Poisoning — Angriffsvektoren verstehen",
|
||||
"modules": [
|
||||
{
|
||||
"id": "adversarial",
|
||||
"title": "Adversarial Robustness",
|
||||
"subtopics": [
|
||||
{"id": "pgd-fgsm", "title": "PGD & FGSM-Attacks", "objectives": ["Perturbations verstehen", "L-infinity-Budget"]},
|
||||
{"id": "certified-robustness", "title": "Certified Robustness", "objectives": ["Formale Garantien vs empirische Tests"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "llm-security",
|
||||
"title": "LLM-Security",
|
||||
"subtopics": [
|
||||
{"id": "prompt-injection", "title": "Prompt-Injection & Jailbreaks", "objectives": ["OWASP LLM Top 10", "Mitigationen"]},
|
||||
{"id": "data-poisoning", "title": "Data-Poisoning-Erkennung", "objectives": ["Training-Set-Forensik", "Monitoring-Metriken"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "governance-reifegrad",
|
||||
"title": "Governance & Reifegrad",
|
||||
"short": "Reifegrad",
|
||||
"icon": "gauge",
|
||||
"color": "#0891b2",
|
||||
"description": "Gartner AI Maturity, MIT CISR, Microsoft RAI MM — wo stehst du, wo willst du hin",
|
||||
"modules": [
|
||||
{
|
||||
"id": "reifegradmodelle",
|
||||
"title": "AI-Reifegradmodelle im Vergleich",
|
||||
"subtopics": [
|
||||
{"id": "gartner-maturity", "title": "Gartner AI Maturity Model", "objectives": ["5 Stufen", "Self-Assessment"]},
|
||||
{"id": "mit-cisr", "title": "MIT CISR & Microsoft RAI MM", "objectives": ["Unterschiede", "DACH-Anwendung"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "self-assessment",
|
||||
"title": "Self-Assessment-Dimensionen",
|
||||
"subtopics": [
|
||||
{"id": "strategy-data", "title": "Strategy, Data, Technology", "objectives": ["Fragebogen-Struktur", "Reifegrad-Score berechnen"]},
|
||||
{"id": "people-processes", "title": "People, Processes", "objectives": ["Kompetenz-Matrix", "Prozess-Reife"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "eu-ai-act",
|
||||
"title": "EU AI Act Compliance",
|
||||
"short": "EU AI Act",
|
||||
"icon": "flag",
|
||||
"color": "#0891b2",
|
||||
"description": "Risikoklassen, CE-Kennzeichnung, Artikel-Pflichten — Was muss ich ab wann erfüllen",
|
||||
"modules": [
|
||||
{
|
||||
"id": "risikoklassen",
|
||||
"title": "Risikoklassen",
|
||||
"subtopics": [
|
||||
{"id": "verboten-high-risk", "title": "Verboten vs High-Risk", "objectives": ["Art. 5 vs Annex III", "Grenzfälle erkennen"]},
|
||||
{"id": "limited-minimal", "title": "Limited & Minimal Risk", "objectives": ["Transparenz-Pflichten", "Chatbots"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "high-risk-pflichten",
|
||||
"title": "High-Risk-Pflichten",
|
||||
"subtopics": [
|
||||
{"id": "risk-management", "title": "Art. 9 Risikomanagement-System", "objectives": ["Dokumentations-Anforderungen"]},
|
||||
{"id": "data-governance", "title": "Art. 10 Data Governance", "objectives": ["Trainings-, Validierungs- und Testdaten"]},
|
||||
{"id": "transparency-art13", "title": "Art. 13 Transparenz", "objectives": ["User-Information", "Logging"]},
|
||||
{"id": "human-oversight", "title": "Art. 14 Menschliche Aufsicht", "objectives": ["Override-Mechanismen"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "timeline-ce",
|
||||
"title": "Timeline & CE-Kennzeichnung",
|
||||
"subtopics": [
|
||||
{"id": "phasen-einfuehrung", "title": "Inkrafttreten-Phasen", "objectives": ["2025/2026/2027-Meilensteine"]},
|
||||
{"id": "ce-marking", "title": "CE-Kennzeichnung für High-Risk", "objectives": ["Konformitäts-Assessment", "Benannte Stellen"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "nist-ai-rmf",
|
||||
"title": "NIST AI RMF",
|
||||
"short": "NIST",
|
||||
"icon": "book",
|
||||
"color": "#0891b2",
|
||||
"description": "Govern, Map, Measure, Manage — US-Framework mit Crosswalk zu EU AI Act und ISO 42001",
|
||||
"modules": [
|
||||
{
|
||||
"id": "vier-funktionen",
|
||||
"title": "Die 4 Kernfunktionen",
|
||||
"subtopics": [
|
||||
{"id": "govern", "title": "Govern", "objectives": ["Organisations-Kultur", "Policies"]},
|
||||
{"id": "map", "title": "Map", "objectives": ["Kontext & Risiken erfassen"]},
|
||||
{"id": "measure", "title": "Measure", "objectives": ["KPIs und Tests"]},
|
||||
{"id": "manage", "title": "Manage", "objectives": ["Priorisieren, Response, Recovery"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "crosswalk",
|
||||
"title": "Crosswalk zu anderen Frameworks",
|
||||
"subtopics": [
|
||||
{"id": "nist-vs-eu-ai-act", "title": "NIST vs EU AI Act", "objectives": ["Überlappungen nutzen"]},
|
||||
{"id": "nist-vs-iso", "title": "NIST vs ISO 42001", "objectives": ["Mapping-Tabellen"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "iso-42001",
|
||||
"title": "ISO/IEC 42001 & 23894",
|
||||
"short": "ISO",
|
||||
"icon": "award",
|
||||
"color": "#0891b2",
|
||||
"description": "AI Management System, AI Risk Management — zertifizierbare Standards seit 2023",
|
||||
"modules": [
|
||||
{
|
||||
"id": "iso-42001-aims",
|
||||
"title": "ISO 42001 — AIMS",
|
||||
"subtopics": [
|
||||
{"id": "aims-struktur", "title": "Was ist ein AIMS", "objectives": ["Management-System-Struktur", "PDCA für KI"]},
|
||||
{"id": "zertifizierungspfad", "title": "Zertifizierungspfad", "objectives": ["Akkreditierte Stellen DACH", "Aufwand schätzen"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "iso-23894",
|
||||
"title": "ISO 23894 — AI Risk Management",
|
||||
"subtopics": [
|
||||
{"id": "risk-assessment", "title": "Risk Assessment Prozess", "objectives": ["Risiko identifizieren/analysieren/bewerten"]},
|
||||
{"id": "komplement-27001", "title": "Komplementarität zu ISO 27001", "objectives": ["Synergie mit ISMS"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "scorecards",
|
||||
"title": "Scorecards & Dashboards",
|
||||
"short": "Scorecards",
|
||||
"icon": "clipboard",
|
||||
"color": "#0891b2",
|
||||
"description": "KPI-Hierarchie Exec/Operational/Technical — vom Dashboard zur Entscheidung",
|
||||
"modules": [
|
||||
{
|
||||
"id": "scorecard-design",
|
||||
"title": "Scorecard-Design",
|
||||
"subtopics": [
|
||||
{"id": "kpi-hierarchie", "title": "Exec-, Operational-, Technical-Layer", "objectives": ["Welche KPI auf welcher Ebene", "Aggregations-Logik"]},
|
||||
{"id": "vanity-metrics", "title": "Vanity-Metrics erkennen", "objectives": ["Anti-Patterns", "Actionability-Test"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dashboarding-tools",
|
||||
"title": "Dashboarding-Tools",
|
||||
"subtopics": [
|
||||
{"id": "tableau-looker-superset", "title": "Tableau, Looker, Superset, Grafana", "objectives": ["Stärken/Schwächen", "Kosten"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "okr-ki",
|
||||
"title": "OKRs für KI-Teams",
|
||||
"short": "OKR",
|
||||
"icon": "target",
|
||||
"color": "#0891b2",
|
||||
"description": "Objectives & Key Results — Balance zwischen Tech-KPIs und Business-Impact",
|
||||
"modules": [
|
||||
{
|
||||
"id": "okr-grundlagen",
|
||||
"title": "OKR-Grundlagen für AI",
|
||||
"subtopics": [
|
||||
{"id": "obj-vs-kr", "title": "Objectives vs Key Results", "objectives": ["Qualitativ vs quantitativ"]},
|
||||
{"id": "balance-tech-biz", "title": "Balance Tech vs Business", "objectives": ["Anti-Pattern: Over-Tech-Indexed"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "beispiele-dach",
|
||||
"title": "Beispiele aus DACH-Mittelstand",
|
||||
"subtopics": [
|
||||
{"id": "mittelstand-cases", "title": "5 Praxisbeispiele", "objectives": ["Formulierungs-Muster"]}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"badges": [
|
||||
{"id": "erste_metrik", "title": "Erste Metrik-Analyse", "icon": "target", "description": "1. Quiz zu klassischen ML-Metriken bestanden"},
|
||||
{"id": "metrik_master", "title": "Metrik-Master:in", "icon": "trophy", "description": "10 Klassifikations-Metriken-Fragen korrekt"},
|
||||
{"id": "bias_hunter", "title": "Bias-Jäger:in", "icon": "scale", "description": "Bias & Fairness-Modul abgeschlossen"},
|
||||
{"id": "ai_act_navigator", "title": "EU-AI-Act-Navigator:in", "icon": "flag", "description": "EU-AI-Act-Modul abgeschlossen"},
|
||||
{"id": "nist_practitioner", "title": "NIST-Praktiker:in", "icon": "book", "description": "NIST AI RMF-Modul abgeschlossen"},
|
||||
{"id": "iso_expert", "title": "ISO-42001-Expert:in", "icon": "award", "description": "ISO 42001/23894-Modul abgeschlossen"},
|
||||
{"id": "scorecard_architect", "title": "Scorecard-Architekt:in", "icon": "clipboard", "description": "Scorecards-Flashcards bestanden"},
|
||||
{"id": "governance_lead", "title": "Governance-Lead", "icon": "crown", "description": "Reifegrad-Modul abgeschlossen"},
|
||||
{"id": "kpi_master", "title": "KI-Kennzahlen-Master", "icon": "star", "description": "Alle 13 Curricula abgeschlossen"},
|
||||
{"id": "night_owl", "title": "Nachteule", "icon": "moon", "description": "Nach 22 Uhr gelernt"},
|
||||
{"id": "early_bird", "title": "Frühaufsteher:in", "icon": "sun", "description": "Vor 7 Uhr gelernt"}
|
||||
],
|
||||
"levels": [
|
||||
{"min": 0, "title": "Einsteiger:in"},
|
||||
{"min": 50, "title": "Analyst:in"},
|
||||
{"min": 200, "title": "Data-Scientist:in"},
|
||||
{"min": 500, "title": "ML-Engineer:in"},
|
||||
{"min": 1250, "title": "AI-Program-Lead"},
|
||||
{"min": 2500, "title": "Head of AI"},
|
||||
{"min": 5000, "title": "Chief Data/AI-Officer"}
|
||||
]
|
||||
}
|
||||
125
www/index.html
Normal file
125
www/index.html
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Kai · KI-Kennzahlen & Methodik-Coach</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||||
<meta name="theme-color" content="#0a0a0f">
|
||||
<meta name="description" content="Kai — dein KI-Kennzahlen-Coach. Methodik, Modell-Bewertung, Bias/Fairness, AI-Reifegrad-Messung für CIOs, CDOs, AI-Leads. Im deutschen Bunker.">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<script>window.__KAI_KEY__ = 'qb_qj0ahcfv6coz';</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app" role="application" aria-label="Kai KI-Kennzahlen & Methodik-Coach">
|
||||
|
||||
<header class="topbar">
|
||||
<div class="brand">
|
||||
<span class="brand-icon" aria-hidden="true">K</span>
|
||||
<span>Kai <small>KI-Kennzahlen</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">
|
||||
Module
|
||||
<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 Kai!</h2>
|
||||
<p>Ich bin dein:e Sparringspartner:in für KI-Kennzahlen, Modell-Bewertung, Reifegradmessung und AI-Governance — ruhig, systematisch, zahlenliebend. Warum das Ganze? <strong>Messbar steuern</strong>, <strong>Bias erkennen</strong>, <strong>ROI zeigen</strong>. Läuft im deutschen Bunker, deine Metriken bleiben hier.</p>
|
||||
<div class="mode-grid">
|
||||
<button class="mode-card" data-goto="chat">
|
||||
<strong>Chat</strong>
|
||||
<span>Frag mich zu Klassifikations-Metriken, Fairness, NIST AI RMF, ISO 42001, AI-Reifegrad.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="quiz">
|
||||
<strong>Quiz</strong>
|
||||
<span>Szenario-Fragen aus dem KI-Projekt-Alltag, mit XP.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="flash">
|
||||
<strong>Flashcards</strong>
|
||||
<span>Metriken, Bias-Tests, Reifegrad-Stufen — mit Spaced-Repetition.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="progress">
|
||||
<strong>Fortschritt</strong>
|
||||
<span>XP, Badges, Level vom AI-Analyst bis Governance-Lead.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="curriculum">
|
||||
<strong>Module</strong>
|
||||
<span>13 Themenblöcke / 28 Module — von Klassifikations-Metriken bis Scorecard-Design.</span>
|
||||
</button>
|
||||
<a class="mode-card" href="cockpit/" target="_blank" rel="noopener">
|
||||
<strong>Cockpit ↗</strong>
|
||||
<span>Reifegrad-Assessment, EU-AI-Act-Risk-Classifier, KPI-Dashboard — Spezial-App neben dem Chat.</span>
|
||||
</a>
|
||||
</div>
|
||||
<p style="font-size:.82rem;color:var(--text-mute)">In 3 Sätzen: Chat zum Verstehen → Quiz zum Testen → Flashcards zum Merken. Das Cockpit liefert dir Reifegrad-Score und AI-Act-Klassifizierung. Brauchst du operative AI-Act-Tiefe? → ruf KURT. AI-Act-Audit-Trail? → VESTIGIA.</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 Kai — 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> · 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
1038
www/styles.css
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue