Files
AI-Vortrag/config.html
Erik Thiele 66204e6f63 Expand deck to 42 slides, add speaker notes, and fix editor artifacts
Adds Grounding, Reasoning-Modelle, Token/Parameter examples, an
autocomplete mini-game, and a Copilot agent-building walkthrough with
a live chat-demo payoff. Adds Sprecher.md with per-slide speaker
cards. Fixes malformed data-def attributes and stray baked-in
active/visible classes left over from in-browser edit-mode sessions,
and stops the local save-in-place backups from being tracked.
2026-07-29 11:25:28 +02:00

270 lines
12 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CANCOM · Deck konfigurieren</title>
<link rel="preconnect" href="https://api.fontshare.com">
<link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=general-sans@700,600,500&f[]=satoshi@700,500,400&f[]=jetbrains-mono@700,500,400&display=swap">
<script src="slides-manifest.js?v=2"></script>
<style>
:root {
--cancom-red: #E4002B;
--cancom-red-glow: rgba(228,0,43,0.55);
--cancom-red-dim: rgba(228,0,43,0.15);
--ink: #0A0A0F;
--ink-soft: #14141C;
--ink-line: #23232E;
--paper: #F5F5F2;
--paper-dim: rgba(245,245,242,0.6);
--paper-mute: rgba(245,245,242,0.35);
--font-display: 'General Sans', sans-serif;
--font-body: 'Satoshi', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { background: var(--ink); color: var(--paper); font-family: var(--font-body); min-height: 100%; }
body { padding: 64px 64px 140px; max-width: 1100px; margin: 0 auto; }
.page-eyebrow {
font-family: var(--font-mono); font-size: 14px; letter-spacing: 0.28em; text-transform: uppercase;
color: var(--cancom-red); display: inline-flex; align-items: center; gap: 14px; margin-bottom: 20px;
}
.page-eyebrow::before { content: ""; width: 40px; height: 2px; background: var(--cancom-red); }
h1 {
font-family: var(--font-display); font-weight: 700; font-size: 48px; letter-spacing: -0.03em;
line-height: 1.1; margin-bottom: 14px;
}
.page-lead { font-size: 18px; color: var(--paper-dim); line-height: 1.5; max-width: 640px; margin-bottom: 40px; }
.toolbar {
position: sticky; top: 0; z-index: 50;
display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
background: rgba(10,10,15,0.9); backdrop-filter: blur(12px);
border: 1px solid var(--ink-line); border-radius: 14px;
padding: 18px 22px; margin-bottom: 36px;
}
.toolbar .count {
font-family: var(--font-mono); font-size: 14px; letter-spacing: 0.1em; color: var(--paper-mute);
margin-right: auto;
}
.toolbar .count strong { color: var(--paper); font-weight: 700; }
.btn {
font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase;
padding: 12px 18px; border-radius: 8px; border: 1px solid var(--ink-line);
background: #0f0f16; color: var(--paper-dim); cursor: pointer;
transition: border-color 0.12s ease-out, color 0.12s ease-out, background 0.12s ease-out;
}
.btn:hover { border-color: var(--cancom-red); color: var(--paper); }
.btn.primary {
background: var(--cancom-red); border-color: var(--cancom-red); color: #fff; font-weight: 700;
box-shadow: 0 10px 30px var(--cancom-red-glow);
}
.btn.primary:hover { transform: translateY(-1px); }
.chapter { margin-bottom: 36px; }
.chapter-head {
display: flex; align-items: center; gap: 14px; margin-bottom: 14px;
font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.18em; text-transform: uppercase;
color: var(--paper-mute);
}
.chapter-head .chapter-toggle-all {
margin-left: auto; font-size: 11px; color: var(--cancom-red); cursor: pointer; letter-spacing: 0.1em;
background: none; border: none; font-family: var(--font-mono); padding: 4px 8px;
}
.chapter-head .chapter-toggle-all:hover { text-decoration: underline; }
.row {
display: flex; align-items: center; gap: 18px;
padding: 16px 20px; border: 1px solid var(--ink-line); border-radius: 10px;
background: #0f0f16; margin-bottom: 10px;
transition: border-color 0.12s ease-out, background 0.12s ease-out;
}
.row:hover { border-color: rgba(228,0,43,0.35); background: #14141c; }
.row.divider-row { background: transparent; border-style: dashed; }
.row.locked { opacity: 0.55; }
.row input[type="checkbox"] {
appearance: none; width: 22px; height: 22px; flex-shrink: 0;
border: 1.5px solid var(--ink-line); border-radius: 6px; background: #0a0a0f;
cursor: pointer; position: relative; transition: border-color 0.12s ease-out, background 0.12s ease-out;
}
.row input[type="checkbox"]:checked { background: var(--cancom-red); border-color: var(--cancom-red); }
.row input[type="checkbox"]:checked::after {
content: ""; position: absolute; left: 7px; top: 3px; width: 5px; height: 10px;
border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.row input[type="checkbox"]:disabled { cursor: not-allowed; }
.row .slide-id {
font-family: var(--font-mono); font-size: 13px; color: var(--paper-mute); width: 28px; flex-shrink: 0;
}
.row .slide-title { font-size: 17px; color: var(--paper); flex: 1; }
.row.divider-row .slide-title { color: var(--paper-mute); font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.1em; text-transform: uppercase; }
.row .badge {
font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
color: var(--paper-mute); border: 1px solid var(--ink-line); border-radius: 20px; padding: 4px 10px;
}
.footer-bar {
position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
display: flex; justify-content: center; gap: 16px;
padding: 22px; background: linear-gradient(0deg, var(--ink) 60%, transparent);
}
.footer-bar .btn { padding: 16px 32px; font-size: 14px; }
.hint { font-size: 14px; color: var(--paper-mute); margin-top: -8px; margin-bottom: 30px; }
</style>
</head>
<body>
<div class="page-eyebrow">Präsentations-Konfigurator</div>
<h1>Welche Slides sollen gezeigt werden?</h1>
<p class="page-lead">Häkchen entfernen blendet eine Slide für den Vortrag aus, ohne sie zu löschen. Die Auswahl wird lokal in diesem Browser gespeichert und beim nächsten Öffnen des Decks automatisch angewendet.</p>
<div class="toolbar">
<span class="count" id="count"> / Slides aktiv</span>
<button class="btn" id="btnAll">Alle an</button>
<button class="btn" id="btnNone">Alle aus</button>
<button class="btn" id="btnReset">Zurücksetzen</button>
<button class="btn primary" id="btnOpen">Zur Präsentation →</button>
</div>
<div id="chapters"></div>
<div class="footer-bar">
<button class="btn primary" id="btnSaveBottom">Speichern & zur Präsentation →</button>
</div>
<script>
(function () {
const chaptersEl = document.getElementById('chapters');
const countEl = document.getElementById('count');
// Slides nach Kapitel gruppieren, Reihenfolge aus dem Manifest beibehalten
const groups = [];
const groupIndex = {};
SLIDE_MANIFEST.forEach((slide) => {
const key = slide.kapitel;
if (!(key in groupIndex)) {
groupIndex[key] = groups.length;
groups.push({ kapitel: key, slides: [] });
}
groups[groupIndex[key]].slides.push(slide);
});
const maxId = SLIDE_MANIFEST.length;
let enabled = loadSlideConfig();
// Veraltete Auswahl von einem Deck-Stand mit anderer Slide-Zahl verwerfen
// (die letzte Slide ist immer `locked` und muss daher immer enthalten sein).
if (enabled && !enabled.has(maxId)) {
enabled = null;
}
if (!enabled) {
enabled = new Set(SLIDE_MANIFEST.map((s) => s.id));
}
function render() {
chaptersEl.innerHTML = '';
groups.forEach((group) => {
const chapterDiv = document.createElement('div');
chapterDiv.className = 'chapter';
const head = document.createElement('div');
head.className = 'chapter-head';
head.innerHTML = '<span>' + group.kapitel + '</span>';
const toggleAllBtn = document.createElement('button');
toggleAllBtn.className = 'chapter-toggle-all';
toggleAllBtn.textContent = 'Kapitel an/aus';
toggleAllBtn.addEventListener('click', () => {
const toggleable = group.slides.filter((s) => !s.locked);
const allOn = toggleable.every((s) => enabled.has(s.id));
toggleable.forEach((s) => {
if (allOn) enabled.delete(s.id); else enabled.add(s.id);
});
render();
});
head.appendChild(toggleAllBtn);
chapterDiv.appendChild(head);
group.slides.forEach((slide) => {
const row = document.createElement('label');
row.className = 'row' + (slide.type === 'divider' ? ' divider-row' : '') + (slide.locked ? ' locked' : '');
const cb = document.createElement('input');
cb.type = 'checkbox';
cb.checked = enabled.has(slide.id);
cb.disabled = !!slide.locked;
cb.addEventListener('change', () => {
if (cb.checked) enabled.add(slide.id); else enabled.delete(slide.id);
updateCount();
});
const idEl = document.createElement('span');
idEl.className = 'slide-id';
idEl.textContent = String(slide.id).padStart(2, '0');
const titleEl = document.createElement('span');
titleEl.className = 'slide-title';
titleEl.textContent = slide.title;
row.appendChild(cb);
row.appendChild(idEl);
row.appendChild(titleEl);
if (slide.locked) {
const badge = document.createElement('span');
badge.className = 'badge';
badge.textContent = 'immer aktiv';
row.appendChild(badge);
} else if (slide.type === 'divider') {
const badge = document.createElement('span');
badge.className = 'badge';
badge.textContent = 'Kapitel-Trenner';
row.appendChild(badge);
}
chapterDiv.appendChild(row);
});
chaptersEl.appendChild(chapterDiv);
});
updateCount();
}
function updateCount() {
countEl.innerHTML = '<strong>' + enabled.size + '</strong> / ' + SLIDE_MANIFEST.length + ' Slides aktiv';
saveSlideConfig(enabled);
}
document.getElementById('btnAll').addEventListener('click', () => {
enabled = new Set(SLIDE_MANIFEST.map((s) => s.id));
render();
});
document.getElementById('btnNone').addEventListener('click', () => {
enabled = new Set(SLIDE_MANIFEST.filter((s) => s.locked).map((s) => s.id));
render();
});
document.getElementById('btnReset').addEventListener('click', () => {
enabled = new Set(SLIDE_MANIFEST.map((s) => s.id));
render();
});
function goToDeck() {
saveSlideConfig(enabled);
// localStorage wird bei file:// nicht zuverlässig zwischen Dateien geteilt (v. a. Safari) —
// die Auswahl deshalb zusätzlich als URL-Parameter mitgeben, das ist die verlässliche Quelle.
const ids = Array.from(enabled).sort((a, b) => a - b).join(',');
window.location.href = 'ki-verstehen.html?slides=' + encodeURIComponent(ids);
}
document.getElementById('btnOpen').addEventListener('click', goToDeck);
document.getElementById('btnSaveBottom').addEventListener('click', goToDeck);
render();
})();
</script>
</body>
</html>