Add full deck, config tool, and updated design docs
Adds the complete "KI verstehen" presentation (36 slides), the Präsentations-Konfigurator (config.html + slides-manifest.js) for curating which slides to show, and updates DESIGN.md/PATTERNS.md/ deck-template.html to reflect the new house font (General Sans) and newly established patterns (chapter dividers, term tooltips, the mitmach game, ambient sound, and the slide configurator).
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.DS_Store
|
||||||
248
DESIGN.md
Normal file
248
DESIGN.md
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
# CANCOM HTML Deck — Design System
|
||||||
|
|
||||||
|
Verbindliche Grundlage für alle HTML-Präsentationen im CANCOM-Look.
|
||||||
|
Referenz-Implementierung: `cancom-html-vs-pptx-v7.html`
|
||||||
|
Ausgebautes Praxisbeispiel (36 Slides, Kapitel-Trenner, Mitmach-Spiel, Begriffs-Tooltips,
|
||||||
|
Präsentations-Konfigurator, Ambient-Sound): `ki-verstehen.html` + `config.html` + `slides-manifest.js`
|
||||||
|
Startpunkt für neue Decks: `deck-template.html`
|
||||||
|
Copy-Paste-Bausteine: `PATTERNS.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Grundprinzipien
|
||||||
|
|
||||||
|
1. **Ein File.** Kein Build, kein Bundler, keine Node-Dependencies. HTML + CSS + JS in einer Datei, per Doppelklick lauffähig, per Mail/Teams/SharePoint verteilbar. Einzige externe Abhängigkeit: die Fonts von Fontshare (mit lokalem Fallback).
|
||||||
|
2. **Feste Bühne, skaliert.** Jede Slide ist exakt 1920×1080 px. Nichts wird responsiv umgebrochen — die gesamte Bühne wird per `transform: scale()` in den Viewport gerechnet. Dadurch sieht das Deck auf jedem Screen und jedem Beamer pixelidentisch aus.
|
||||||
|
3. **Design in Tokens.** Farben, Fonts, Easings stehen ausschließlich in `:root`. Keine Hex-Codes im Slide-Markup. Ein neues Kunden-Theme = neue Token-Werte, kein Umbau.
|
||||||
|
4. **Animation ist Argument.** Das Deck ist ein Beweis dafür, dass HTML mehr kann als PPTX. Jede Slide sollte mindestens eine Sache tun, die eine PowerPoint-Folie nicht kann — Live-Daten, echte Interaktion, GPU-Animation.
|
||||||
|
5. **Ehrlichkeit im Ton.** Deutsch, direkt, kein Marketing-Sprech. Headlines sind kurze Sätze mit Punkt am Ende („PPTX ist Vergangenheit."), nicht Substantiv-Ketten.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Farb-Tokens
|
||||||
|
|
||||||
|
| Token | Wert | Verwendung |
|
||||||
|
|---|---|---|
|
||||||
|
| `--cancom-red` | `#E4002B` | Signalfarbe. Akzente, Zahlen, Eyebrows, genau **ein** Highlight pro Headline |
|
||||||
|
| `--cancom-red-glow` | `rgba(228,0,43,0.55)` | `box-shadow` / `text-shadow` Glow |
|
||||||
|
| `--cancom-red-dim` | `rgba(228,0,43,0.15)` | Flächige Rot-Hinterlegung, Hover-Auras |
|
||||||
|
| `--ink` | `#0A0A0F` | Standard-Slide-Hintergrund |
|
||||||
|
| `--ink-soft` | `#14141C` | Erhöhte Flächen, Hover-States |
|
||||||
|
| `--ink-line` | `#23232E` | Alle Rahmen und Trennlinien |
|
||||||
|
| `--paper` | `#F5F5F2` | Primärtext auf Dunkel, Hintergrund der Closing-Slide |
|
||||||
|
| `--paper-dim` | `rgba(245,245,242,0.6)` | Fließtext, Lead-Absätze |
|
||||||
|
| `--paper-mute` | `rgba(245,245,242,0.35)` | Labels, Slide-Nummern, HUD |
|
||||||
|
| `--accent-cool` | `#4CC9F0` | Zweitfarbe in Charts und Code-Highlighting |
|
||||||
|
| `--accent-warm` | `#FFB627` | Drittfarbe, Flash-States |
|
||||||
|
|
||||||
|
**Regeln**
|
||||||
|
|
||||||
|
- Rot ist Signal, nicht Dekoration. Pro Slide maximal ein rotes Wort in der Headline.
|
||||||
|
- Rot nie als große Textfläche — nur Headline-Akzente, Zahlen, Linien, Glows.
|
||||||
|
- Semantische Ausnahmen sind erlaubt und stehen bewusst außerhalb der Marke: `#4ade80` (grün, Kurs steigt) und `#ff5060` (hellrot, Kurs fällt).
|
||||||
|
- Die Closing-Slide dreht das Schema um: `--paper` als Hintergrund, `--ink` als Text. Das ist der einzige helle Slide — er markiert das Ende.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Typografie
|
||||||
|
|
||||||
|
Drei Schriften, drei klar getrennte Jobs:
|
||||||
|
|
||||||
|
| Token | Font | Job |
|
||||||
|
|---|---|---|
|
||||||
|
| `--font-display` | General Sans (700/600/500) | Alle Headlines, Zahlen, Buttons, Brand-Marks |
|
||||||
|
| `--font-body` | Satoshi (700/500/400) | Fließtext, Lead-Absätze, Listen |
|
||||||
|
| `--font-mono` | JetBrains Mono (700/500/400) | Eyebrows, Labels, HUD, Code, alles Technische |
|
||||||
|
|
||||||
|
### Type-Scale (bei 1920×1080)
|
||||||
|
|
||||||
|
| Rolle | Größe | Line-Height | Letter-Spacing |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Title-Headline | 220 px | 0.88 | −0.045em |
|
||||||
|
| Closing-Headline | 240 px | 0.86 | −0.045em |
|
||||||
|
| Counter-Value | 220 px | 0.90 | −0.05em |
|
||||||
|
| Slide-Title | 112 px (eng: 92 px) | 0.94 | −0.035em |
|
||||||
|
| Versus-Headline | 100 px | 0.92 | −0.035em |
|
||||||
|
| Card-Title | 42 px | 1.05 | −0.02em |
|
||||||
|
| Lead-Absatz | 30 px (eng: 22–24 px) | 1.4 | normal |
|
||||||
|
| Body / Listen | 20–24 px | 1.4–1.5 | normal |
|
||||||
|
| Eyebrow / Label | 14–18 px | — | 0.14em–0.3em, uppercase |
|
||||||
|
|
||||||
|
**Regeln**
|
||||||
|
|
||||||
|
- Je größer die Schrift, desto negativer das Tracking und desto enger die Line-Height. Große Display-Typo braucht optischen Zusammenzug.
|
||||||
|
- Mono ist **immer** uppercase mit weitem Tracking (≥ 0.14em). Es ist die „Maschinen-Stimme" des Decks.
|
||||||
|
- Body-Text nie über 1300–1500 px Breite (`max-width` am `.slide-lead`).
|
||||||
|
- Headlines brechen manuell per `<br>`. Der Zeilenumbruch ist Gestaltung, nicht Zufall.
|
||||||
|
- Zahlen, die sich live ändern, brauchen `font-variant-numeric: tabular-nums`, sonst springt das Layout.
|
||||||
|
|
||||||
|
**Schriftwahl:** Ursprünglich stand hier Clash Display. Bei sehr engem Tracking und großen
|
||||||
|
Displaygrößen (Zahlen, Zwei-Wort-Headlines) wurde die Schrift auf Distanz schwerer lesbar —
|
||||||
|
enge, kantige Formen laufen bei Beamer-Projektion schnell zusammen. **General Sans** ersetzt sie:
|
||||||
|
gleiche Bühnengröße und Gewichtsstufen (500/600/700), aber offenere Punzen (a, e, s) und eine
|
||||||
|
größere x-Höhe, die auch bei −0.04em Tracking klar bleibt. Fließtext (Satoshi) und Mono
|
||||||
|
(JetBrains Mono) sind unverändert. Beim Schriftwechsel testen: `font-vorschau.html` zeigt
|
||||||
|
Kandidaten direkt in Deck-Größe (92 px / 40 px) nebeneinander, bevor man sich festlegt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Layout
|
||||||
|
|
||||||
|
### Die Bühne
|
||||||
|
|
||||||
|
```
|
||||||
|
.deck-viewport → fixed, füllt das Browserfenster, schwarz, overflow hidden
|
||||||
|
.deck-stage → 1920×1080, transform-origin 0 0, wird per JS skaliert + zentriert
|
||||||
|
.slide → absolute inset 0, 1920×1080, default unsichtbar
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Scale-Faktor ist `Math.min(innerWidth/1920, innerHeight/1080)`, der Rest wird als Letterbox zentriert. **Diese Struktur ist nicht optional** — sie ist der Grund, warum das Deck überall gleich aussieht.
|
||||||
|
|
||||||
|
### Grid & Spacing
|
||||||
|
|
||||||
|
- **Slide-Padding:** 100 px oben/unten, 120 px links/rechts (`.slide-content`). Title- und Closing-Slide: 120 px rundum.
|
||||||
|
- **Spacing-Rhythmus:** Vielfache von 4, praktisch 8/12/16/20/24/32/44/60. Kein 13, kein 27.
|
||||||
|
- **Gap in Grids:** 20 px (dichte Feature-Tiles), 32 px (Cards), 60 px (Spalten-Splits).
|
||||||
|
- Content-Bereiche nutzen `flex: 1`, damit sie den Rest der Höhe füllen. Bei Grids mit fester Zeilenzahl zusätzlich `min-height: 0`, sonst sprengt Inhalt die Slide-Höhe.
|
||||||
|
|
||||||
|
### Layout-Archetypen
|
||||||
|
|
||||||
|
| Klasse | Struktur | Wofür |
|
||||||
|
|---|---|---|
|
||||||
|
| `.title-slide` | Space-between, 3 Zonen (Topbar / Headline / Meta) + Canvas-Layer | Slide 1 |
|
||||||
|
| `.slide-content` | Eyebrow → Titel → Lead → Content-Block | Der Standard-Slide, 80 % aller Fälle |
|
||||||
|
| `.versus` | 2 Spalten randlos + zentraler VS-Kreis | Direktvergleich, Vorher/Nachher |
|
||||||
|
| `.counter-grid` | 3 Spalten, große animierte Zahlen | Kennzahlen |
|
||||||
|
| `.code-slide` | Text links, Code-Window + Live-Preview rechts | Beweis-Slides |
|
||||||
|
| `.feature-grid` | 4×2 Tiles, `.wide` = span 2 | Feature-Showcase |
|
||||||
|
| `.closing-slide` | Hell, Space-between, Blur-Blob | Letzter Slide |
|
||||||
|
| `.section-slide` | Dunkel, zentriert, riesige „Geister-Zahl" im Hintergrund | Kapitel-Trenner bei Themenwechsel |
|
||||||
|
| `.game-wrap` | Satz/Lücke + Options-Buttons, JS-gesteuert | Mitmach-Slides (Publikum rät mit) |
|
||||||
|
|
||||||
|
Die Slide-Nummer kommt automatisch aus `data-num="04 / 10"` am `.slide-content` (gerendert per `::before`). Sie muss beim Umbau des Decks von Hand nachgezogen werden. `.section-slide`, `.versus`, `.code-slide`, Title- und Closing-Slide tragen bewusst **kein** `data-num` — sie zählen als „Zwischenspiel", nicht als nummerierter Inhaltspunkt.
|
||||||
|
|
||||||
|
### Kapitel-Trenner (`.section-slide`)
|
||||||
|
|
||||||
|
Bei mehr als ~5–6 Slides pro Thema lohnt sich ein eigener Trenner-Slide vor jedem Themenwechsel — er gibt dem Publikum eine Atempause und macht die Dramaturgie sichtbar. Aufbau: riesige, transparente Zahl mit Outline (`-webkit-text-stroke`) im Hintergrund, davor ein roter Mono-Kicker („Kapitel 03 / 07") und eine kurze, große Headline plus ein Satz Unterzeile. Siehe `PATTERNS.md` Nr. 7 für den Code.
|
||||||
|
|
||||||
|
Faustregel: ein Trenner pro inhaltlichem Kapitel, nicht pro Slide. Bei 30+ Slides sind 6–8 Trenner ein guter Rhythmus — mehr wirkt zerhackt, weniger verliert die Orientierung.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Motion
|
||||||
|
|
||||||
|
### Reveal-System
|
||||||
|
|
||||||
|
Elemente mit `.reveal` starten bei `opacity: 0; translateY(40px)` und fahren ein, sobald die Slide `.visible` bekommt. Die Staffelung läuft über `.d1`–`.d7` (100 ms → 820 ms, Schritte von ~120 ms). `.reveal-slide-x` macht dasselbe horizontal (−60 px), gedacht für die linke Versus-Spalte.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<span class="slide-eyebrow reveal d1">Das Problem</span>
|
||||||
|
<h2 class="slide-title reveal d2">Warum das <span class="accent">nervt.</span></h2>
|
||||||
|
<p class="slide-lead reveal d3">…</p>
|
||||||
|
<div class="pain-card reveal d4">…</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Regel:** Reihenfolge im DOM = Reihenfolge der Delays. Nie `d5` vor `d3` einfahren lassen. Mehrere gleichrangige Karten dürfen sich ein Delay teilen.
|
||||||
|
|
||||||
|
### Easings
|
||||||
|
|
||||||
|
| Token | Kurve | Wofür |
|
||||||
|
|---|---|---|
|
||||||
|
| `--ease-out-expo` | `cubic-bezier(0.16, 1, 0.3, 1)` | Standard für alles, was einfährt. Schnell rein, weich aus. |
|
||||||
|
| `--ease-swift` | `cubic-bezier(0.65, 0, 0.35, 1)` | Kurze, entschiedene Bewegungen (Durchstreichen) |
|
||||||
|
|
||||||
|
### Timings
|
||||||
|
|
||||||
|
- Reveal-Einfahrt: **0.9 s**
|
||||||
|
- Hover-Feedback: **0.12–0.15 s** (muss sich sofort anfühlen)
|
||||||
|
- Chart-Balken: **1.2 s** mit gestaffeltem `transition-delay` von 0.4 s bis 0.9 s
|
||||||
|
- Counter-Zählung: **1.6 s**, Easing `1 - (1-p)³`
|
||||||
|
- Loops (Blob, Ticker, Cube): **4–12 s**, `linear` oder `ease-in-out`, damit sie nie „getaktet" wirken
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
Animiert werden **nur** `transform` und `opacity` — die laufen auf der GPU. Kein `width`, `height`, `top`, `left` in Transitions. Ausnahme: `.demo-charts-bars` animiert `height`, aber auf 6 winzigen Divs, das ist vertretbar.
|
||||||
|
|
||||||
|
`prefers-reduced-motion` ist global verdrahtet: Animationen gehen auf 0.01 ms, Transitions auf 0.2 s. Slides bleiben voll funktionsfähig, nur ohne Show.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Interaktion
|
||||||
|
|
||||||
|
| Trigger | Wirkung |
|
||||||
|
|---|---|
|
||||||
|
| `→`, `Leertaste`, `PageDown` | nächster Slide |
|
||||||
|
| `←`, `PageUp` | vorheriger Slide |
|
||||||
|
| `Home` / `End` | erster / letzter Slide |
|
||||||
|
| Mausrad | Slide-Wechsel, 900 ms Cooldown |
|
||||||
|
| Swipe (> 60 px) | Slide-Wechsel auf Touch |
|
||||||
|
| `E` | Edit-Modus |
|
||||||
|
| `C` | Zum Präsentations-Konfigurator (`config.html`), falls vorhanden |
|
||||||
|
| Hotzone oben links | blendet Edit- und Config-Button ein (400 ms Grace-Period) |
|
||||||
|
| Klick auf `.term` | öffnet Begriffs-Tooltip |
|
||||||
|
|
||||||
|
**Edit-Modus:** Setzt `contenteditable` auf alle Text-Elemente, markiert sie mit rotem Dashed-Outline. Beim Verlassen wird der Stage-HTML in `localStorage` geschrieben. Das Zurückladen ist **absichtlich auskommentiert** — restauriertes HTML kollidiert mit den Animations-States. Der Edit-Modus ist ein Live-Korrektur-Werkzeug für die Bühne („Kunde will hier ein anderes Wort"), keine Persistenz-Schicht.
|
||||||
|
|
||||||
|
**Begriffs-Tooltips (`.term`):** Für Laien-Publikum unentbehrlich — Fachbegriffe (Token, LLM, MCP, GPT, ANI/AGI, …) bekommen eine gepunktete rote Unterstreichung plus kleines „?"-Badge. Klick öffnet eine kurze Erklärung. Das Tooltip-Element lebt **außerhalb** der 1920er-Bühne (ein einziges `#globalTooltip`, direkt an `<body>` gehängt, per JS in echten Viewport-Pixeln positioniert) — sonst schneiden `overflow:hidden`-Container wie `.pain-card` oder `.feature-tile` es ab. Es klappt automatisch ober- oder unterhalb des Begriffs aus, je nachdem was im Fenster noch Platz hat, und schließt bei Klick daneben, `Esc`, Resize oder Slide-Wechsel. Siehe `PATTERNS.md` Nr. 8.
|
||||||
|
|
||||||
|
**Präsentations-Konfigurator:** Bei Decks mit vielen Slides (Workshop-Varianten, 45/60/90-Minuten-Kürzungen) lohnt sich `config.html` + `slides-manifest.js` statt Slides von Hand zu löschen. Der Konfigurator listet alle Slides gruppiert nach Kapitel mit Checkbox; die Auswahl wird beim Wechsel zur Präsentation als `?slides=1,4,5,…`-Parameter an die Deck-URL gehängt (nicht nur `localStorage` — das wird zwischen einzelnen `file://`-Dateien nicht überall geteilt, vor allem Safari trennt es strikt). Titel- und Closing-Slide sind im Manifest als `locked: true` markiert und immer aktiv. Siehe `PATTERNS.md` Nr. 11.
|
||||||
|
|
||||||
|
**Ambient-Sound (optional, nur Titelfolie):** Ein per Web Audio API erzeugter Klangteppich (kein externes Audio-File) kann vor Vortragsbeginn Spannung aufbauen. Läuft nur, während die Titelfolie aktiv ist, blendet beim Verlassen automatisch aus. Browser blockieren Audio ohne Nutzer-Geste — deshalb immer über einen expliziten Icon-Button starten, nie automatisch. Siehe `PATTERNS.md` Nr. 10.
|
||||||
|
|
||||||
|
**Hover-Regel:** Cards heben sich um 4–8 px (`translateY`) und wechseln die Border auf Rot. `.pain-card` zieht zusätzlich eine Radial-Aura hinter dem Cursor her (`--mx`/`--my` per JS gesetzt).
|
||||||
|
|
||||||
|
**Wichtig bei Maus-Koordinaten:** Die Bühne ist skaliert. `e.clientX` sind Bildschirm-Pixel, das Element lebt im 1920er-Raum. Immer durch den Scale-Faktor teilen:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
const scale = rect.width / el.offsetWidth;
|
||||||
|
const x = (e.clientX - rect.left) / scale;
|
||||||
|
```
|
||||||
|
|
||||||
|
Bei `.pain-card` fällt das nicht auf, weil dort in Prozent gerechnet wird — beim Ripple des Demo-Buttons schon.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Print / PDF
|
||||||
|
|
||||||
|
`@media print` löst die fixe Bühne auf: `.deck-stage` verliert `transform`, jede Slide wird `position: relative` mit `break-after: page`. HUD, Progress-Bar und Edit-Controls verschwinden. Export: **Strg/Cmd+P → Ziel „Als PDF sichern" → Papierformat Querformat, Ränder: keine, Hintergrundgrafiken: an.**
|
||||||
|
|
||||||
|
Ergebnis: eine Seite pro Slide. Animierte Inhalte frieren im jeweiligen Endzustand ein — Live-Daten (WebSocket) zeigen den letzten Wert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Neues Deck bauen
|
||||||
|
|
||||||
|
1. `deck-template.html` kopieren und umbenennen.
|
||||||
|
2. `<title>`, Brand-Mark, Datum, Sprecher-Name, Kontakt anpassen.
|
||||||
|
3. Slides aus `PATTERNS.md` zusammenstellen. Bewährte Dramaturgie:
|
||||||
|
**Titel → Problem → Vergleich → Zahlen → Beweis (Live) → Der ehrliche Deal → Closing.**
|
||||||
|
4. `data-num` an allen `.slide-content` auf die neue Gesamtzahl ziehen. Das HUD zählt selbst, `data-num` nicht.
|
||||||
|
5. Durchklicken bei 1920×1080 **und** bei kleinem Fenster. Der Scale muss sauber greifen, Text darf nirgends aus einer Card laufen.
|
||||||
|
6. Print-Preview prüfen, bevor das Deck rausgeht.
|
||||||
|
7. Bei vielen Slides oder mehreren Vortrags-Längen (45/60/90 Min.): `slides-manifest.js` mit der finalen Slide-Liste anlegen (ein Eintrag pro `<section class="slide" data-slide-id="…">`, gleiche Reihenfolge), `config.html` aus dem Beispiel-Projekt kopieren. Damit lässt sich die Slide-Auswahl kuratieren, ohne Markup zu löschen.
|
||||||
|
|
||||||
|
### Kunden-Theme
|
||||||
|
|
||||||
|
Nur die Tokens in `:root` tauschen: Signalfarbe, ggf. Display-Font. Alles andere bleibt. Wenn die Signalfarbe hell ist (Gelb, Cyan), zusätzlich prüfen: Kontrast von `.demo-button` (weißer Text auf Signalfarbe) und die Glow-Shadows — die brauchen dann weniger Alpha.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Do / Don't
|
||||||
|
|
||||||
|
**Do**
|
||||||
|
|
||||||
|
- Ein Gedanke pro Slide. Die Headline sagt ihn, alles andere belegt ihn.
|
||||||
|
- Große Zahlen groß machen. 220 px ist kein Fehler.
|
||||||
|
- Weißraum halten. Die 120 px Padding sind Design, kein Verschnitt.
|
||||||
|
- Live-Elemente bekommen immer einen Fallback (siehe WebSocket-Fallback: simuliert Ticks, wenn kein Netz da ist). **Eine Demo, die auf der Bühne offline abstürzt, ist ein Eigentor gegen die eigene These.**
|
||||||
|
- Ein Argument pro Slide beweisen statt behaupten.
|
||||||
|
|
||||||
|
**Don't**
|
||||||
|
|
||||||
|
- Keine Bullet-Point-Listen aus Gewohnheit. Wenn eine Liste, dann mit gestalteten Markern (`×` / `→`).
|
||||||
|
- Keine Hex-Codes im Markup. Token benutzen.
|
||||||
|
- Kein Scroll innerhalb einer Slide. Passt es nicht, ist es zu viel Inhalt — nicht zu wenig Platz.
|
||||||
|
- Keine externen Libraries. Das Deck beweist, dass Vanilla reicht.
|
||||||
|
- Kein Rot als Fläche hinter Text.
|
||||||
|
- Keine Animation ohne Grund. Jeder Loop, den niemand anschaut, ist verbranntes Budget an Aufmerksamkeit.
|
||||||
1010
PATTERNS.md
Normal file
1010
PATTERNS.md
Normal file
File diff suppressed because it is too large
Load Diff
53
README.md
53
README.md
@@ -0,0 +1,53 @@
|
|||||||
|
# KI verstehen – von den Anfängen bis zu Agenten-Schwärmen
|
||||||
|
|
||||||
|
CANCOM-Vortragsdeck im HTML-Format. Ein File, kein Build, kein Bundler –
|
||||||
|
per Doppelklick lauffähig, per Mail/Teams/SharePoint verteilbar.
|
||||||
|
|
||||||
|
## Dateien
|
||||||
|
|
||||||
|
| Datei | Zweck |
|
||||||
|
|---|---|
|
||||||
|
| `ki-verstehen.html` | Das eigentliche Deck. 36 Slides, 1920×1080, per `E` editierbar. |
|
||||||
|
| `config.html` | Präsentations-Konfigurator: Slides für den Vortrag ein-/ausblenden. |
|
||||||
|
| `slides-manifest.js` | Gemeinsame Titel-/Reihenfolge-Liste, von Deck **und** Konfigurator geladen. |
|
||||||
|
| `font-vorschau.html` | Vergleichsseite für Headline-Schriften (Rückblick auf die Font-Auswahl). |
|
||||||
|
| `DESIGN.md` | Design-System: Tokens, Typografie, Layout, Motion – verbindlich für Umbauten. |
|
||||||
|
| `PATTERNS.md` | Copy-Paste-Bausteine für einzelne Slide-Typen. |
|
||||||
|
| `deck-template.html` | Leerer Startpunkt für ein neues Deck im selben Look. |
|
||||||
|
|
||||||
|
## Präsentieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
open ki-verstehen.html
|
||||||
|
```
|
||||||
|
|
||||||
|
- `→` / Leertaste / Mausrad / Swipe: nächste Slide · `←`: vorherige · `Home`/`End`: Anfang/Ende
|
||||||
|
- `E`: Live-Editor (Text direkt auf der Bühne korrigieren, z. B. bei Kundenwunsch „anderes Wort")
|
||||||
|
- `C` oder das ⚙-Symbol oben links (Hotzone): zum Konfigurator
|
||||||
|
- 🔈-Symbol unten rechts auf der Titelfolie: erzeugter Ambient-Sound (Web Audio, keine externe Datei)
|
||||||
|
|
||||||
|
## Slides für einen Vortrag auswählen
|
||||||
|
|
||||||
|
`config.html` öffnen, Häkchen setzen/entfernen, auf „Speichern & zur Präsentation" klicken.
|
||||||
|
Die Auswahl wird als URL-Parameter ans Deck übergeben (zuverlässig auch bei `file://`, wo
|
||||||
|
`localStorage` zwischen einzelnen HTML-Dateien nicht immer geteilt wird – v. a. Safari).
|
||||||
|
Titelfolie und Closing sind fest verankert und lassen sich nicht abwählen.
|
||||||
|
|
||||||
|
## Aufbau des Decks (36 Slides in 7 Kapiteln)
|
||||||
|
|
||||||
|
1. **Titel** + **Hook** (Handzeichen-Frage)
|
||||||
|
2. **Kapitel 1 · Wie alles begann** – Zeitstrahl, verschachtelte Kreise (KI/ML/DL/GenAI), wie eine Maschine lernt, ANI vs. AGI
|
||||||
|
3. **Kapitel 2 · Wie ein LLM funktioniert** – Mitmach-Spiel „Alle meine Entchen …", Autovervollständigung, Token & Parameter, Reasoning-Modelle, System-/User-Prompt & Context, Kontext-Limit & Compact
|
||||||
|
4. **Kapitel 3 · Die Modell-Landschaft** – woher das Wissen kommt, Anbieter-Übersicht, Geschäftsmodelle, SEO vs. GEO, wo man Modelle findet
|
||||||
|
5. **Kapitel 4 · Vom Chatbot zum Agenten** – Eskalationsleiter, MCP
|
||||||
|
6. **Kapitel 5 · Die Grenzen der KI** – Schwächen-Übersicht, Halluzination im Detail
|
||||||
|
7. **Kapitel 6 · Sicherheit im Umgang mit KI** – Prompt Injection, weitere Security-Punkte
|
||||||
|
8. **Kapitel 7 · Praxis & Ausblick** – Tipps, Ausblick, Glossar, Closing
|
||||||
|
|
||||||
|
Begriffe wie *Token*, *LLM*, *MCP*, *GPT*, *ANI/AGI* u. a. sind im Fließtext anklickbar
|
||||||
|
(gepunktete Unterstreichung) und öffnen eine kurze Erklärung.
|
||||||
|
|
||||||
|
## Neues Deck bauen
|
||||||
|
|
||||||
|
Siehe `DESIGN.md` Abschnitt 8 und `PATTERNS.md` für Copy-Paste-Bausteine.
|
||||||
|
`deck-template.html` kopieren, umbenennen, Slides zusammenstellen.
|
||||||
|
|||||||
263
config.html
Normal file
263
config.html
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
<!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);
|
||||||
|
});
|
||||||
|
|
||||||
|
let enabled = loadSlideConfig();
|
||||||
|
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>
|
||||||
961
deck-template.html
Normal file
961
deck-template.html
Normal file
@@ -0,0 +1,961 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>CANCOM · [DECK-TITEL]</title>
|
||||||
|
|
||||||
|
<!-- General Sans für Headlines, Satoshi für Body, JetBrains Mono für alles Technische -->
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* ===========================================
|
||||||
|
THEME TOKENS — für ein Kunden-Theme nur hier anfassen
|
||||||
|
=========================================== */
|
||||||
|
: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);
|
||||||
|
--accent-cool: #4CC9F0;
|
||||||
|
--accent-warm: #FFB627;
|
||||||
|
|
||||||
|
--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);
|
||||||
|
--ease-swift: cubic-bezier(0.65, 0, 0.35, 1);
|
||||||
|
--stage-bg: #000;
|
||||||
|
--slide-bg: var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
VIEWPORT-BASE (FIXED 16:9 STAGE) — NICHT ÄNDERN
|
||||||
|
=========================================== */
|
||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--stage-bg);
|
||||||
|
font-family: var(--font-body);
|
||||||
|
color: var(--paper);
|
||||||
|
}
|
||||||
|
.deck-viewport { position: fixed; inset: 0; overflow: hidden; background: var(--stage-bg); }
|
||||||
|
.deck-stage {
|
||||||
|
position: absolute; left: 0; top: 0;
|
||||||
|
width: 1920px; height: 1080px;
|
||||||
|
overflow: hidden; transform-origin: 0 0;
|
||||||
|
background: var(--slide-bg);
|
||||||
|
}
|
||||||
|
.slide {
|
||||||
|
position: absolute; inset: 0;
|
||||||
|
width: 1920px; height: 1080px;
|
||||||
|
overflow: hidden; display: block;
|
||||||
|
visibility: hidden; opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
background: var(--slide-bg);
|
||||||
|
}
|
||||||
|
.slide.active, .slide.visible {
|
||||||
|
visibility: visible; opacity: 1;
|
||||||
|
pointer-events: auto; z-index: 1;
|
||||||
|
}
|
||||||
|
img, video, canvas, svg { max-width: 100%; max-height: 100%; }
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
html, body { width: 1920px; height: auto; overflow: visible; background: #fff; }
|
||||||
|
.deck-viewport { position: static; overflow: visible; background: #fff; }
|
||||||
|
.deck-stage { position: static; width: auto; height: auto; transform: none !important; background: none; }
|
||||||
|
.slide { position: relative; visibility: visible !important; opacity: 1 !important;
|
||||||
|
pointer-events: auto !important; width: 1920px; height: 1080px;
|
||||||
|
break-after: page; page-break-after: always; }
|
||||||
|
.slide:last-child { break-after: auto; page-break-after: auto; }
|
||||||
|
.deck-controls, .deck-hud, .edit-hotzone, .edit-toggle { display: none !important; }
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*, *::before, *::after {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
transition-duration: 0.2s !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
HUD & CHROME (außerhalb der Slides)
|
||||||
|
=========================================== */
|
||||||
|
.deck-hud {
|
||||||
|
position: absolute;
|
||||||
|
left: 48px; bottom: 32px;
|
||||||
|
z-index: 900;
|
||||||
|
display: flex; align-items: center; gap: 18px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--paper-mute);
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.deck-hud .dot { width: 8px; height: 8px; background: var(--cancom-red); border-radius: 50%;
|
||||||
|
box-shadow: 0 0 12px var(--cancom-red-glow); animation: pulse 1.6s infinite; }
|
||||||
|
.deck-progress {
|
||||||
|
position: fixed; left: 0; top: 0;
|
||||||
|
height: 3px; width: 100%;
|
||||||
|
background: rgba(255,255,255,0.06);
|
||||||
|
z-index: 900;
|
||||||
|
}
|
||||||
|
.deck-progress .bar {
|
||||||
|
height: 100%; width: 0;
|
||||||
|
background: linear-gradient(90deg, var(--cancom-red), #ff5060);
|
||||||
|
box-shadow: 0 0 20px var(--cancom-red-glow);
|
||||||
|
transition: width 0.6s var(--ease-out-expo);
|
||||||
|
}
|
||||||
|
@keyframes pulse {
|
||||||
|
0%,100% { opacity: 1; transform: scale(1); }
|
||||||
|
50% { opacity: 0.4; transform: scale(1.4); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Edit-Hotzone + Toggle (JS-gesteuert) */
|
||||||
|
.edit-hotzone {
|
||||||
|
position: fixed; top: 0; left: 0;
|
||||||
|
width: 80px; height: 80px; z-index: 10000; cursor: pointer;
|
||||||
|
}
|
||||||
|
.edit-toggle {
|
||||||
|
position: fixed; top: 18px; left: 18px;
|
||||||
|
width: 44px; height: 44px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.15);
|
||||||
|
background: rgba(20,20,28,0.85);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
color: var(--paper);
|
||||||
|
font-size: 18px; cursor: pointer;
|
||||||
|
opacity: 0; pointer-events: none;
|
||||||
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
.edit-toggle.show, .edit-toggle.active { opacity: 1; pointer-events: auto; }
|
||||||
|
.edit-toggle.active { background: var(--cancom-red); border-color: var(--cancom-red); }
|
||||||
|
[contenteditable="true"] {
|
||||||
|
outline: 2px dashed var(--cancom-red);
|
||||||
|
outline-offset: 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
REVEAL ANIMATION SYSTEM
|
||||||
|
DOM-Reihenfolge = Delay-Reihenfolge. Nie d5 vor d3.
|
||||||
|
=========================================== */
|
||||||
|
.reveal {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(40px);
|
||||||
|
transition: opacity 0.9s var(--ease-out-expo),
|
||||||
|
transform 0.9s var(--ease-out-expo);
|
||||||
|
}
|
||||||
|
.slide.visible .reveal { opacity: 1; transform: translateY(0); }
|
||||||
|
.slide.visible .reveal.d1 { transition-delay: 0.10s; }
|
||||||
|
.slide.visible .reveal.d2 { transition-delay: 0.22s; }
|
||||||
|
.slide.visible .reveal.d3 { transition-delay: 0.34s; }
|
||||||
|
.slide.visible .reveal.d4 { transition-delay: 0.46s; }
|
||||||
|
.slide.visible .reveal.d5 { transition-delay: 0.58s; }
|
||||||
|
.slide.visible .reveal.d6 { transition-delay: 0.70s; }
|
||||||
|
.slide.visible .reveal.d7 { transition-delay: 0.82s; }
|
||||||
|
|
||||||
|
.reveal-slide-x {
|
||||||
|
opacity: 0; transform: translateX(-60px);
|
||||||
|
transition: opacity 0.9s var(--ease-out-expo), transform 0.9s var(--ease-out-expo);
|
||||||
|
}
|
||||||
|
.slide.visible .reveal-slide-x { opacity: 1; transform: translateX(0); }
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
TITLE SLIDE
|
||||||
|
=========================================== */
|
||||||
|
.title-slide {
|
||||||
|
background: radial-gradient(ellipse at 20% 30%, #1a0409 0%, var(--ink) 55%),
|
||||||
|
var(--ink);
|
||||||
|
padding: 120px;
|
||||||
|
display: flex; flex-direction: column; justify-content: space-between;
|
||||||
|
}
|
||||||
|
.title-canvas { position: absolute; inset: 0; z-index: 0; opacity: 0.9; }
|
||||||
|
.title-inner { position: relative; z-index: 2; }
|
||||||
|
.title-topbar {
|
||||||
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 18px; letter-spacing: 0.28em;
|
||||||
|
text-transform: uppercase; color: var(--paper-mute);
|
||||||
|
}
|
||||||
|
.brand-mark {
|
||||||
|
display: inline-flex; align-items: baseline; gap: 14px;
|
||||||
|
font-family: var(--font-display); font-weight: 700;
|
||||||
|
color: var(--paper); font-size: 26px; letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
.brand-mark::before {
|
||||||
|
content: ""; width: 14px; height: 14px;
|
||||||
|
background: var(--cancom-red);
|
||||||
|
display: inline-block;
|
||||||
|
box-shadow: 0 0 20px var(--cancom-red-glow);
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
.title-headline {
|
||||||
|
position: relative; z-index: 2;
|
||||||
|
font-family: var(--font-display); font-weight: 700;
|
||||||
|
font-size: 220px; line-height: 0.88;
|
||||||
|
letter-spacing: -0.045em;
|
||||||
|
color: var(--paper);
|
||||||
|
}
|
||||||
|
.title-headline .accent { color: var(--cancom-red); display: inline-block; position: relative; }
|
||||||
|
.title-headline .accent::after {
|
||||||
|
content: ""; position: absolute; left: 0; bottom: 8px;
|
||||||
|
width: 100%; height: 8px;
|
||||||
|
background: var(--cancom-red);
|
||||||
|
transform: scaleX(0); transform-origin: left;
|
||||||
|
animation: underline-in 1.4s var(--ease-out-expo) 1.2s forwards;
|
||||||
|
}
|
||||||
|
@keyframes underline-in { to { transform: scaleX(1); } }
|
||||||
|
|
||||||
|
.title-strike { display: inline-block; position: relative; color: var(--paper-mute); }
|
||||||
|
.title-strike::after {
|
||||||
|
content: ""; position: absolute; left: -4%; top: 52%;
|
||||||
|
width: 108%; height: 10px;
|
||||||
|
background: var(--cancom-red);
|
||||||
|
transform: scaleX(0); transform-origin: left;
|
||||||
|
animation: strike-in 0.8s var(--ease-swift) 0.8s forwards;
|
||||||
|
}
|
||||||
|
@keyframes strike-in { to { transform: scaleX(1); } }
|
||||||
|
|
||||||
|
.title-meta {
|
||||||
|
position: relative; z-index: 2;
|
||||||
|
display: flex; justify-content: space-between; align-items: flex-end;
|
||||||
|
font-family: var(--font-mono); font-size: 16px;
|
||||||
|
color: var(--paper-mute); letter-spacing: 0.14em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.title-meta .huge {
|
||||||
|
font-family: var(--font-display); font-size: 90px;
|
||||||
|
color: var(--cancom-red); letter-spacing: -0.03em;
|
||||||
|
text-transform: none; line-height: 1;
|
||||||
|
text-shadow: 0 0 40px var(--cancom-red-glow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
GENERIC CONTENT SLIDE — der Standard-Fall
|
||||||
|
=========================================== */
|
||||||
|
.slide-content {
|
||||||
|
padding: 100px 120px;
|
||||||
|
display: flex; flex-direction: column;
|
||||||
|
height: 100%; position: relative;
|
||||||
|
}
|
||||||
|
.slide-content::before {
|
||||||
|
content: attr(data-num);
|
||||||
|
position: absolute;
|
||||||
|
top: 60px; right: 120px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 14px; letter-spacing: 0.2em;
|
||||||
|
color: var(--paper-mute);
|
||||||
|
}
|
||||||
|
.slide-eyebrow {
|
||||||
|
font-family: var(--font-mono); font-size: 16px;
|
||||||
|
letter-spacing: 0.28em; text-transform: uppercase;
|
||||||
|
color: var(--cancom-red);
|
||||||
|
display: inline-flex; align-items: center; gap: 14px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
.slide-eyebrow::before { content: ""; width: 40px; height: 2px; background: var(--cancom-red); }
|
||||||
|
.slide-title {
|
||||||
|
font-family: var(--font-display); font-weight: 700;
|
||||||
|
font-size: 112px; line-height: 0.94;
|
||||||
|
letter-spacing: -0.035em;
|
||||||
|
color: var(--paper);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.slide-title .accent { color: var(--cancom-red); }
|
||||||
|
.slide-lead {
|
||||||
|
font-size: 30px; line-height: 1.4;
|
||||||
|
color: var(--paper-dim);
|
||||||
|
max-width: 1300px;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
CARD GRID
|
||||||
|
=========================================== */
|
||||||
|
.pain-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 32px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.pain-card {
|
||||||
|
border: 1px solid var(--ink-line);
|
||||||
|
background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(0,0,0,0.15));
|
||||||
|
padding: 44px;
|
||||||
|
display: flex; flex-direction: column; justify-content: space-between;
|
||||||
|
position: relative; overflow: hidden;
|
||||||
|
transition: transform 0.15s ease-out, border-color 0.12s ease-out;
|
||||||
|
}
|
||||||
|
.pain-card:hover { transform: translateY(-8px); border-color: var(--cancom-red); }
|
||||||
|
.pain-card::before {
|
||||||
|
content: ""; position: absolute; inset: 0;
|
||||||
|
background: radial-gradient(400px circle at var(--mx, 50%) var(--my, 50%), var(--cancom-red-dim), transparent 70%);
|
||||||
|
opacity: 0; transition: opacity 0.12s ease-out;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.pain-card:hover::before { opacity: 1; }
|
||||||
|
.pain-num {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 14px; color: var(--paper-mute);
|
||||||
|
letter-spacing: 0.24em;
|
||||||
|
}
|
||||||
|
.pain-title {
|
||||||
|
font-family: var(--font-display); font-weight: 600;
|
||||||
|
font-size: 42px; color: var(--paper);
|
||||||
|
line-height: 1.05; letter-spacing: -0.02em;
|
||||||
|
margin: 24px 0 16px;
|
||||||
|
}
|
||||||
|
.pain-body { font-size: 20px; line-height: 1.5; color: var(--paper-dim); }
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
VERSUS SLIDE — Split Layout
|
||||||
|
=========================================== */
|
||||||
|
.versus { position: absolute; inset: 0; display: grid; grid-template-columns: 1fr 1fr; }
|
||||||
|
.versus-side {
|
||||||
|
padding: 120px 90px;
|
||||||
|
position: relative;
|
||||||
|
display: flex; flex-direction: column; justify-content: center;
|
||||||
|
}
|
||||||
|
.versus-side.left {
|
||||||
|
background: linear-gradient(135deg, #1a1a22 0%, #0e0e14 100%);
|
||||||
|
color: var(--paper);
|
||||||
|
}
|
||||||
|
.versus-side.right {
|
||||||
|
background: linear-gradient(135deg, #24060d 0%, var(--ink) 100%);
|
||||||
|
color: var(--paper);
|
||||||
|
border-left: 3px solid var(--cancom-red);
|
||||||
|
}
|
||||||
|
.versus-label {
|
||||||
|
font-family: var(--font-mono); font-size: 16px;
|
||||||
|
letter-spacing: 0.3em; text-transform: uppercase;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.versus-side.left .versus-label { color: rgba(255,255,255,0.55); }
|
||||||
|
.versus-side.right .versus-label { color: var(--cancom-red); }
|
||||||
|
.versus-headline {
|
||||||
|
font-family: var(--font-display); font-weight: 700;
|
||||||
|
font-size: 100px; line-height: 0.92;
|
||||||
|
letter-spacing: -0.035em;
|
||||||
|
margin-bottom: 48px;
|
||||||
|
}
|
||||||
|
.versus-list { list-style: none; }
|
||||||
|
.versus-list li {
|
||||||
|
padding: 18px 0;
|
||||||
|
font-size: 24px; line-height: 1.35;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||||
|
display: flex; gap: 18px; align-items: baseline;
|
||||||
|
}
|
||||||
|
.versus-side.left .versus-list li::before {
|
||||||
|
content: "×"; color: #a0a0ac; font-size: 28px; font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
.versus-side.right .versus-list li::before {
|
||||||
|
content: "→"; color: var(--cancom-red); font-size: 22px; font-family: var(--font-mono);
|
||||||
|
}
|
||||||
|
.versus-vs {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%; left: 50%;
|
||||||
|
width: 140px; height: 140px;
|
||||||
|
margin: -70px 0 0 -70px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--cancom-red);
|
||||||
|
color: var(--paper);
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
font-family: var(--font-display); font-weight: 700;
|
||||||
|
font-size: 46px; letter-spacing: -0.02em;
|
||||||
|
box-shadow: 0 0 60px var(--cancom-red-glow), inset 0 0 40px rgba(0,0,0,0.2);
|
||||||
|
z-index: 2;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.9s var(--ease-out-expo);
|
||||||
|
}
|
||||||
|
.slide.visible .versus-vs { opacity: 1; }
|
||||||
|
.versus-vs::before {
|
||||||
|
content: ""; position: absolute; inset: -10px;
|
||||||
|
border: 2px solid var(--cancom-red);
|
||||||
|
border-radius: 50%;
|
||||||
|
opacity: 0.4;
|
||||||
|
animation: expand-pulse 2s infinite;
|
||||||
|
}
|
||||||
|
@keyframes expand-pulse {
|
||||||
|
0% { transform: scale(1); opacity: 0.5; }
|
||||||
|
100% { transform: scale(1.4); opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
COUNTER SLIDE — animierte Zahlen
|
||||||
|
=========================================== */
|
||||||
|
.counter-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 60px; flex: 1; align-items: center; }
|
||||||
|
.counter-item { text-align: left; }
|
||||||
|
.counter-value {
|
||||||
|
font-family: var(--font-display); font-weight: 700;
|
||||||
|
font-size: 220px; line-height: 0.9;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
color: var(--cancom-red);
|
||||||
|
text-shadow: 0 0 60px var(--cancom-red-glow);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.counter-value .unit { font-size: 80px; color: var(--paper); text-shadow: none; }
|
||||||
|
.counter-label {
|
||||||
|
font-family: var(--font-mono); font-size: 18px;
|
||||||
|
letter-spacing: 0.16em; text-transform: uppercase;
|
||||||
|
color: var(--paper-mute);
|
||||||
|
margin-top: 16px; line-height: 1.5;
|
||||||
|
}
|
||||||
|
.counter-caption { font-size: 22px; color: var(--paper-dim); margin-top: 12px; max-width: 380px; line-height: 1.4; }
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
CODE SLIDE
|
||||||
|
=========================================== */
|
||||||
|
.code-slide { padding: 100px 120px; display: flex; gap: 80px; height: 100%; }
|
||||||
|
.code-left { flex: 1; display: flex; flex-direction: column; justify-content: center; }
|
||||||
|
.code-right { flex: 1.2; display: flex; flex-direction: column; }
|
||||||
|
.code-window {
|
||||||
|
background: #0d0d13;
|
||||||
|
border: 1px solid var(--ink-line);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 40px 100px rgba(0,0,0,0.6), 0 0 0 1px rgba(228,0,43,0.15);
|
||||||
|
flex: 1;
|
||||||
|
display: flex; flex-direction: column;
|
||||||
|
}
|
||||||
|
.code-tabs {
|
||||||
|
background: #16161e;
|
||||||
|
padding: 14px 20px;
|
||||||
|
display: flex; align-items: center; gap: 12px;
|
||||||
|
border-bottom: 1px solid var(--ink-line);
|
||||||
|
}
|
||||||
|
.code-tabs .dots { display: flex; gap: 8px; }
|
||||||
|
.code-tabs .dots span { width: 12px; height: 12px; border-radius: 50%; background: #35353f; }
|
||||||
|
.code-tabs .dots span:first-child { background: #ff5f57; }
|
||||||
|
.code-tabs .dots span:nth-child(2) { background: #febc2e; }
|
||||||
|
.code-tabs .dots span:nth-child(3) { background: #28c840; }
|
||||||
|
.code-tabs .filename { font-family: var(--font-mono); font-size: 14px; color: var(--paper-mute); margin-left: 12px; }
|
||||||
|
.code-tabs .live {
|
||||||
|
margin-left: auto;
|
||||||
|
font-family: var(--font-mono); font-size: 12px;
|
||||||
|
letter-spacing: 0.2em; color: var(--cancom-red);
|
||||||
|
display: flex; align-items: center; gap: 8px;
|
||||||
|
}
|
||||||
|
.code-tabs .live::before {
|
||||||
|
content: ""; width: 8px; height: 8px; background: var(--cancom-red);
|
||||||
|
border-radius: 50%; animation: pulse 1.2s infinite;
|
||||||
|
box-shadow: 0 0 10px var(--cancom-red-glow);
|
||||||
|
}
|
||||||
|
.code-body {
|
||||||
|
padding: 32px;
|
||||||
|
font-family: var(--font-mono); font-size: 20px;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: #d6d6dc;
|
||||||
|
flex: 1; overflow: hidden;
|
||||||
|
}
|
||||||
|
.code-body .kw { color: #ff5060; }
|
||||||
|
.code-body .fn { color: #4CC9F0; }
|
||||||
|
.code-body .str { color: #ffb627; }
|
||||||
|
.code-body .com { color: #5a5a68; font-style: italic; }
|
||||||
|
.code-body .num { color: #b5f4a5; }
|
||||||
|
.code-body .prop { color: #d0a8ff; }
|
||||||
|
|
||||||
|
.code-preview {
|
||||||
|
margin-top: 32px;
|
||||||
|
padding: 40px;
|
||||||
|
background: linear-gradient(135deg, #16161e, #0d0d13);
|
||||||
|
border: 1px solid var(--ink-line);
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
.demo-button {
|
||||||
|
padding: 20px 40px;
|
||||||
|
background: var(--cancom-red);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-family: var(--font-display); font-weight: 600;
|
||||||
|
font-size: 22px; letter-spacing: 0.02em;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative; overflow: hidden;
|
||||||
|
transition: transform 0.3s var(--ease-out-expo);
|
||||||
|
box-shadow: 0 10px 40px var(--cancom-red-glow);
|
||||||
|
}
|
||||||
|
.demo-button:hover { transform: translateY(-3px) scale(1.03); }
|
||||||
|
.demo-button .ripple {
|
||||||
|
position: absolute;
|
||||||
|
left: var(--rx, 50%);
|
||||||
|
top: var(--ry, 50%);
|
||||||
|
width: 0; height: 0;
|
||||||
|
background: rgba(255,255,255,0.35);
|
||||||
|
border-radius: 50%;
|
||||||
|
transform: translate(-50%,-50%);
|
||||||
|
pointer-events: none;
|
||||||
|
animation: ripple-out 0.7s ease-out forwards;
|
||||||
|
}
|
||||||
|
@keyframes ripple-out { to { width: 400px; height: 400px; opacity: 0; } }
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
FEATURE GRID — 4×2 Tiles, .wide = span 2
|
||||||
|
=========================================== */
|
||||||
|
.feature-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
grid-template-rows: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
.feature-tile {
|
||||||
|
border: 1px solid var(--ink-line);
|
||||||
|
padding: 28px;
|
||||||
|
background: #0f0f16;
|
||||||
|
position: relative; overflow: hidden;
|
||||||
|
display: flex; flex-direction: column; justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
transition: transform 0.15s ease-out, border-color 0.12s ease-out, background 0.15s ease-out;
|
||||||
|
cursor: default;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
.feature-tile.wide { grid-column: span 2; }
|
||||||
|
.feature-tile:hover { background: #14141c; border-color: var(--cancom-red); transform: translateY(-4px); }
|
||||||
|
.feature-name {
|
||||||
|
font-family: var(--font-display); font-weight: 600;
|
||||||
|
font-size: 28px; color: var(--paper);
|
||||||
|
letter-spacing: -0.02em; line-height: 1.05;
|
||||||
|
}
|
||||||
|
.feature-desc { font-size: 16px; color: var(--paper-dim); line-height: 1.4; margin-top: 8px; }
|
||||||
|
.feature-tag {
|
||||||
|
font-family: var(--font-mono); font-size: 12px;
|
||||||
|
color: var(--cancom-red); letter-spacing: 0.2em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
CLOSING SLIDE — invertiert, hell
|
||||||
|
=========================================== */
|
||||||
|
.closing-slide {
|
||||||
|
background: var(--paper);
|
||||||
|
color: var(--ink);
|
||||||
|
padding: 120px;
|
||||||
|
display: flex; flex-direction: column; justify-content: space-between;
|
||||||
|
position: relative; overflow: hidden;
|
||||||
|
}
|
||||||
|
.closing-slide::before {
|
||||||
|
content: ""; position: absolute;
|
||||||
|
top: -20%; right: -10%;
|
||||||
|
width: 900px; height: 900px;
|
||||||
|
background: var(--cancom-red);
|
||||||
|
border-radius: 50%;
|
||||||
|
filter: blur(120px);
|
||||||
|
opacity: 0.3;
|
||||||
|
animation: blob-drift 12s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes blob-drift {
|
||||||
|
0%, 100% { transform: translate(0, 0) scale(1); }
|
||||||
|
50% { transform: translate(-100px, 80px) scale(1.15); }
|
||||||
|
}
|
||||||
|
.closing-top {
|
||||||
|
position: relative; z-index: 2;
|
||||||
|
font-family: var(--font-mono); font-size: 18px;
|
||||||
|
letter-spacing: 0.3em; text-transform: uppercase;
|
||||||
|
color: var(--cancom-red);
|
||||||
|
display: flex; align-items: center; gap: 16px;
|
||||||
|
}
|
||||||
|
.closing-top::before { content: ""; width: 60px; height: 2px; background: var(--cancom-red); }
|
||||||
|
.closing-headline {
|
||||||
|
position: relative; z-index: 2;
|
||||||
|
font-family: var(--font-display); font-weight: 700;
|
||||||
|
font-size: 240px; line-height: 0.86;
|
||||||
|
letter-spacing: -0.045em;
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
.closing-headline .accent { color: var(--cancom-red); }
|
||||||
|
.closing-meta {
|
||||||
|
position: relative; z-index: 2;
|
||||||
|
display: flex; justify-content: space-between; align-items: flex-end;
|
||||||
|
font-family: var(--font-mono); font-size: 16px;
|
||||||
|
letter-spacing: 0.16em; text-transform: uppercase;
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
.closing-meta .brand {
|
||||||
|
font-family: var(--font-display); font-weight: 700;
|
||||||
|
font-size: 42px; letter-spacing: -0.02em;
|
||||||
|
text-transform: none; color: var(--ink);
|
||||||
|
display: flex; align-items: center; gap: 14px;
|
||||||
|
}
|
||||||
|
.closing-meta .brand::before { content: ""; width: 14px; height: 14px; background: var(--cancom-red); }
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
SLIDE-SPEZIFISCHES CSS — hier anhängen
|
||||||
|
=========================================== */
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="deck-progress"><div class="bar" id="progressBar"></div></div>
|
||||||
|
<div class="edit-hotzone"></div>
|
||||||
|
<button class="edit-toggle" id="editToggle" title="Edit mode (E)">✏️</button>
|
||||||
|
|
||||||
|
<!-- ===========================================
|
||||||
|
16:9 BÜHNE — 1920×1080
|
||||||
|
=========================================== -->
|
||||||
|
<div class="deck-viewport">
|
||||||
|
<main class="deck-stage" id="deckStage">
|
||||||
|
<div class="deck-hud">
|
||||||
|
<span class="dot"></span>
|
||||||
|
<span id="hudCounter">01 / 04</span>
|
||||||
|
<span>·</span>
|
||||||
|
<span>[DECK-NAME]</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ============ SLIDE 1: TITEL ============ -->
|
||||||
|
<section class="slide title-slide active">
|
||||||
|
<canvas class="title-canvas" id="titleCanvas"></canvas>
|
||||||
|
<div class="title-inner title-topbar reveal d1">
|
||||||
|
<span class="brand-mark">CANCOM</span>
|
||||||
|
<span>Deck · MM · JJJJ</span>
|
||||||
|
</div>
|
||||||
|
<h1 class="title-headline reveal d2">
|
||||||
|
Erste Zeile.<br>
|
||||||
|
Zweite <span class="accent">Zeile.</span>
|
||||||
|
</h1>
|
||||||
|
<div class="title-meta reveal d4">
|
||||||
|
<div>
|
||||||
|
<div class="huge">CLAIM.</div>
|
||||||
|
<div style="margin-top: 16px; font-size: 26px; letter-spacing: 0.14em;">Untertitel in einer Zeile</div>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: right;">
|
||||||
|
<div>[Sprecher:in]</div>
|
||||||
|
<div style="margin-top: 6px; opacity: 0.6;">CANCOM · [Anlass]</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============ SLIDE 2: STANDARD-CONTENT ============ -->
|
||||||
|
<section class="slide">
|
||||||
|
<div class="slide-content" data-num="02 / 04">
|
||||||
|
<span class="slide-eyebrow reveal d1">Eyebrow</span>
|
||||||
|
<h2 class="slide-title reveal d2">Die Aussage<br>der <span class="accent">Slide.</span></h2>
|
||||||
|
<p class="slide-lead reveal d3">Ein bis zwei Sätze, die die Headline aufmachen und zum Content-Block überleiten.</p>
|
||||||
|
<div class="pain-grid">
|
||||||
|
<div class="pain-card reveal d4">
|
||||||
|
<span class="pain-num">01</span>
|
||||||
|
<div>
|
||||||
|
<div class="pain-title">Karten-<br>Titel</div>
|
||||||
|
<div class="pain-body">Zwei bis drei Sätze. Konkret, kein Marketing-Sprech.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pain-card reveal d5">
|
||||||
|
<span class="pain-num">02</span>
|
||||||
|
<div>
|
||||||
|
<div class="pain-title">Karten-<br>Titel</div>
|
||||||
|
<div class="pain-body">Zwei bis drei Sätze. Konkret, kein Marketing-Sprech.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pain-card reveal d6">
|
||||||
|
<span class="pain-num">03</span>
|
||||||
|
<div>
|
||||||
|
<div class="pain-title">Karten-<br>Titel</div>
|
||||||
|
<div class="pain-body">Zwei bis drei Sätze. Konkret, kein Marketing-Sprech.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============ SLIDE 3: ZAHLEN ============ -->
|
||||||
|
<section class="slide">
|
||||||
|
<div class="slide-content" data-num="03 / 04">
|
||||||
|
<span class="slide-eyebrow reveal d1">Die Zahlen</span>
|
||||||
|
<h2 class="slide-title reveal d2">Warum sich das<br><span class="accent">rechnet.</span></h2>
|
||||||
|
<div class="counter-grid">
|
||||||
|
<div class="counter-item reveal d3">
|
||||||
|
<span class="counter-value"><span data-count="100">0</span><span class="unit">%</span></span>
|
||||||
|
<div class="counter-label">Label der Kennzahl</div>
|
||||||
|
<div class="counter-caption">Ein Satz Kontext, damit die Zahl etwas bedeutet.</div>
|
||||||
|
</div>
|
||||||
|
<div class="counter-item reveal d4">
|
||||||
|
<span class="counter-value"><span data-count="0.2">0</span><span class="unit">MB</span></span>
|
||||||
|
<div class="counter-label">Label der Kennzahl</div>
|
||||||
|
<div class="counter-caption">Ein Satz Kontext, damit die Zahl etwas bedeutet.</div>
|
||||||
|
</div>
|
||||||
|
<div class="counter-item reveal d5">
|
||||||
|
<span class="counter-value"><span data-count="60">0</span><span class="unit">fps</span></span>
|
||||||
|
<div class="counter-label">Label der Kennzahl</div>
|
||||||
|
<div class="counter-caption">Ein Satz Kontext, damit die Zahl etwas bedeutet.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============ SLIDE 4: CLOSING ============ -->
|
||||||
|
<section class="slide">
|
||||||
|
<div class="closing-slide">
|
||||||
|
<div class="closing-top reveal d1">Take-away · [Deck-Name]</div>
|
||||||
|
<h2 class="closing-headline reveal d2">
|
||||||
|
Der eine Satz,<br>der <span class="accent">hängen</span><br>bleibt.
|
||||||
|
</h2>
|
||||||
|
<div class="closing-meta reveal d4">
|
||||||
|
<div class="brand">CANCOM</div>
|
||||||
|
<div style="text-align: right;">
|
||||||
|
<div>Fragen? → [mail@cancom.de]</div>
|
||||||
|
<div style="margin-top: 6px; opacity: 0.55;">Deck gebaut mit HTML · CSS · JS</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/* ===========================================
|
||||||
|
CANVAS BACKGROUND — Partikelfeld für die Titelslide
|
||||||
|
Entfernen, wenn die Titelslide kein <canvas id="titleCanvas"> hat.
|
||||||
|
=========================================== */
|
||||||
|
(function initTitleCanvas() {
|
||||||
|
const canvas = document.getElementById('titleCanvas');
|
||||||
|
if (!canvas) return;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
let w, h, dpr = window.devicePixelRatio || 1;
|
||||||
|
let particles = [];
|
||||||
|
const N = 80;
|
||||||
|
|
||||||
|
function resize() {
|
||||||
|
w = canvas.offsetWidth = 1920;
|
||||||
|
h = canvas.offsetHeight = 1080;
|
||||||
|
canvas.width = w * dpr;
|
||||||
|
canvas.height = h * dpr;
|
||||||
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||||
|
}
|
||||||
|
function init() {
|
||||||
|
particles = [];
|
||||||
|
for (let i = 0; i < N; i++) {
|
||||||
|
particles.push({
|
||||||
|
x: Math.random() * w,
|
||||||
|
y: Math.random() * h,
|
||||||
|
vx: (Math.random() - 0.5) * 0.3,
|
||||||
|
vy: (Math.random() - 0.5) * 0.3,
|
||||||
|
r: Math.random() * 1.6 + 0.4,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function tick() {
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
for (let i = 0; i < particles.length; i++) {
|
||||||
|
const a = particles[i];
|
||||||
|
a.x += a.vx; a.y += a.vy;
|
||||||
|
if (a.x < 0 || a.x > w) a.vx *= -1;
|
||||||
|
if (a.y < 0 || a.y > h) a.vy *= -1;
|
||||||
|
for (let j = i + 1; j < particles.length; j++) {
|
||||||
|
const b = particles[j];
|
||||||
|
const dx = a.x - b.x, dy = a.y - b.y;
|
||||||
|
const dist = Math.sqrt(dx*dx + dy*dy);
|
||||||
|
if (dist < 180) {
|
||||||
|
ctx.strokeStyle = `rgba(228, 0, 43, ${(1 - dist/180) * 0.35})`;
|
||||||
|
ctx.lineWidth = 0.6;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(a.x, a.y);
|
||||||
|
ctx.lineTo(b.x, b.y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.fillStyle = 'rgba(255,255,255,0.65)';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(a.x, a.y, a.r, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
requestAnimationFrame(tick);
|
||||||
|
}
|
||||||
|
resize(); init(); tick();
|
||||||
|
})();
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
CARDS — Mouse-Follow Highlight
|
||||||
|
=========================================== */
|
||||||
|
document.querySelectorAll('.pain-card').forEach(card => {
|
||||||
|
card.addEventListener('mousemove', (e) => {
|
||||||
|
const rect = card.getBoundingClientRect();
|
||||||
|
card.style.setProperty('--mx', ((e.clientX - rect.left) / rect.width * 100) + '%');
|
||||||
|
card.style.setProperty('--my', ((e.clientY - rect.top) / rect.height * 100) + '%');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
COUNTER ANIMATION — zählt beim ersten Anzeigen der Slide
|
||||||
|
=========================================== */
|
||||||
|
function animateCounter(el) {
|
||||||
|
const target = parseFloat(el.dataset.count);
|
||||||
|
const isFloat = target % 1 !== 0;
|
||||||
|
const duration = 1600;
|
||||||
|
const start = performance.now();
|
||||||
|
function step(now) {
|
||||||
|
const p = Math.min((now - start) / duration, 1);
|
||||||
|
const eased = 1 - Math.pow(1 - p, 3);
|
||||||
|
const val = target * eased;
|
||||||
|
el.textContent = isFloat ? val.toFixed(1) : Math.round(val);
|
||||||
|
if (p < 1) requestAnimationFrame(step);
|
||||||
|
else el.textContent = isFloat ? target.toFixed(1) : target;
|
||||||
|
}
|
||||||
|
requestAnimationFrame(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
SLIDE PRESENTATION CONTROLLER
|
||||||
|
=========================================== */
|
||||||
|
class SlidePresentation {
|
||||||
|
constructor() {
|
||||||
|
this.slides = document.querySelectorAll('.slide');
|
||||||
|
this.currentSlide = 0;
|
||||||
|
this.stage = document.getElementById('deckStage');
|
||||||
|
this.progress = document.getElementById('progressBar');
|
||||||
|
this.hud = document.getElementById('hudCounter');
|
||||||
|
this.setupStageScale();
|
||||||
|
this.setupKeyboardNav();
|
||||||
|
this.setupTouchNav();
|
||||||
|
this.setupWheelNav();
|
||||||
|
this.showSlide(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupStageScale() {
|
||||||
|
const scale = () => {
|
||||||
|
const factor = Math.min(window.innerWidth / 1920, window.innerHeight / 1080);
|
||||||
|
const x = (window.innerWidth - 1920 * factor) / 2;
|
||||||
|
const y = (window.innerHeight - 1080 * factor) / 2;
|
||||||
|
this.stage.style.transform = `translate(${x}px, ${y}px) scale(${factor})`;
|
||||||
|
};
|
||||||
|
scale();
|
||||||
|
window.addEventListener('resize', scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupKeyboardNav() {
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.target.getAttribute && e.target.getAttribute('contenteditable') === 'true') return;
|
||||||
|
if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown') {
|
||||||
|
e.preventDefault(); this.showSlide(this.currentSlide + 1);
|
||||||
|
} else if (e.key === 'ArrowLeft' || e.key === 'PageUp') {
|
||||||
|
e.preventDefault(); this.showSlide(this.currentSlide - 1);
|
||||||
|
} else if (e.key === 'Home') {
|
||||||
|
this.showSlide(0);
|
||||||
|
} else if (e.key === 'End') {
|
||||||
|
this.showSlide(this.slides.length - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupTouchNav() {
|
||||||
|
let startX = 0;
|
||||||
|
document.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; }, {passive: true});
|
||||||
|
document.addEventListener('touchend', (e) => {
|
||||||
|
const dx = e.changedTouches[0].clientX - startX;
|
||||||
|
if (Math.abs(dx) > 60) this.showSlide(this.currentSlide + (dx < 0 ? 1 : -1));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupWheelNav() {
|
||||||
|
let cooldown = false;
|
||||||
|
document.addEventListener('wheel', (e) => {
|
||||||
|
if (cooldown) return;
|
||||||
|
if (Math.abs(e.deltaY) < 30) return;
|
||||||
|
cooldown = true;
|
||||||
|
this.showSlide(this.currentSlide + (e.deltaY > 0 ? 1 : -1));
|
||||||
|
setTimeout(() => cooldown = false, 900);
|
||||||
|
}, {passive: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
showSlide(index) {
|
||||||
|
this.currentSlide = Math.max(0, Math.min(index, this.slides.length - 1));
|
||||||
|
this.slides.forEach((slide, i) => {
|
||||||
|
const active = i === this.currentSlide;
|
||||||
|
slide.classList.toggle('active', active);
|
||||||
|
slide.classList.toggle('visible', active);
|
||||||
|
if (active) {
|
||||||
|
slide.querySelectorAll('[data-count]').forEach(el => {
|
||||||
|
if (!el.dataset.animated) {
|
||||||
|
el.dataset.animated = 'true';
|
||||||
|
animateCounter(el);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const pct = (this.currentSlide + 1) / this.slides.length * 100;
|
||||||
|
this.progress.style.width = pct + '%';
|
||||||
|
this.hud.textContent =
|
||||||
|
String(this.currentSlide + 1).padStart(2, '0') + ' / ' +
|
||||||
|
String(this.slides.length).padStart(2, '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deck = new SlidePresentation();
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
INLINE EDITOR — Live-Korrektur auf der Bühne (Taste E)
|
||||||
|
=========================================== */
|
||||||
|
const editor = {
|
||||||
|
isActive: false,
|
||||||
|
toggleEditMode() {
|
||||||
|
this.isActive = !this.isActive;
|
||||||
|
const toggle = document.getElementById('editToggle');
|
||||||
|
toggle.classList.toggle('active', this.isActive);
|
||||||
|
document.querySelectorAll(
|
||||||
|
'.slide h1, .slide h2, .slide p, .slide li, .slide .pain-title, .slide .pain-body, ' +
|
||||||
|
'.slide .feature-name, .slide .feature-desc, .slide .counter-label, .slide .counter-caption, ' +
|
||||||
|
'.slide .slide-lead, .slide .versus-headline, .slide .closing-headline'
|
||||||
|
).forEach(el => {
|
||||||
|
el.setAttribute('contenteditable', this.isActive ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
if (!this.isActive) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem('cancom-deck-edits', document.querySelector('.deck-stage').innerHTML);
|
||||||
|
} catch(_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.getElementById('editToggle').addEventListener('click', () => editor.toggleEditMode());
|
||||||
|
|
||||||
|
// Hotzone mit 400 ms Grace-Period
|
||||||
|
const hotzone = document.querySelector('.edit-hotzone');
|
||||||
|
const editToggleBtn = document.getElementById('editToggle');
|
||||||
|
let hideTimeout = null;
|
||||||
|
hotzone.addEventListener('mouseenter', () => {
|
||||||
|
clearTimeout(hideTimeout);
|
||||||
|
editToggleBtn.classList.add('show');
|
||||||
|
});
|
||||||
|
hotzone.addEventListener('mouseleave', () => {
|
||||||
|
hideTimeout = setTimeout(() => {
|
||||||
|
if (!editor.isActive) editToggleBtn.classList.remove('show');
|
||||||
|
}, 400);
|
||||||
|
});
|
||||||
|
editToggleBtn.addEventListener('mouseenter', () => clearTimeout(hideTimeout));
|
||||||
|
editToggleBtn.addEventListener('mouseleave', () => {
|
||||||
|
hideTimeout = setTimeout(() => {
|
||||||
|
if (!editor.isActive) editToggleBtn.classList.remove('show');
|
||||||
|
}, 400);
|
||||||
|
});
|
||||||
|
hotzone.addEventListener('click', () => editor.toggleEditMode());
|
||||||
|
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if ((e.key === 'e' || e.key === 'E') &&
|
||||||
|
!(e.target.getAttribute && e.target.getAttribute('contenteditable') === 'true')) {
|
||||||
|
editor.toggleEditMode();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ===========================================
|
||||||
|
SLIDE-SPEZIFISCHES JS — hier anhängen
|
||||||
|
=========================================== */
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
61
font-vorschau.html
Normal file
61
font-vorschau.html
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Font-Vorschau · Headline-Alternativen</title>
|
||||||
|
<link rel="preconnect" href="https://api.fontshare.com">
|
||||||
|
<link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=clash-display@700,600&f[]=general-sans@700,600&f[]=switzer@700,600&f[]=cabinet-grotesk@700,800&display=swap">
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body { background: #0A0A0F; color: #F5F5F2; font-family: sans-serif; padding: 60px 80px; }
|
||||||
|
.row { border-bottom: 1px solid #23232E; padding: 48px 0; }
|
||||||
|
.row:last-child { border-bottom: none; }
|
||||||
|
.label {
|
||||||
|
font-family: 'JetBrains Mono', monospace; font-size: 14px; letter-spacing: 0.2em;
|
||||||
|
text-transform: uppercase; color: #E4002B; margin-bottom: 16px;
|
||||||
|
display: flex; align-items: center; gap: 14px;
|
||||||
|
}
|
||||||
|
.label .tag { color: rgba(245,245,242,0.35); text-transform: none; letter-spacing: 0; }
|
||||||
|
.sample {
|
||||||
|
font-weight: 700; font-size: 92px; line-height: 0.96; letter-spacing: -0.035em;
|
||||||
|
max-width: 1300px;
|
||||||
|
}
|
||||||
|
.sample .accent { color: #E4002B; }
|
||||||
|
.sample-small {
|
||||||
|
font-weight: 600; font-size: 40px; letter-spacing: -0.02em; margin-top: 20px;
|
||||||
|
color: rgba(245,245,242,0.6);
|
||||||
|
}
|
||||||
|
h1 { font-family: 'JetBrains Mono', monospace; font-size: 20px; letter-spacing: 0.14em;
|
||||||
|
text-transform: uppercase; color: rgba(245,245,242,0.5); margin-bottom: 50px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Headline-Schrift · Vorschau in Deck-Größe (92px / 40px)</h1>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">Aktuell <span class="tag">Clash Display · 700</span></div>
|
||||||
|
<div class="sample" style="font-family: 'Clash Display', sans-serif;">MCP ist USB-C<br>für <span class="accent">KI-Anwendungen.</span></div>
|
||||||
|
<div class="sample-small" style="font-family: 'Clash Display', sans-serif;">Karten-Titel · Zahlen · Buttons</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">Option 1 <span class="tag">General Sans · 700 (Empfohlen)</span></div>
|
||||||
|
<div class="sample" style="font-family: 'General Sans', sans-serif;">MCP ist USB-C<br>für <span class="accent">KI-Anwendungen.</span></div>
|
||||||
|
<div class="sample-small" style="font-family: 'General Sans', sans-serif;">Karten-Titel · Zahlen · Buttons</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">Option 2 <span class="tag">Switzer · 700</span></div>
|
||||||
|
<div class="sample" style="font-family: 'Switzer', sans-serif;">MCP ist USB-C<br>für <span class="accent">KI-Anwendungen.</span></div>
|
||||||
|
<div class="sample-small" style="font-family: 'Switzer', sans-serif;">Karten-Titel · Zahlen · Buttons</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">Option 3 <span class="tag">Cabinet Grotesk · 700</span></div>
|
||||||
|
<div class="sample" style="font-family: 'Cabinet Grotesk', sans-serif;">MCP ist USB-C<br>für <span class="accent">KI-Anwendungen.</span></div>
|
||||||
|
<div class="sample-small" style="font-family: 'Cabinet Grotesk', sans-serif;">Karten-Titel · Zahlen · Buttons</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1913
ki-verstehen.html
Normal file
1913
ki-verstehen.html
Normal file
File diff suppressed because it is too large
Load Diff
60
slides-manifest.js
Normal file
60
slides-manifest.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/* ===========================================
|
||||||
|
SLIDE-MANIFEST — einzige Quelle der Wahrheit für Titel/Reihenfolge.
|
||||||
|
Wird von ki-verstehen.html (Deck) UND config.html (Konfigurator) geladen.
|
||||||
|
`id` muss exakt der Dokumentreihenfolge der <section class="slide" data-slide-id="…">
|
||||||
|
in ki-verstehen.html entsprechen (1-basiert). Beim Umbau des Decks hier mitpflegen.
|
||||||
|
=========================================== */
|
||||||
|
const SLIDE_MANIFEST = [
|
||||||
|
{ id: 1, title: "Titel", kapitel: "—", type: "content", locked: true },
|
||||||
|
{ id: 2, title: "Hook: Wer hat heute schon KI benutzt?", kapitel: "—", type: "content" },
|
||||||
|
{ id: 3, title: "Kapitel 1 · Wie alles begann", kapitel: "Wie alles begann", type: "divider" },
|
||||||
|
{ id: 4, title: "Geschichte: Zeitstrahl 1950–2022", kapitel: "Wie alles begann", type: "content" },
|
||||||
|
{ id: 5, title: "Verschachtelte Kreise: KI/ML/DL/GenAI", kapitel: "Wie alles begann", type: "content" },
|
||||||
|
{ id: 6, title: "Wie lernt eine Maschine (das „A“)", kapitel: "Wie alles begann", type: "content" },
|
||||||
|
{ id: 7, title: "ANI vs. AGI", kapitel: "Wie alles begann", type: "content" },
|
||||||
|
{ id: 8, title: "Kapitel 2 · Wie ein LLM funktioniert", kapitel: "Wie ein LLM funktioniert", type: "divider" },
|
||||||
|
{ id: 9, title: "Mitmach-Spiel: Satz vervollständigen", kapitel: "Wie ein LLM funktioniert", type: "content" },
|
||||||
|
{ id: 10, title: "LLM: Autovervollständigung", kapitel: "Wie ein LLM funktioniert", type: "content" },
|
||||||
|
{ id: 11, title: "Token & Parameter", kapitel: "Wie ein LLM funktioniert", type: "content" },
|
||||||
|
{ id: 12, title: "Reasoning-Modelle", kapitel: "Wie ein LLM funktioniert", type: "content" },
|
||||||
|
{ id: 13, title: "System-Prompt, User-Prompt, Context", kapitel: "Wie ein LLM funktioniert", type: "content" },
|
||||||
|
{ id: 14, title: "Beispiel: System- & User-Prompt", kapitel: "Wie ein LLM funktioniert", type: "content" },
|
||||||
|
{ id: 15, title: "Kontext-Limit & Compact", kapitel: "Wie ein LLM funktioniert", type: "content" },
|
||||||
|
{ id: 16, title: "Kapitel 3 · Die Modell-Landschaft", kapitel: "Die Modell-Landschaft", type: "divider" },
|
||||||
|
{ id: 17, title: "Woher kommt das Wissen", kapitel: "Die Modell-Landschaft", type: "content" },
|
||||||
|
{ id: 18, title: "Modell-Landschaft (GPT, Claude, Gemini …)", kapitel: "Die Modell-Landschaft", type: "content" },
|
||||||
|
{ id: 19, title: "Wer baut sie, was unterscheidet sie", kapitel: "Die Modell-Landschaft", type: "content" },
|
||||||
|
{ id: 20, title: "Geschäftsmodell: Womit verdienen sie Geld", kapitel: "Die Modell-Landschaft", type: "content" },
|
||||||
|
{ id: 21, title: "SEO vs. GEO", kapitel: "Die Modell-Landschaft", type: "content" },
|
||||||
|
{ id: 22, title: "Wo finde ich diese Modelle", kapitel: "Die Modell-Landschaft", type: "content" },
|
||||||
|
{ id: 23, title: "Kapitel 4 · Vom Chatbot zum Agenten", kapitel: "Vom Chatbot zum Agenten", type: "divider" },
|
||||||
|
{ id: 24, title: "Eskalationsleiter: Chatbot → Agent", kapitel: "Vom Chatbot zum Agenten", type: "content" },
|
||||||
|
{ id: 25, title: "MCP: USB-C für KI", kapitel: "Vom Chatbot zum Agenten", type: "content" },
|
||||||
|
{ id: 26, title: "Kapitel 5 · Die Grenzen der KI", kapitel: "Die Grenzen der KI", type: "divider" },
|
||||||
|
{ id: 27, title: "Schwächen der KI (Übersicht)", kapitel: "Die Grenzen der KI", type: "content" },
|
||||||
|
{ id: 28, title: "Halluzination im Detail", kapitel: "Die Grenzen der KI", type: "content" },
|
||||||
|
{ id: 29, title: "Kapitel 6 · Sicherheit im Umgang mit KI", kapitel: "Sicherheit im Umgang mit KI", type: "divider" },
|
||||||
|
{ id: 30, title: "Prompt Injection", kapitel: "Sicherheit im Umgang mit KI", type: "content" },
|
||||||
|
{ id: 31, title: "Security: weitere Punkte", kapitel: "Sicherheit im Umgang mit KI", type: "content" },
|
||||||
|
{ id: 32, title: "Kapitel 7 · Praxis & Ausblick", kapitel: "Praxis & Ausblick", type: "divider" },
|
||||||
|
{ id: 33, title: "Praktische Tipps", kapitel: "Praxis & Ausblick", type: "content" },
|
||||||
|
{ id: 34, title: "Ausblick", kapitel: "Praxis & Ausblick", type: "content" },
|
||||||
|
{ id: 35, title: "Glossar", kapitel: "Praxis & Ausblick", type: "content" },
|
||||||
|
{ id: 36, title: "Closing", kapitel: "—", type: "content", locked: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
const SLIDE_CONFIG_STORAGE_KEY = 'cancomDeckSlideConfig';
|
||||||
|
|
||||||
|
function loadSlideConfig() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(SLIDE_CONFIG_STORAGE_KEY);
|
||||||
|
if (!raw) return null;
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
if (Array.isArray(parsed)) return new Set(parsed);
|
||||||
|
} catch (_) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveSlideConfig(enabledIdSet) {
|
||||||
|
localStorage.setItem(SLIDE_CONFIG_STORAGE_KEY, JSON.stringify(Array.from(enabledIdSet)));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user