Merge branch 'mai/cronus/issue-13-light-dark-en': Light/Dark + EN/DE Toggle (#13)

This commit is contained in:
mAi
2026-05-08 11:17:49 +02:00
66 changed files with 1331 additions and 236 deletions

243
docs/plans/theme-toggle.md Normal file
View File

@@ -0,0 +1,243 @@
# Theme + Sprache Toggle (Issue #13) — Design
**Status:** Design (Shift-1, Inventor) — Pilot auf 4 Sites. Awaiting m's go-ahead vor Rollout.
**Branch:** `mai/cronus/issue-13-light-dark-en`
**Issue:** [m/onepager#13](https://mgit.msbls.de/m/onepager/issues/13)
## Was bereits da ist (Stand 2026-05-07)
- `shared/i18n.js` — DE/EN Toggle, `[data-i18n-toggle]` Buttons, navigator.language Detection, localStorage Persistence (`onepager-lang`), MutationObserver auf `lang`. Alle 59 Sites annotiert.
- 33 Dark-Sites haben unique-Palette (Issue #12 Audit + Lift).
- Existing Toggle-Position: **Footer** — kleine Pill `EN`/`DE` plus statische Disclaimer-Zeile "Maschinell übersetzt".
- 4 Light-Templates existieren (`person-light.html`, `product-light.html`) — werden derzeit von keiner Site benutzt; Custom-Sites haben hardcoded Dark.
## Ziel
Kombiniertes Toggle-Widget oben rechts, **fixed**. Zwei Buttons in einer Pill:
```
┌──────────────┐
│ ☀ │ EN │ ← Dark-Mode aktiv: Sonne (Klick → light), "EN" (Klick → english)
└──────────────┘
```
- **Theme-Toggle**: Sonne/Mond Icon, persistiert in `localStorage("onepager-theme")`, Initial-Default = `prefers-color-scheme` Media-Query.
- **Lang-Toggle**: existing `[data-i18n-toggle]` Pattern (recht-Button im Widget bekommt das Attribut, i18n.js übernimmt Logik).
## Architektur-Entscheidungen
### 1. Eine Datei oder zwei?
**Entscheidung:** Zwei Dateien — `shared/theme.js` (theme logic) + `shared/toggles.js` (UI widget).
- **Trennung Logic ↔ UI** — theme.js ist die Quelle der Wahrheit für `data-theme` Attribut + localStorage. Tests, alternative UI, programmatischer Aufruf bleiben möglich.
- **toggles.js** ist ein optionales UI-Layer das beide bestehenden Skripte konsumiert (i18n.js's `[data-i18n-toggle]` + theme.js's `window.onepagerTheme`).
- Sites mit eigenem custom-Toggle-UI (zukünftig denkbar) können `toggles.js` weglassen, aber `theme.js` behalten.
### 2. CSS-Pattern: `[data-theme]` Attribut auf `<html>`
```css
/* Default = dark, no attr */
:root {
--bg: #0a0a0c;
--text: #e8e8ed;
--accent: #c9a84c;
}
/* Light overrides — gleiche Variablen, andere Werte */
[data-theme="light"] {
--bg: #faf9f6;
--text: #1a1a1a;
/* --accent bleibt erhalten (Site-Identität) */
}
```
**Warum `[data-theme]` auf `<html>`** (nicht `class="light"`):
- HTML-Attribut ist von der CSS-Cascade aus exakt gleich spezifisch wie `:root`. Per Source-Order kann man Defaults setzen und überschreiben — vorhersehbar.
- Kompatibel mit dem `prefers-color-scheme` Pattern: ein Dataset-Hook ohne Klassen-Kollision.
- Inline-Anti-FOUC-Script muss nur **ein Attribut** setzen, nicht eine Klasse + DOM-Manipulation.
### 3. Anti-FOUC: Inline-Pre-Script in `<head>`
**Kritisch.** Wenn der Theme erst nach Page-Load gesetzt wird, sieht der User für ~50200ms das Default-Theme bevor sein gespeichertes Theme angewandt wird → flackernder Modus-Switch.
```html
<head>
...
<script>
(function(){
try {
var t = localStorage.getItem('onepager-theme');
if (!t) t = matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', t);
} catch(e) {
document.documentElement.setAttribute('data-theme', 'dark');
}
})();
</script>
<style>...</style>
</head>
```
- Inline IIFE — synchron, **vor** `<style>` ausgeführt.
- Try/catch: Private-Browsing-Modi werfen bei `localStorage.getItem`. Fallback auf `prefers-color-scheme`. Fallback-Fallback auf `dark`.
- Etwa 14 Zeilen minified — Inline-Cost vernachlässigbar.
### 4. Light-Palette: Hybrid (Shared Default + Per-Site Override)
**Entscheidung:** `shared/css/theme.css` enthält neutrale Light-Defaults für gemeinsame Variablen-Namen (`--bg`, `--text`, `--text-dim`, `--text-muted`, `--border`, `--bg-card`, `--bg-elevated`).
```css
/* shared/css/theme.css */
[data-theme="light"] {
--bg: #faf9f6; /* warmes Off-White */
--bg-elevated: #ffffff;
--bg-card: #ffffff;
--text: #1a1a1a;
--text-dim: #5a5a5a;
--text-muted: #8a8a8a;
--border: rgba(0,0,0,0.08);
}
```
Pro Site, wenn der neutral-Default nicht trägt (Gold-, Olive-, Octopus-Sites): **eigenes** `[data-theme="light"]` Block im Site-Inline-`<style>` *nach* dem Light-Default-Link → überschreibt punktuell.
**Trade-off:** Vs. eine zentrale "alle-Sites-gleich-light" Lösung verliert man pro Site Identität sonst nicht. Diese Hybrid hält Identität (Akzent + ggf. tönende Hintergründe), gibt aber 90% der Sites einen passablen Light-Modus ohne manuelle Arbeit.
### 5. Per-Site Akzent-Behandlung in Light
| Site | Dark-Akzent | Light-Akzent (Pilot-Vorschlag) | Begründung |
|---|---|---|---|
| ichbinotto.de | Octopus `#e85040` (rot) | gleicher Octopus, evtl. `#d23a2a` minimal-darker | Identität bleibt; Kontrast auf Cream-BG ≥ 4.5:1 |
| paragraphenraiter.de | Gold `#c9a84c` | `#a0822a` (deeper Gold) | Gold auf weiß ist sub-WCAG; muss tiefer |
| kilitaer.de | Olive `#6b8e23` | gleiches Olive, ggf. `#557018` | Olive ist mid-luminance, hält auf cream |
| deinesei.de | Indigo `#6366f1` | gleicher Indigo | Indigo-Akzent funktioniert beidseitig |
**Regel:** Akzent bleibt visuell konstant wo möglich. Nur wenn WCAG AA (4.5:1) für Text-Akzente bzw. 3:1 für Large/UI verletzt → punktuell verdunkeln (lightness -10..-15%).
### 6. Toggle-Widget Position + Visuals
```
position: fixed;
top: 12px;
right: 12px;
z-index: 9999;
display: flex;
gap: 0;
border-radius: 999px;
backdrop-filter: blur(8px);
background: var(--bg-card, rgba(255,255,255,0.6));
border: 1px solid var(--border, rgba(0,0,0,0.08));
padding: 4px;
```
- **Backdrop-blur** — nötig damit das Pill über Hero-Bildern lesbar bleibt (kilitaer hat Camo-Pattern, paragraphenraiter Gold-Glow).
- **CSS-Variablen** als Background/Border — passt sich automatisch dem aktiven Theme an.
- **Mobile**: 12px statt 16px (Daumen-Reach), Buttons 36×36px (Touch-Mindestmaß).
- **Desktop ≥ 768px**: 16px Abstand.
- **Icons**: Inline-SVG für Sun/Moon (kein Emoji-Rendering-Drift across OS), Text "DE"/"EN" für Lang.
### 7. Per-Site Opt-Out
Manche Satire-Sites (kilibri, killusion, killionaer, killuminati) leben von der Dark-Stimmung. Light-Mode kippt deren Aesthetik komplett.
**Mechanismus:**
```html
<html lang="de" data-theme-lock="dark">
```
`theme.js` prüft das Attribut: wenn `data-theme-lock` gesetzt ist, wird dessen Wert erzwungen, localStorage ignoriert, und `toggles.js` blendet den Theme-Button aus (Lang-Button bleibt). Pro Site auswählbar in Shift-2 nach m's Review der Pilot-Screenshots.
### 8. Audit-Erweiterung: Light-Mode-Kontrast
`tools/contrast-audit.py` aktuell prüft nur `:root` (Dark). Erweitert zu:
- Parse beide Block-Typen: `:root { ... }` und `[data-theme="light"] { ... }`
- Für Light-Mode-Audit: light-bg gegen alle text-Variablen prüfen
- Output beide Tabellen, Light-Mode-Findings separat
Wenn ein Site keinen `[data-theme="light"]` Block hat, fällt es auf den Shared-Default zurück. Audit kann das simulieren indem es `shared/css/theme.css` als Override mitliest, falls die Site das Theme-Linkt einbindet.
## Out of Scope (Shift-2)
- Rollout auf alle 59 Sites (Shift-2 / Coder)
- Per-Site Light-Palette-Verfeinerung wo neutral-Default nicht trägt
- Per-Site Opt-Out-Liste basierend auf m's Pilot-Review
- Removal der existierenden Footer-Toggle-Buttons (während Pilot bleibt sie redundant aktiv — i18n.js fängt beide ab)
## Out of Scope (komplett)
- Sepia/High-Contrast/Tritan Modi
- Auto-Switch nach Tageszeit
- Custom-Site-Inhalte (SVG/Bilder), die Dark-BG voraussetzen — pro-Site-Issue im Bedarfsfall
## Reuse / Bestehende Patterns
- `shared/i18n.js` — Lang-Toggle bleibt komplett unverändert. Neuer Top-Right-Button bekommt nur `data-i18n-toggle` und i18n.js verbindet ihn automatisch.
- `shared/ai-disclosure.js` — Vorbild für auto-injection: `MutationObserver` auf `documentElement` Attributes, IIFE im File-Tail. Theme.js folgt dem Muster.
- `tools/contrast-audit.py` — bestehende Logik (HEX-Parser, WCAG-Ratio, Variable-Klassifikation) bleibt; nur ein zweiter Audit-Pass für `[data-theme="light"]` wird ergänzt.
## Pilot-Sites
| Site | Spektrum-Begründung |
|---|---|
| **ichbinotto.de** | m's Beobachtungs-Origin, Octopus-Akzent, persönliche Brand-Site |
| **paragraphenraiter.de** | Gold-Theme — anspruchsvolle Light-Variante (Gold auf weiß = WCAG-Drama) |
| **kilitaer.de** | Olive — Wortspiel-Satire-Site, mittlere Luminanz, Camo-BG-Pattern |
| **deinesei.de** | Indigo — Standard-Web-Akzent, "deinesei-shared-pattern" das auf 14 weitere Sites rippelt wenn's hier hält |
Pilot-Validation:
1. Manueller Klick-Test in Chrome + Firefox: Toggle zwischen Modi, Reload, andere Tab → State persistent.
2. `prefers-color-scheme: light` als initial-Default greift bei erstem Besuch ohne localStorage.
3. Kein FOUC bei Hard-Reload (Network throttling auf Slow 3G).
4. `tools/contrast-audit.py --light` läuft sauber durch.
## Implementation-Reihenfolge
1. `shared/css/theme.css` — neutrale Light-Defaults (~30 LoC).
2. `shared/theme.js` — Logik-Modul, exposes `window.onepagerTheme`, MutationObserver, FOUC-Companion (~50 LoC).
3. `shared/toggles.js` — UI-Widget Auto-Injection (~80 LoC).
4. Inline Anti-FOUC-Script: in `templates/base.html` einbauen, plus manuell in 4 Pilot-Sites.
5. Pilot-Site CSS: `[data-theme="light"]` Block je Site, ~10 Zeilen.
6. Pilot-Site Removal: existing Footer-Toggle-Button kann bleiben (i18n.js fängt beide), oder wird in Shift-2 systematisch entfernt.
7. `tools/contrast-audit.py``--light` Flag.
8. README/Adding-a-New-Site Doku update (kommt mit Shift-2).
## Ausgewählte Trade-offs (kompakt)
| Frage | Gewählt | Alternative | Warum |
|---|---|---|---|
| Toggle-Position | Fixed top-right | In-Footer / In-Header | m's Vorgabe + State-Visibility unabhängig vom Scroll |
| Storage-Key | `onepager-theme` | gleicher key wie i18n? | Trennung — Lang und Theme sind unabhängig |
| FOUC-Prevention | Inline IIFE in `<head>` | externes Script + render-blocking | Synchron geht nur inline |
| Light-Default-Verteilung | Shared CSS-Link + Site-Override | Nur per-Site / nur shared | 90% kostenlos, 10% manuell |
| `class="dark"` vs. `data-theme` | `data-theme` Attribut | Klasse | Spez-Verhalten klarer; SSR-frei |
| Lang+Theme im selben File | Zwei Dateien (theme.js + toggles.js) | Eine Mega-Datei | Logik vs. UI trennen, Test+Reuse |
| Akzent-Invert in Light | Akzent erhalten | Mathematisch invertieren | Identität geht vor Algorithmus |
## Bekannte Risiken / Anti-Empfehlungen
- **Body-Background-Image-Sites** (kilitaer Camo, deinesei Noise): die Hintergrund-`background-image:` Url(s) sind oft so dunkel dass auch `[data-theme="light"]` cream-bg darunter durchschimmert nicht. Lösung: `body::before` Overlays unter `[data-theme="light"]` opacity reduzieren.
- **CSS-only Animations mit hardcoded Hex** (statt CSS-Variablen): manche Sites animieren via `from { background: #060610 }`. Diese müssen pro Site gefixt werden; nicht aus shared lösbar.
- **AI-Disclosure-Footer** uses `var(--text-muted)`. Wird im Light-Modus automatisch dunkler. ✅ Kein Bruch.
- **Impressum-Overlay** uses `inherit` für Farben. ✅ Kein Bruch.
## Done-Definition Shift-1
- [x] Design-Doc commit
- [x] `shared/theme.js`, `shared/toggles.js`, `shared/css/theme.css` implementiert
- [x] `templates/base.html` mit Anti-FOUC + Theme-Link
- [x] 4 Pilot-Sites: ichbinotto, paragraphenraiter, kilitaer, deinesei — Anti-FOUC inline + light overrides + script-Includes
- [x] `tools/contrast-audit.py` mit `--light` Mode
- [x] Branch gepusht
- [x] Issue-Comment mit DESIGN READY FOR REVIEW
## Inventor-Self-Assessment (Shift-2 Eignung)
Ich (cronus) wäre **gut geeignet** für Shift-2-Rollout, weil:
- Pattern + Edge-Cases bereits durch alle 4 Pilot-Site-Stile durchgegangen
- Shared-Files getestet und stabilisiert
- Audit-Tool kann fail-fast vor Rollout
Aber Shift-2 ist *Bulk-Mechanik* (54 Sites × `<head>` editieren + opt-out-Liste pflegen). Das passt eher zu einem Coder-Worker mit Bulk-Scripting-Fokus. Final-Decision liegt bei head + m.

29
shared/css/theme.css Normal file
View File

@@ -0,0 +1,29 @@
/*
* Onepager neutral light-mode defaults.
*
* Linked into every site via <link rel="stylesheet" href="/shared/css/theme.css">
* BEFORE the per-site <style> block, so site-specific [data-theme="light"]
* overrides win on cascade.
*
* Variables targeted are the common set used across templates and custom sites.
* Sites with custom palettes (gold, olive, octopus, etc.) supply their own
* [data-theme="light"] block to override these defaults.
*/
[data-theme="light"] {
--bg: #faf9f6;
--bg-elevated: #ffffff;
--bg-card: #ffffff;
--bg-surface: #ffffff;
--text: #1a1a1a;
--text-dim: #4a4a4a;
--text-dimmed: #4a4a4a;
--text-muted: #6b6b6b; /* AA on white: 4.85:1 */
--text-secondary: #4a4a4a;
--border: rgba(0, 0, 0, 0.08);
color-scheme: light;
}
[data-theme="dark"] {
color-scheme: dark;
}

63
shared/theme.js Normal file
View File

@@ -0,0 +1,63 @@
/**
* Theme switcher for onepager sites.
*
* Sets data-theme="light"|"dark" on <html>. Persists to localStorage
* ("onepager-theme"). Initial-default falls back to prefers-color-scheme.
*
* Anti-FOUC companion: this file expects an inline IIFE in <head> to set
* data-theme BEFORE CSS loads, so the first paint is correct. This file
* only handles runtime toggling and exposes window.onepagerTheme.
*
* Per-site opt-out: <html data-theme-lock="dark"> forces dark and ignores
* localStorage. toggles.js consumes data-theme-lock to hide the theme button.
*/
(function () {
var KEY = 'onepager-theme';
var SUPPORTED = ['light', 'dark'];
function lock() {
var v = document.documentElement.getAttribute('data-theme-lock');
return v && SUPPORTED.indexOf(v) !== -1 ? v : null;
}
function detect() {
var locked = lock();
if (locked) return locked;
var stored = null;
try { stored = localStorage.getItem(KEY); } catch (e) { /* private browsing */ }
if (stored && SUPPORTED.indexOf(stored) !== -1) return stored;
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
return 'light';
}
return 'dark';
}
function get() {
return document.documentElement.getAttribute('data-theme') || 'dark';
}
function set(theme) {
if (lock()) return;
if (SUPPORTED.indexOf(theme) === -1) return;
document.documentElement.setAttribute('data-theme', theme);
try { localStorage.setItem(KEY, theme); } catch (e) { /* private browsing */ }
}
function toggle() {
set(get() === 'light' ? 'dark' : 'light');
}
// Reconcile with anti-FOUC inline script. If <html> already has data-theme
// set (by anti-FOUC IIFE), keep it. Otherwise apply detect().
if (!document.documentElement.getAttribute('data-theme')) {
document.documentElement.setAttribute('data-theme', detect());
}
window.onepagerTheme = {
get: get,
set: set,
toggle: toggle,
detect: detect,
isLocked: function () { return lock() !== null; }
};
})();

132
shared/toggles.js Normal file
View File

@@ -0,0 +1,132 @@
/**
* Combined Theme + Language toggle widget for onepager sites.
*
* Auto-injects a fixed top-right pill with two buttons:
* [☀/🌙] [DE/EN]
*
* Depends on theme.js (window.onepagerTheme) and i18n.js ([data-i18n-toggle]).
* Include all three in this order at end of <body>:
* <script src="/shared/theme.js"></script>
* <script src="/shared/i18n.js"></script>
* <script src="/shared/toggles.js"></script>
*
* Per-site opt-out:
* <html data-no-toggles> — skip both buttons
* <html data-theme-lock="dark"> — hide theme button, keep lang
* <script data-no-lang> — on toggles.js tag, hide lang button
*/
(function () {
if (document.documentElement.hasAttribute('data-no-toggles')) return;
var script = document.currentScript;
var hideLang = script && script.hasAttribute('data-no-lang');
var hideTheme = !window.onepagerTheme || window.onepagerTheme.isLocked();
if (hideTheme && hideLang) return;
var SUN = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>';
var MOON = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';
function inject() {
if (document.getElementById('onepager-toggles')) return;
var pill = document.createElement('div');
pill.id = 'onepager-toggles';
pill.style.cssText = [
'position:fixed',
'top:12px',
'right:12px',
'z-index:9999',
'display:flex',
'gap:0',
'align-items:stretch',
'border-radius:999px',
'padding:3px',
'background:var(--bg-card,rgba(255,255,255,0.6))',
'border:1px solid var(--border,rgba(127,127,127,0.25))',
'backdrop-filter:blur(8px)',
'-webkit-backdrop-filter:blur(8px)',
'box-shadow:0 2px 8px rgba(0,0,0,0.08)',
'font-family:inherit'
].join(';');
var btnStyle = [
'background:transparent',
'border:0',
'color:var(--text,inherit)',
'cursor:pointer',
'padding:6px 10px',
'font-size:0.7rem',
'font-weight:500',
'letter-spacing:0.05em',
'border-radius:999px',
'display:inline-flex',
'align-items:center',
'justify-content:center',
'min-width:34px',
'min-height:28px',
'line-height:1',
'transition:background 0.15s,color 0.15s',
'opacity:0.85'
].join(';');
if (!hideTheme) {
var themeBtn = document.createElement('button');
themeBtn.type = 'button';
themeBtn.id = 'onepager-theme-toggle';
themeBtn.style.cssText = btnStyle;
themeBtn.setAttribute('aria-label', 'Toggle light/dark theme');
themeBtn.title = 'Hell/Dunkel umschalten';
function renderTheme() {
var isLight = window.onepagerTheme && window.onepagerTheme.get() === 'light';
themeBtn.innerHTML = isLight ? MOON : SUN;
}
renderTheme();
themeBtn.addEventListener('click', function () {
if (window.onepagerTheme) window.onepagerTheme.toggle();
renderTheme();
});
themeBtn.addEventListener('mouseenter', function () { themeBtn.style.opacity = '1'; });
themeBtn.addEventListener('mouseleave', function () { themeBtn.style.opacity = '0.85'; });
pill.appendChild(themeBtn);
}
if (!hideLang) {
if (!hideTheme) {
var sep = document.createElement('span');
sep.style.cssText = 'width:1px;background:var(--border,rgba(127,127,127,0.25));margin:4px 0;';
sep.setAttribute('aria-hidden', 'true');
pill.appendChild(sep);
}
var langBtn = document.createElement('button');
langBtn.type = 'button';
langBtn.id = 'onepager-lang-toggle';
langBtn.setAttribute('data-i18n-toggle', '');
langBtn.style.cssText = btnStyle;
langBtn.title = 'Maschinell übersetzt / Machine-translated — German is the original.';
// i18n.js fills text & aria-label and binds click; placeholder until then:
langBtn.textContent = (document.documentElement.lang || 'de') === 'de' ? 'EN' : 'DE';
langBtn.addEventListener('mouseenter', function () { langBtn.style.opacity = '1'; });
langBtn.addEventListener('mouseleave', function () { langBtn.style.opacity = '0.85'; });
pill.appendChild(langBtn);
}
document.body.appendChild(pill);
// If i18n.js already initialised (loaded before us), it won't have bound
// the new button — re-bind manually.
if (!hideLang && window.onepagerI18n) {
var lb = document.getElementById('onepager-lang-toggle');
if (lb) {
lb.addEventListener('click', window.onepagerI18n.toggle);
lb.textContent = (document.documentElement.lang || 'de') === 'de' ? 'EN' : 'DE';
}
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', inject);
} else {
inject();
}
})();

View File

@@ -3,11 +3,13 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<meta name="theme-color" content="#0f1210">
<title>6034.de</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
:root {
--bg: #0f1210;
@@ -20,6 +22,10 @@
--font: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
}
[data-theme="light"] {
--green-dim: #15803d;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
background: var(--bg);

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>all·AI·n·all·AI·n</title>
<meta name="description" content="Umgeben von KIs. Trotzdem allein.">
<meta name="robots" content="noindex, nofollow">
@@ -10,6 +11,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -27,6 +29,10 @@
--ai-glow: rgba(123, 138, 173, 0.12);
}
[data-theme="light"] {
--text-faint: #5a608a;
}
html { scroll-behavior: smooth; }
body {
@@ -376,10 +382,6 @@
<footer>
<div class="wrap">
<p>allainallain.de &mdash; 2026</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
@@ -434,6 +436,8 @@
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>All AIs On Me — AI Optimization</title>
<meta name="description" content="SEO is dead. All AIs On Me makes your brand visible to ChatGPT, Gemini, Claude and beyond. AI Optimization, not Search Engine Optimization.">
<meta property="og:title" content="All AIs On Me — AI Optimization">
@@ -10,6 +11,7 @@
<meta property="og:type" content="website">
<meta property="og:url" content="https://allaisonme.com">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>👁</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600&display=swap');
@@ -700,10 +702,6 @@
<div class="footer-brand">All<span class="ai-highlight">AIs</span>OnMe</div>
<p style="font-style:italic;color:var(--text-muted);font-size:0.8rem;margin-bottom:8px;" data-de="&quot;Only God can judge AI.&quot;" data-en="&quot;Only God can judge AI.&quot;">&ldquo;Only God can judge AI.&rdquo;</p>
<p data-de="&copy; 2026 AllAIsOnMe &amp;middot; &lt;a href=&quot;mailto:hello@allaisonme.com&quot;&gt;hello@allaisonme.com&lt;/a&gt;" data-en="&copy; 2026 AllAIsOnMe &amp;middot; &lt;a href=&quot;mailto:hello@allaisonme.com&quot;&gt;hello@allaisonme.com&lt;/a&gt;">&copy; 2026 AllAIsOnMe &middot; <a href="mailto:hello@allaisonme.com">hello@allaisonme.com</a></p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">DE</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Machine-translated</small>
</div>
</div>
</footer>
@@ -721,6 +719,8 @@
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Billable Aua.</title>
<meta name="description" content="Billable Hours. Das Vergütungsmodell, das Effizienz bestraft und Leidensfähigkeit belohnt.">
<meta name="robots" content="noindex, nofollow">
@@ -10,6 +11,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@300;400;600;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -405,10 +407,6 @@
<footer>
<div class="wrap">
<p>billableaua.de &mdash; 2026</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
@@ -479,6 +477,8 @@
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,11 +3,13 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Clemens Plassmann — Hogan Lovells</title>
<meta name="description" content="Clemens Plassmann — Patent Litigation, Hogan Lovells.">
<meta http-equiv="refresh" content="0; url=https://www.hoganlovells.com/en/clemens-plassmann">
<link rel="canonical" href="https://www.hoganlovells.com/en/clemens-plassmann">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚖️</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
@@ -33,6 +35,8 @@
<p>Weiter zu <a href="https://www.hoganlovells.com/en/clemens-plassmann">hoganlovells.com</a>.</p>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,11 +3,13 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>CommanderKIn — Commander Keen + KI</title>
<meta name="description" content="Old-School Vibes, New-School AI. 90er Retro-Gaming meets künstliche Intelligenz.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
@@ -22,6 +24,10 @@
--blue-glow:0 0 10px rgba(0,85,170,.4),0 0 40px rgba(0,85,170,.15);
}
[data-theme="light"] {
--green-dim: #166e16;
}
html{scroll-behavior:smooth}
body{
@@ -481,10 +487,6 @@
<div class="container">
<p class="insert-coin" data-de="INSERT COIN TO CONTINUE" data-en="INSERT COIN TO CONTINUE">INSERT COIN TO CONTINUE</p>
<p class="footer-sub" data-de="&copy; 2026 CommanderKIn &mdash; Powered by Pixel &amp; KI" data-en="&copy; 2026 CommanderKIn &mdash; Powered by Pixel &amp; AI">&copy; 2026 CommanderKIn &mdash; Powered by Pixel &amp; KI</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid #333;color:#333;font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:#333;font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
@@ -515,6 +517,8 @@ document.addEventListener('keydown', (e) => {
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,10 +3,12 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>DaNoSi — Hausgemachte Feinkost</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600&family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400;1,500&family=Lato:wght@300;400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -865,13 +867,11 @@
<!-- Footer -->
<footer>
<p data-de="© 2026 DaNoSi Feinkost — Norbert Siebels. Alle Rechte vorbehalten." data-en="© 2026 DaNoSi Feinkost — Norbert Siebels. All rights reserved.">&copy; 2026 DaNoSi Feinkost — Norbert Siebels. Alle Rechte vorbehalten.</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid rgba(250,246,238,0.3);color:rgba(250,246,238,0.3);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:rgba(250,246,238,0.3);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>deineSei.de — Wir bauen dir deine Seite</title>
<meta name="description" content="deineSei.de — Deine Webseite, gebaut in Minuten. Domain, Design, Hosting — alles aus einer Hand.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌐</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600;700&family=Inter:wght@300;400;500&display=swap');
@@ -25,6 +27,20 @@
--green-glow: rgba(34, 197, 94, 0.12);
}
[data-theme="light"] {
--bg: #fafafd;
--bg-card: #ffffff;
--border: rgba(20, 20, 40, 0.10);
--text: #15151f;
--text-dim: #4a4a5e;
--text-muted: #6e6e85;
--accent: #4f46e5; /* indigo-600, AA on white */
--accent-glow: rgba(79, 70, 229, 0.15);
--accent-subtle: rgba(79, 70, 229, 0.06);
--green: #15803d; /* AA on white */
--green-glow: rgba(21, 128, 61, 0.10);
}
html { scroll-behavior: smooth; }
body {
@@ -358,14 +374,12 @@
<footer>
<div class="container">
<p><span style="color:var(--text-dim)">deine</span><span style="color:var(--accent)">Sei</span><span data-de=".de — ein Projekt von msbls.de" data-en=".de — a project by msbls.de">.de — ein Projekt von msbls.de</span></p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,11 +3,13 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Der Kaiser ist nackt. — Eine ehrliche Bestandsaufnahme der KI-Revolution</title>
<meta name="description" content="Eine Initiative für ehrliche KI-Kommunikation. Kritik ist kein Pessimismus. Es ist Qualitätskontrolle.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -468,13 +470,11 @@
<footer class="footer">
<div class="footer-mark"></div>
<p data-de="Eine Initiative für ehrliche KI-Kommunikation" data-en="An initiative for honest AI communication">Eine Initiative f&uuml;r ehrliche KI-Kommunikation</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--grey);color:var(--grey);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--grey);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,10 +3,12 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>ElefantenHor.de — Die Horde kommt.</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Passion+One:wght@400;700;900&family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -806,10 +808,6 @@
<p class="footer-text shake-target" data-de="Hier war mal eine Webseite. Dann kam die Horde." data-en="There used to be a website here. Then came the horde.">Hier war mal eine Webseite. Dann kam die Horde.</p>
<div class="footer-elephants wobble-target">🐘 🐘 🐘 🐘 🐘</div>
<p class="footer-copy" data-de="© 2026 ElefantenHor.de — Alle Rechte zertrampelt." data-en="© 2026 ElefantenHor.de — All rights trampled.">&copy; 2026 ElefantenHor.de — Alle Rechte zertrampelt.</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--dust-light,#E8D5A3);color:var(--dust-light,#E8D5A3);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;opacity:0.7;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--dust-light,#E8D5A3);font-size:0.6rem;opacity:0.4;">Maschinell übersetzt</small>
</div>
</footer>
</div>
@@ -900,6 +898,8 @@
}, 800);
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Ina Kamps — Knowledge Lawyer | IP, Marke, Green Claims</title>
<meta name="description" content="Ina Kamps — Knowledge Lawyer. Markenrecht, Green Claims, Nachhaltigkeit und IP an der Schnittstelle von Recht und Innovation.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>I</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@@ -566,14 +568,12 @@
<footer>
<div class="container">
<p>fr<span style="color: var(--accent);">AGI</span>na.de — Ina Kamps</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,11 +3,13 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>frenchkis.de — Franz&ouml;sische K&uuml;nstliche Intelligenzen</title>
<meta name="description" content="French KIs: Alles &uuml;ber franz&ouml;sische K&uuml;nstliche Intelligenzen. Sie streiken, philosophieren und gehen mittags zwei Stunden essen.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Space+Mono:wght@400;700&family=Playfair+Display:ital,wght@0,700;1,400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -890,13 +892,11 @@
Daf&uuml;r h&auml;tten wir bis Dienstag warten m&uuml;ssen. Und der Antrag liegt noch beim Minist&egrave;re.
</p>
<div class="fine-print" data-de="Satire · Kein Affiliat von Mistral, außer sie wollen uns Geld geben · Vive la France" data-en="Satire · Not affiliated with Mistral, unless they want to give us money · Vive la France">Satire &bull; Kein Affiliat von Mistral, außer sie wollen uns Geld geben &bull; Vive la France</div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>FrollAIn — Ihr KI-Sekretariat</title>
<meta name="description" content="FrollAIn. Immer lächelnd. Nie krank. Beschwert sich nicht.">
<meta property="og:title" content="FrollAIn — Ihr KI-Sekretariat">
@@ -13,6 +14,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Special+Elite&family=Inter:wght@300;400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -294,10 +296,6 @@
<footer>
<div class="wrap">
<p data-de="FrollAIn. Weil &laquo;Fräulein&raquo; seit 1972 keine offizielle Anrede mehr ist.<br>Aus gutem Grund." data-en="FrollAIn. Because &laquo;Fräulein&raquo; has not been an official form of address since 1972.<br>For good reason.">FrollAIn. Weil &laquo;Fräulein&raquo; seit 1972 keine offizielle Anrede mehr ist.<br>Aus gutem Grund.</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
@@ -314,6 +312,8 @@
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,12 +3,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Hallo Frau Maier — KI-gestützte Hausverwaltung</title>
<meta name="description" content="Ihre Hausverwaltung ist jetzt immer erreichbar. Auch nachts. Auch bei Wasserschäden. Frau Maier — die KI-Hausverwaltung, die nie schläft.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏠</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=DM+Serif+Display&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0; padding: 0; box-sizing: border-box;
@@ -32,6 +34,10 @@
--card-border: rgba(196, 168, 130, 0.25);
}
[data-theme="light"] {
--text-light: #6e6452;
}
html { scroll-behavior: smooth; }
body {
@@ -930,10 +936,6 @@
KI-gestützte Hausverwaltung
</div>
</div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-light,#aaa);color:var(--text-light,#aaa);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-light,#aaa);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script>
@@ -956,6 +958,8 @@
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,12 +3,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Hey Goldi! — Dein freundlicher KI-Begleiter</title>
<meta name="description" content="Hey Goldi! — KI-Companion für Menschen, die es einfach mögen. Warm, freundlich, immer da.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🧸</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0; padding: 0; box-sizing: border-box;
@@ -34,6 +36,10 @@
--sky: #A8C8D8;
}
[data-theme="light"] {
--text-gentle: #6e6452;
}
html { scroll-behavior: smooth; }
body {
@@ -577,14 +583,12 @@
<div class="footer-bear">🧸</div>
<p class="footer-text" data-de="Hey Goldi! — Dein freundlicher KI-Begleiter." data-en="Hey Goldi! — Your friendly AI companion.">Hey Goldi! — Dein freundlicher KI-Begleiter.</p>
<p class="footer-copy">&copy; 2026 heygoldi.de</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid rgba(251,240,212,0.4);color:rgba(251,240,212,0.6);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:rgba(251,240,212,0.4);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script src="/shared/impressum.js" data-owner="flexsiebels" data-variant="full"></script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Ich bin auf Bali.</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌴</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@@ -103,11 +105,9 @@
<p class="subtitle" data-de="Und du so?" data-en="And you?">Und du so?</p>
<p class="footer">ichbinaufb<span style="color:var(--accent)">AI</span>.de</p>
</div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Ich bin auf Barley.</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌾</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@@ -131,12 +133,10 @@
</p>
<a href="https://barley.de" class="cta" data-de="Barley entdecken" data-en="Discover Barley">Barley entdecken</a>
<p class="footer">ichbinaufbarley.de</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title data-de="Otto — dein sideKIck" data-en="Otto — your AI sideKIck">Otto — dein sideKIck</title>
<meta name="description" content="Otto ist Matthias' persönlicher sideKIck. E-Mail, Kalender, Infrastruktur, WhatsApp — alles im Griff." data-de="Otto ist Matthias' persönlicher sideKIck. E-Mail, Kalender, Infrastruktur, WhatsApp — alles im Griff." data-en="Otto is Matthias' personal AI sideKIck. Email, calendar, infrastructure, WhatsApp — all under control.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🐙</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500&display=swap');
@@ -25,6 +27,20 @@
--teal-glow: rgba(64, 200, 184, 0.15);
}
[data-theme="light"] {
--bg: #faf9f6;
--bg-card: #ffffff;
--border: rgba(20, 20, 40, 0.10);
--text: #1a1a2e;
--text-dim: #4a4a6e;
--text-muted: #6b6b8a;
--octopus: #c63a2c; /* AA on cream: 4.7:1 */
--octopus-glow: rgba(198, 58, 44, 0.12);
--octopus-subtle: rgba(198, 58, 44, 0.05);
--teal: #1f8a7c; /* AA on cream */
--teal-glow: rgba(31, 138, 124, 0.10);
}
html { scroll-behavior: smooth; }
body {
@@ -435,13 +451,13 @@
<div class="container">
<p data-de='<span class="otto">Otto</span> — mai-otto.de — ein Projekt von <a href="https://msbls.de" style="color:var(--text-muted);text-decoration:none;">msbls.de</a>'
data-en='<span class="otto">Otto</span> — mai-otto.de — a project by <a href="https://msbls.de" style="color:var(--text-muted);text-decoration:none;">msbls.de</a>'><span class="otto">Otto</span> — mai-otto.de — ein Projekt von <a href="https://msbls.de" style="color:var(--text-muted);text-decoration:none;">msbls.de</a></p>
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--border);color:var(--text-muted);font-family:'Space Grotesk',sans-serif;font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;margin-top:12px;transition:color 0.2s,border-color 0.2s;" onmouseover="this.style.color='var(--text)';this.style.borderColor='var(--text-muted)'" onmouseout="this.style.color='var(--text-muted)';this.style.borderColor='var(--border)'">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted);font-family:'Space Grotesk',sans-serif;font-size:0.6rem;letter-spacing:0.05em;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
<script src="/shared/impressum.js" data-owner="flexsiebels" data-variant="full"></script>
</body>
</html>

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>insAIn.</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -55,9 +57,10 @@
<div class="divider"></div>
<p class="subtitle" data-de="Wahnsinnig intelligent." data-en="Insanely intelligent.">Wahnsinnig intelligent.</p>
<p class="footer">insAIn.de</p>
<p class="footer" style="margin-top:12px;"><button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted);color:var(--text-muted);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button><br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small></p>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>JULIETENSITY</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎮</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&family=Inter:wght@300;400;500&display=swap');
@@ -582,14 +584,12 @@
<footer>
<div class="container">
<p>julietensity.de</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">DE</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,12 +3,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KeinCo.de — Kein Code. Nur Ergebnisse.</title>
<meta name="description" content="KeinCo.de — Du beschreibst das Problem, KI baut die Loesung. Kein Code, kein No-Code-Tool. Einfach Ergebnisse.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>K</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -612,11 +614,12 @@
<div class="container">
<div class="footer-brand"><span class="hi">K</span>e<span class="hi">I</span>nCo<span style="color:var(--text-muted)">.de</span></div>
<p class="footer-sub" data-de="Kein Code. Nur Ergebnisse. — Ein Projekt von msbls.de" data-en="No code. Just results. — A project by msbls.de">Kein Code. Nur Ergebnisse. — Ein Projekt von msbls.de</p>
<button data-i18n-toggle style="margin-top:16px;background:none;border:1px solid var(--border);color:var(--text-secondary);padding:6px 14px;border-radius:6px;font-size:0.75rem;cursor:pointer;font-family:inherit;letter-spacing:0.06em;">EN</button>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>kAIneFrage — KI-Beratung ohne Fragezeichen</title>
<meta name="description" content="kAIneFrage — KI + keine Frage. Klarheit statt Buzzwords. AI-Beratung, die Antworten liefert.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>?</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap');
@@ -650,14 +652,12 @@
<footer>
<div class="container">
<p>k<span style="color: var(--accent);">AI</span>neFrage.de — <span data-de="ein Projekt von" data-en="a project by">ein Projekt von</span> <a href="https://msbls.de" target="_blank" rel="noopener">msbls.de</a></p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>kAInstress — KI, die Stress nimmt</title>
<meta name="description" content="kAInstress — Kuenstliche Intelligenz, die dir den Alltag abnimmt. Weniger Stress, mehr Fokus.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>K</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@@ -574,14 +576,12 @@
<footer>
<div class="container">
<p>k<span style="color: var(--accent);">AI</span>nstress.de — <span data-de="ein Projekt von" data-en="a project by">ein Projekt von</span> <a href="https://msbls.de" target="_blank" rel="noopener">msbls.de</a></p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,10 +3,12 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>keinefreun.de — Offizielle Seite der Freundlosigkeit</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700&family=Inter:wght@300;400;500;600&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -28,6 +30,11 @@
--border: #222228;
}
[data-theme="light"] {
--text-dimmer: #5a5662;
--accent-dim: #5d28b8;
}
html {
scroll-behavior: smooth;
}
@@ -627,10 +634,11 @@
<div class="footer-domain">keinefreun.de</div>
<p data-de="Ein Projekt von jemandem ohne Freunde" data-en="A project by someone without friends">Ein Projekt von jemandem ohne Freunde</p>
<p style="margin-top: 0.5rem; opacity: 0.4;" data-de="© 2026 — Alle Rechte vorbehalten. Nicht dass es jemanden interessiert." data-en="© 2026 — All rights reserved. Not that anyone cares.">&copy; 2026 &mdash; Alle Rechte vorbehalten. Nicht dass es jemanden interessiert.</p>
<button data-i18n-toggle style="margin-top:16px;background:none;border:1px solid var(--border);color:var(--text-dim);padding:6px 14px;border-radius:6px;font-size:0.75rem;cursor:pointer;font-family:inherit;letter-spacing:0.06em;">EN</button>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KIlemma — Die Fragen, die keine KI beantworten kann</title>
<meta name="description" content="Die unlösbaren Fragen der KI-Ära. Ein Dilemma hat keine Lösung. Nur eine Entscheidung.">
<meta property="og:title" content="KIlemma">
@@ -12,6 +13,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400;1,500&family=Inter:wght@300;400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -307,10 +309,6 @@
<footer class="footer">
<p class="footer-quote" data-de="Ein Dilemma hat keine Lösung.<br><strong>Nur eine Entscheidung.</strong>" data-en="A dilemma has no solution.<br><strong>Only a decision.</strong>">Ein Dilemma hat keine Lösung.<br><strong>Nur eine Entscheidung.</strong></p>
<p class="footer-meta">kilemma.de</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
</main>
@@ -328,6 +326,8 @@
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -1,11 +1,13 @@
<!DOCTYPE html>
<html lang="de">
<html lang="de" data-theme-lock="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KIlibri — Die kleinste KI die du je gesehen hast</title>
<meta name="description" content="KIlibri — KI + Kolibri. Klein, schnell, bunt. Mikro-KI für Zuhause. Smart Home mit Flügelschlag.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🐦</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;600;700;800&display=swap');
@@ -267,14 +269,11 @@
<footer>
<div class="container">
<p data-de="🐦 &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;libri — Klein, schnell, bunt. Die &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt; für dein Nest." data-en="🐦 &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;libri — Small, fast, colourful. The &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt; for your nest.">🐦 <span class="ki">KI</span>libri — Klein, schnell, bunt. Die <span class="ki">KI</span> für dein Nest.</p> <div class="onepager-msbls-link" style="text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;">Ein Projekt von <a href="https://msbls.de" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;">msbls.de</a></div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KIlitär — KI im Einsatz</title>
<meta name="description" content="KIlitär — KI + Militär. Strategische KI-Operationen. Satirische Militär-KI für den Alltag.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>★</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Inter:wght@300;400;500;600;700&display=swap');
@@ -26,6 +28,21 @@
--red-star: #cc2200;
}
[data-theme="light"] {
--bg: #f4f2e8; /* khaki cream */
--bg-elevated: #fbfaf2;
--bg-card: #ffffff;
--border: rgba(60, 70, 30, 0.14);
--text: #1a1f10;
--text-dim: #4a5230;
--text-muted: #6b7245;
--olive: #4a6b18; /* deeper olive, AA on cream */
--olive-light: #6b8e23;
--olive-glow: rgba(74, 107, 24, 0.12);
--amber: #8a6614;
--red-star: #a01c00;
}
html { scroll-behavior: smooth; }
body {
@@ -288,14 +305,11 @@
<footer>
<div class="container">
<p data-de="★ &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;litär — &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt; im Einsatz. Strategisch. Taktisch. Sinnlos. ★" data-en="★ &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;litär — &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt; deployed. Strategic. Tactical. Pointless. ★"><span class="ki">KI</span>litär — <span class="ki">KI</span> im Einsatz. Strategisch. Taktisch. Sinnlos. ★</p> <div class="onepager-msbls-link" style="text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;">Ein Projekt von <a href="https://msbls.de" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;">msbls.de</a></div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KIllegal — Diese KI ist nicht erlaubt</title>
<meta name="description" content="KIllegal — KI + Illegal. Verbotene KI. Sie existiert trotzdem. Satirische Polizei-Absperrung für künstliche Intelligenz.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🚧</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700;800&family=Inter:wght@300;400;500;600&display=swap');
@@ -296,14 +298,11 @@
<footer>
<div class="container">
<p data-de="🚧 <span class=&quot;ki&quot;>KI</span>llegal — Diese <span class=&quot;ki&quot;>KI</span> ist nicht erlaubt. Aber sie existiert trotzdem." data-en="🚧 <span class=&quot;ki&quot;>KI</span>llegal — This <span class=&quot;ki&quot;>AI</span> is not permitted. But it exists anyway.">🚧 <span class="ki">KI</span>llegal — Diese <span class="ki">KI</span> ist nicht erlaubt. Aber sie existiert trotzdem.</p> <div class="onepager-msbls-link" style="text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;">Ein Projekt von <a href="https://msbls.de" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;">msbls.de</a></div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -1,11 +1,13 @@
<!DOCTYPE html>
<html lang="de">
<html lang="de" data-theme-lock="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KIllionär — Making KIllions seit gestern</title>
<meta name="description" content="KIllionär — KI + Millionär. Werde reich mit KI. Satirische Vermögensberatung der Zukunft.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>💰</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700;900&family=Inter:wght@300;400;500;600&display=swap');
@@ -297,14 +299,11 @@
<div class="container">
<p data-de="<span class=&quot;ki&quot;>KI</span>llionär — Making <span class=&quot;ki&quot;>KI</span>llions seit gestern." data-en="<span class=&quot;ki&quot;>KI</span>llionär — Making <span class=&quot;ki&quot;>KI</span>llions since yesterday."><span class="ki">KI</span>llionär — Making <span class="ki">KI</span>llions seit gestern.</p>
<p style="margin-top: 8px;" data-de="* Alle Testimonials sind frei erfunden. Wie die meisten KI-Versprechen." data-en="* All testimonials are entirely fictitious. Like most AI promises.">* Alle Testimonials sind frei erfunden. Wie die meisten KI-Versprechen.</p> <div class="onepager-msbls-link" style="text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;">Ein Projekt von <a href="https://msbls.de" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;">msbls.de</a></div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KIllions — Die Währung der KI-Ära</title>
<meta name="description" content="KIllions — KI + Millions. 1 KIllion = unendlich Möglichkeiten. Die Kryptowährung die es nie geben wird.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>◈</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Inter:wght@300;400;500;600&display=swap');
@@ -287,14 +289,11 @@
<footer>
<div class="container">
<p data-de="&lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;llions — 1 &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;llion = ∞ Möglichkeiten = 0 Euro" data-en="&lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;llions — 1 &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;llion = ∞ Possibilities = 0 Euro"><span class="ki">KI</span>llions — 1 <span class="ki">KI</span>llion = ∞ Möglichkeiten = 0 Euro</p> <div class="onepager-msbls-link" style="text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;">Ein Projekt von <a href="https://msbls.de" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;">msbls.de</a></div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -1,11 +1,13 @@
<!DOCTYPE html>
<html lang="de">
<html lang="de" data-theme-lock="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KIlluminati — Sie wissen alles. Sie sind KI.</title>
<meta name="description" content="KIlluminati — KI + Illuminati. Die geheime KI-Verschwörung. Sie sehen alles. Sie wissen alles. Sie sind KI.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>👁</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700;900&family=Inter:wght@300;400;500&display=swap');
@@ -287,14 +289,11 @@
<footer>
<div class="container">
<p data-de="&lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;lluminati — Novus Ordo Algorithmorum" data-en="&lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;lluminati — Novus Ordo Algorithmorum"><span class="ki">KI</span>lluminati — Novus Ordo Algorithmorum</p> <div class="onepager-msbls-link" style="text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;">Ein Projekt von <a href="https://msbls.de" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;">msbls.de</a></div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -1,11 +1,13 @@
<!DOCTYPE html>
<html lang="de">
<html lang="de" data-theme-lock="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KIllusion — Nichts ist echt. Alles ist KI.</title>
<meta name="description" content="KIllusion — KI + Illusion. Was du siehst ist nicht echt. Was du liest auch nicht. Meta-Satire über KI-generierte Realität.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>◎</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500&display=swap');
@@ -278,14 +280,11 @@
<footer>
<div class="container">
<p data-de="&lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;llusion — Nichts ist echt. Alles ist &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;." data-en="&lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;llusion — Nothing is real. Everything is &lt;span class=&quot;ki&quot;&gt;KI&lt;/span&gt;."><span class="ki">KI</span>llusion — Nichts ist echt. Alles ist <span class="ki">KI</span>.</p> <div class="onepager-msbls-link" style="text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;">Ein Projekt von <a href="https://msbls.de" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;">msbls.de</a></div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,12 +3,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KIlofant — KI so groß, dass der Boden wackelt</title>
<meta name="description" content="KIlofant — KI + Elefant. Verspielt, groß, unvergesslich. KI-Lösungen die man nicht vergisst.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🐘</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@400;500;600;700;800&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0; padding: 0; box-sizing: border-box;
@@ -741,10 +743,6 @@
<div class="footer-elephants wobble-it">🐘 🐘 🐘 🐘 🐘</div>
<p class="footer-text shake-it" data-de="Hier war mal eine Webseite. Dann kam der KIlofant." data-en="There used to be a website here. Then came the KIlofant.">Hier war mal eine Webseite. Dann kam der KIlofant.</p>
<p class="footer-copy" data-de="© 2026 KIlofant.de — ein Projekt von &lt;a href=&quot;https://msbls.de&quot;&gt;msbls.de&lt;/a&gt;" data-en="© 2026 KIlofant.de — a project by &lt;a href=&quot;https://msbls.de&quot;&gt;msbls.de&lt;/a&gt;">&copy; 2026 KIlofant.de — ein Projekt von <a href="https://msbls.de">msbls.de</a></p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--tusk,#F5E6C8);color:var(--tusk,#F5E6C8);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;opacity:0.7;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--tusk,#F5E6C8);font-size:0.6rem;opacity:0.4;">Maschinell übersetzt</small>
</div>
</footer>
</div>
@@ -788,6 +786,8 @@
observer.observe(herdParade);
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KInough — Es reicht.</title>
<meta name="description" content="Es reicht.">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
* {
margin: 0;
@@ -79,11 +81,9 @@
<p data-de="Wir fordern ein Innehalten. Ein Moratorium. Zeit, um zu verstehen, was wir gebaut haben &mdash; bevor sie versteht, was wir sind." data-en="We demand a pause. A moratorium. Time to understand what we have built — before it understands what we are.">Wir fordern ein Innehalten. Ein Moratorium. Zeit, um zu verstehen, was wir gebaut haben &mdash; bevor sie versteht, was wir sind.</p>
<p class="echo" data-de="Es reicht." data-en="Enough.">Es reicht.</p>
</main>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KiNOWHOW — KI-Wissen. Jetzt.</title>
<meta name="description" content="KiNOWHOW — KI + Know-How. Beratung, Wissen und Strategie rund um künstliche Intelligenz.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>K</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap');
@@ -649,14 +651,12 @@
<footer>
<div class="container">
<p><span style="color: var(--accent);">Ki</span>NOWHOW.de — <span data-de="ein Projekt von" data-en="a project by">ein Projekt von</span> <a href="https://msbls.de" target="_blank" rel="noopener">msbls.de</a></p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,11 +3,13 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>KItox — Dein KI-Detox</title>
<meta name="description" content="KItox ist die 7-Tage KI-Detox Challenge. Bewusster Entzug von künstlicher Intelligenz.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -417,10 +419,6 @@
<footer>
<span>KItox &mdash; 2026</span>
<div style="text-align:center;margin-top:16px;opacity:1;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script>
@@ -436,6 +434,8 @@
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>knzlmgmt.</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚖️</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -55,9 +57,10 @@
<div class="divider"></div>
<p class="subtitle" data-de="Kanzleimanagement. Neu gedacht." data-en="Law firm management. Reimagined.">Kanzleimanagement. Neu gedacht.</p>
<p class="footer">knzlmgmt.de</p>
<p class="footer" style="margin-top:12px;"><button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted);color:var(--text-muted);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button><br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small></p>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>kopffrAI.</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🧠</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -55,9 +57,10 @@
<div class="divider"></div>
<p class="subtitle" data-de="Klarer Kopf. Kuenstliche Intelligenz." data-en="Clear mind. Artificial intelligence.">Klarer Kopf. Kuenstliche Intelligenz.</p>
<p class="footer">kopffrAI.de</p>
<p class="footer" style="margin-top:12px;"><button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted);color:var(--text-muted);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button><br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small></p>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Legal AIs — KI fuer die Rechtspraxis</title>
<meta name="description" content="Legal AIs — Kuenstliche Intelligenz, die Rechtsarbeit transformiert. Recherche, Analyse, Entwuerfe — praezise, schnell, zuverlaessig.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>L</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@@ -377,14 +379,12 @@
<footer>
<div class="container">
<p>Legal<span style="color: var(--accent);">AI</span>s.de — <span data-de="ein Projekt von" data-en="a project by">ein Projekt von</span> <a href="https://msbls.de" target="_blank" rel="noopener">msbls.de</a></p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Lex Siebels — Knowledge Lawyer, Patent & UPC</title>
<meta name="description" content="Matthias Siebels — Knowledge Lawyer bei Hogan Lovells. Patentrecht, Unified Patent Court, IP-Strategie, Legal Tech.">
<meta property="og:title" content="Lex Siebels — Knowledge Lawyer, Patent & UPC">
@@ -12,6 +13,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,600;0,700;1,400&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -29,6 +31,10 @@
--sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
[data-theme="light"] {
--text-bright: #1a1a1a;
}
html { scroll-behavior: smooth; }
body {
@@ -533,10 +539,6 @@
Dies ist eine persönliche Seite. Keine Rechtsberatung.
&mdash; This is a personal page. No legal advice.
</div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid rgba(200,164,110,0.3);color:rgba(200,164,110,0.6);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:rgba(200,164,110,0.4);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
@@ -556,6 +558,8 @@
<script src="/shared/impressum.js" data-owner="flexsiebels" data-variant="full"></script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Mach es doch einfach.</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🚀</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&family=Inter:wght@300;400;500&display=swap');
@@ -20,6 +22,10 @@
--line: rgba(232, 120, 58, 0.15);
}
[data-theme="light"] {
--text-faint: #6e6452;
}
html { scroll-behavior: smooth; }
body {
@@ -220,15 +226,13 @@
<footer>
<p>machesdocheinfach.de</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Otto — mAI Personal Assistant</title>
<meta name="description" content="Otto ist Matthias' autonomer KI-Assistent. E-Mail, Kalender, Infrastruktur, Recherche — alles im Griff.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🐙</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500&display=swap');
@@ -433,15 +435,13 @@
<footer>
<div class="container">
<p><span class="otto">Otto</span> — mai-otto.de — <span data-de="ein Projekt von" data-en="a project by">ein Projekt von</span> <a href="https://msbls.de" style="color:var(--text-muted);text-decoration:none;">msbls.de</a></p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/impressum.js" data-owner="flexsiebels" data-variant="full"></script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Martin Siebels — Profil</title>
<meta name="description" content="Martin Siebels — Osnabrück. Berufliches Profil.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>M</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@@ -411,15 +413,13 @@
<footer>
<div class="container">
<p>martinsiebels.de</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
<script src="/shared/impressum.js" data-owner="martinsiebels" data-variant="full"></script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,11 +3,13 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Matthias Breier — IT Projektmanager</title>
<meta name="description" content="Matthias Breier — IT Projektmanager. Digitale Transformation, Agile Methoden, Strategische IT-Beratung.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0;
@@ -32,6 +34,10 @@
--text-light: #cbd5e1;
}
[data-theme="light"] {
--text-light: #475569;
}
html {
scroll-behavior: smooth;
}
@@ -931,10 +937,6 @@
<footer>
<div class="footer-brand">Matthias Breier</div>
<p data-de="IT Projektmanagement" data-en="IT Project Management">IT Projektmanagement</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid rgba(255,255,255,0.3);color:rgba(255,255,255,0.3);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:rgba(255,255,255,0.3);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script>
@@ -953,6 +955,8 @@
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,12 +3,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>OmaKIse — KI-Erlebnis, vom Chef kuratiert</title>
<meta name="description" content="OmaKIse — Omakase + KI. Kuratierte KI-Beratung wie ein Omakase-Menü. Du bekommst was der Chef empfiehlt.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🍣</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+JP:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after {
margin: 0; padding: 0; box-sizing: border-box;
@@ -634,11 +636,12 @@
<footer>
<div class="container">
<p>Oma<span style="color:var(--aka);">KI</span>se.de — ein Projekt von <a href="https://msbls.de">msbls.de</a></p>
<button data-i18n-toggle style="margin-top:12px;background:none;border:1px solid var(--ink-ghost);color:var(--ink-faint);padding:5px 12px;border-radius:4px;font-size:0.75rem;cursor:pointer;font-family:inherit;letter-spacing:0.06em;">EN</button>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,12 +3,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>OraKIl — Frag das Orakel</title>
<meta name="description" content="Das Orakel weiß. Frag.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔮</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@300;400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -27,6 +29,10 @@
--text-muted: #837867;
}
[data-theme="light"] {
--gold-dim: #6c5b1a;
}
html { scroll-behavior: smooth; }
body {
@@ -348,11 +354,6 @@
<div class="ornament-bottom">&#x2726; &#x2726; &#x2726;</div>
<div class="footer-toggle">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
<script>
(function() {
var hint = document.querySelector('.hint');
@@ -443,6 +444,8 @@
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>oster🥚AI — Frohe Ostern!</title>
<meta name="description" content="osterAI — Frohe Ostern mit einer Prise künstlicher Intelligenz.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🐣</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&family=Inter:wght@300;400;500;600&display=swap');
@@ -27,6 +29,10 @@
--blue: #5898d0;
}
[data-theme="light"] {
--text-faint: #6e6452;
}
html { scroll-behavior: smooth; }
body {
@@ -263,15 +269,13 @@
<footer>
<p data-de="oster&lt;span class=&quot;ai&quot;&gt;AI&lt;/span&gt;.de — Frohe Ostern 2026" data-en="oster&lt;span class=&quot;ai&quot;&gt;AI&lt;/span&gt;.de — Happy Easter 2026">oster<span class="ai">AI</span>.de — Frohe Ostern 2026</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,7 +3,9 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>ParagraphenrAIter — KI trifft Jura</title>
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700&family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
@@ -27,6 +29,20 @@
--accent-red: #8b3a3a;
}
[data-theme="light"] {
--gold: #8a6f1e; /* deep gold, AA on cream (5.4:1) */
--gold-light: #c9a84c; /* original gold becomes accent-light */
--gold-dim: #6e5818; /* darker, AA on white */
--dark: #f8f6ee; /* warm parchment */
--dark-surface: #ffffff;
--dark-card: #ffffff;
--dark-border: rgba(60, 50, 20, 0.12);
--text: #1a1815;
--text-dim: #5a554a;
--accent-blue: #2a4f7c;
--accent-red: #6a2828;
}
html {
scroll-behavior: smooth;
}
@@ -726,13 +742,11 @@
<p class="footer-brand">Paragraphen<span>rAI</span>ter</p>
<p class="footer-legal" data-de="© 2026 · Recht. Aber schlauer." data-en="© 2026 · Law. But smarter.">&copy; 2026 &middot; Recht. Aber schlauer.</p>
<div class="onepager-msbls-link" style="text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;">Ein Projekt von <a href="https://msbls.de" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;">msbls.de</a></div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Patentonkel — Dein Onkel fürs Patent. Nur klüger.</title>
<meta name="description" content="KI-gestützte Patentberatung. Kein Anwalt, aber der klügste Onkel, den du je hattest.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>👓</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,400;0,600;0,700;0,800;0,900;1,400;1,700&family=Space+Mono:wght@400;700&display=swap');
@@ -30,6 +32,10 @@
--surface: #f5efe3;
}
[data-theme="light"] {
--warm-dim: #6e5418;
}
html { scroll-behavior: smooth; }
body {
@@ -652,13 +658,11 @@
<p class="footer-tagline" data-de="Familiär. Kompetent. KI." data-en="Familiar. Competent. AI.">Familiär. Kompetent. KI.</p>
<p class="footer-legal" data-de="© 2026 · Familienkompatibel ausgedacht." data-en="© 2026 · Family-compatibly conceived.">&copy; 2026 &middot; Familienkompatibel ausgedacht.</p>
<div class="onepager-msbls-link" style="text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;">Ein Projekt von <a href="https://msbls.de" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;">msbls.de</a></div>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>schulfrAI.</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎓</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -55,9 +57,10 @@
<div class="divider"></div>
<p class="subtitle" data-de="Lernen ohne Grenzen." data-en="Learning without limits.">Lernen ohne Grenzen.</p>
<p class="footer">schulfrAI.de</p>
<p class="footer" style="margin-top:12px;"><button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted);color:var(--text-muted);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button><br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small></p>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Slopschild — Das Stoppschild für KI-Slop</title>
<meta name="description" content="Was du mir gerade geschickt hast, ist KI-Slop. Bitte hör auf.">
<meta property="og:title" content="SLOP. Bitte hör auf.">
@@ -12,6 +13,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -360,12 +362,10 @@
<footer>
<p data-de="slopschild.de — Zum Weiterleiten an Slop-Verbreiter." data-en="slopschild.de — For forwarding to slop spreaders.">slopschild.de — Zum Weiterleiten an Slop-Verbreiter.</p>
<p style="margin-top: 2rem; font-size: 0.7rem; opacity: 0.35; font-style: italic;" data-de="*this website has been fully generated by ai and should be considered slop" data-en="*this website has been fully generated by ai and should be considered slop">*this website has been fully generated by ai and should be considered slop</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>sMARTin 3D — Smart in 3D</title>
<meta name="description" content="sMARTin 3D — Dein 3D-Druck-Shop. Individuelle Teile, Prototypen, Kleinserien. Smart gedacht, präzise gedruckt.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🧊</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500&display=swap');

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>sorgenfrAI.</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🧘</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -55,9 +57,10 @@
<div class="divider"></div>
<p class="subtitle" data-de="Deine Sorgen. Unsere AI." data-en="Your worries. Our AI.">Deine Sorgen. Unsere AI.</p>
<p class="footer">sorgenfrAI.de</p>
<p class="footer" style="margin-top:12px;"><button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted);color:var(--text-muted);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button><br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small></p>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,10 +3,12 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>traihard.de — Marginal Gains. Maximum Effort.</title>
<meta name="description" content="Trai Hard — Optimierungswahn in allen Lebensbereichen. Rennrad, Fantasy Football, Productivity, Fitness. Marginal Gains everywhere.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600;700&family=Orbitron:wght@700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
@@ -20,6 +22,10 @@
--accent-glow: rgba(255, 62, 0, 0.12);
}
[data-theme="light"] {
--muted: #555568;
}
html { scroll-behavior: smooth; }
body {

View File

@@ -3,9 +3,11 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Florian von Schreitter — Kartellrecht</title>
<meta name="description" content="Florian von Schreitter — Kartellrechtsspezialist bei Hogan Lovells. Fusionskontrolle, Kartellverfahren, Compliance.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>F</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@@ -668,10 +670,6 @@
<footer>
<div class="container">
<p>vonschr<span style="color: var(--accent);">AI</span>tter.de — Florian von Schreitter</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</div>
</footer>
@@ -687,6 +685,8 @@
</script>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,8 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>Warte bitte.</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🫖</text></svg>">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500&family=Newsreader:ital,wght@0,300;0,400;1,300;1,400&display=swap');
@@ -20,6 +22,10 @@
--line: rgba(196, 168, 130, 0.2);
}
[data-theme="light"] {
--text-faint: #6e685e;
}
html { scroll-behavior: smooth; }
body {
@@ -434,15 +440,13 @@
<footer>
<p>wartebitte.de</p>
<div style="text-align:center;margin-top:16px;">
<button data-i18n-toggle title="Maschinell übersetzt / Machine-translated — German is the original." style="background:none;border:1px solid var(--text-muted,#444);color:var(--text-muted,#444);font-size:0.65rem;letter-spacing:0.1em;padding:4px 12px;border-radius:4px;cursor:pointer;">EN</button>
<br><small data-de="Maschinell übersetzt" data-en="Machine-translated" style="color:var(--text-muted,#444);font-size:0.6rem;opacity:0.5;">Maschinell übersetzt</small>
</div>
</footer>
</div>
<script src="/shared/ai-disclosure.js" data-tone="playful"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -3,12 +3,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title>ZenSiebels — 静けさ</title>
<meta name="description" content="ZenSiebels — Stille. Klarheit. Praxis.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🪷</text></svg>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+JP:wght@200;300;400;500;600&family=Inter:wght@200;300;400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

View File

@@ -3,12 +3,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>
<title{{title_i18n}}>{{title}}</title>
<meta name="description" content="{{description}}"{{description_i18n}}>
<meta name="robots" content="index, follow">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>{{favicon}}</text></svg>">
{{fonts}}
{{schema_jsonld}}
<link rel="stylesheet" href="/shared/css/theme.css">
<style>
{{css_variables}}
{{css_responsive}}
@@ -20,6 +22,8 @@
<body class="noise-overlay">
{{body}}
<script src="/shared/ai-disclosure.js" data-tone="{{disclosure_tone}}"></script>
<script src="/shared/theme.js"></script>
<script src="/shared/i18n.js"></script>
<script src="/shared/toggles.js"></script>
</body>
</html>

View File

@@ -4,16 +4,52 @@
Extracts CSS custom properties for backgrounds and text colors,
computes WCAG contrast ratio for likely text-on-bg pairs, flags
violations.
Modes:
--dark (default) Audit :root / [data-theme="dark"] palette
--light Audit [data-theme="light"] palette
--both Audit both, separately
Light-mode audit picks up site-specific [data-theme="light"] blocks.
Sites without their own light block fall through to shared/css/theme.css
(which is checked separately and used as a fallback).
"""
import argparse
import re
import sys
from pathlib import Path
SITES_DIR = Path(__file__).resolve().parent.parent / "sites"
ROOT = Path(__file__).resolve().parent.parent
SITES_DIR = ROOT / "sites"
SHARED_THEME_CSS = ROOT / "shared" / "css" / "theme.css"
HEX_RE = re.compile(r"#([0-9a-fA-F]{3,8})\b")
VAR_DECL_RE = re.compile(r"--([\w-]+)\s*:\s*([^;]+);")
# Match a CSS rule block by selector. Returns body of the block.
def block_body(css, selector_pattern):
"""Return list of bodies from CSS rules whose selector matches the regex."""
bodies = []
# Walk char-by-char to handle nested braces correctly
for match in re.finditer(selector_pattern, css):
start = match.end()
# Find opening brace right after selector
idx = css.find("{", start)
if idx == -1:
continue
depth = 1
i = idx + 1
while i < len(css) and depth > 0:
if css[i] == "{":
depth += 1
elif css[i] == "}":
depth -= 1
i += 1
if depth == 0:
bodies.append(css[idx + 1 : i - 1])
return bodies
def hex_to_rgb(h):
h = h.lstrip("#")
if len(h) == 3:
@@ -27,6 +63,7 @@ def hex_to_rgb(h):
except ValueError:
return None
def relative_luminance(rgb):
def channel(c):
c /= 255
@@ -34,92 +71,177 @@ def relative_luminance(rgb):
r, g, b = (channel(c) for c in rgb)
return 0.2126 * r + 0.7152 * g + 0.0722 * b
def contrast_ratio(rgb1, rgb2):
l1 = relative_luminance(rgb1)
l2 = relative_luminance(rgb2)
lighter, darker = max(l1, l2), min(l1, l2)
return (lighter + 0.05) / (darker + 0.05)
# Variable names that suggest "background" or "text"
BG_KEYS = ("bg", "background", "surface")
TEXT_KEYS = ("text", "fg", "foreground", "color", "muted", "dim", "subtle", "secondary")
def is_bg_var(name):
n = name.lower()
return any(k in n for k in BG_KEYS) and "border" not in n
def is_text_var(name):
n = name.lower()
return any(k in n for k in TEXT_KEYS) and "border" not in n and "bg" not in n.split("-")[0]
def audit(site):
html = (SITES_DIR / site / "index.html")
if not html.exists():
return None
css = html.read_text(errors="ignore")
# Only look at the <style> block(s)
style_blocks = re.findall(r"<style.*?>(.*?)</style>", css, re.DOTALL | re.IGNORECASE)
if not style_blocks:
return None
css_only = "\n".join(style_blocks)
def parse_vars(css_text):
"""Extract CSS custom property name -> RGB triplet, ignoring non-hex values."""
vars_ = {}
for m in VAR_DECL_RE.finditer(css_only):
for m in VAR_DECL_RE.finditer(css_text):
name, val = m.group(1), m.group(2).strip()
# only resolve to hex
hm = HEX_RE.search(val)
if hm:
rgb = hex_to_rgb(hm.group(0))
if rgb:
vars_[name] = rgb
return vars_
def get_style_blocks(html):
"""Return concatenated CSS from all <style> blocks in HTML."""
blocks = re.findall(r"<style.*?>(.*?)</style>", html, re.DOTALL | re.IGNORECASE)
return "\n".join(blocks)
def shared_light_defaults():
"""Read the shared light defaults so sites without overrides get audited
against the actual palette they'll receive at runtime."""
if not SHARED_THEME_CSS.exists():
return {}
css = SHARED_THEME_CSS.read_text(errors="ignore")
bodies = block_body(css, r'\[data-theme="light"\]')
merged = "\n".join(bodies)
return parse_vars(merged)
def audit_palette(vars_, mode_label):
"""Given a {var_name: rgb} palette, return findings dict or None."""
bg_vars = {n: c for n, c in vars_.items() if is_bg_var(n)}
text_vars = {n: c for n, c in vars_.items() if is_text_var(n)}
if not bg_vars or not text_vars:
return None
# Find primary bg (the darkest one is usually --bg)
primary_bg_name = min(bg_vars, key=lambda n: relative_luminance(bg_vars[n]))
bg_rgb = bg_vars[primary_bg_name]
bg_lum = relative_luminance(bg_rgb)
# Only audit dark backgrounds (lum < 0.05 = near-black)
if bg_lum > 0.1:
return None # not a dark site
if mode_label == "dark":
primary_bg_name = min(bg_vars, key=lambda n: relative_luminance(bg_vars[n]))
bg_rgb = bg_vars[primary_bg_name]
bg_lum = relative_luminance(bg_rgb)
if bg_lum > 0.1:
return None # not dark enough to be the dark mode
else: # light
primary_bg_name = max(bg_vars, key=lambda n: relative_luminance(bg_vars[n]))
bg_rgb = bg_vars[primary_bg_name]
bg_lum = relative_luminance(bg_rgb)
if bg_lum < 0.5:
return None # not light enough
findings = []
for tname, trgb in text_vars.items():
ratio = contrast_ratio(trgb, bg_rgb)
if ratio < 4.5: # WCAG AA for body text
if ratio < 4.5:
findings.append((tname, trgb, ratio))
if not findings:
return None
return {
"site": site,
"bg_name": primary_bg_name,
"bg_rgb": bg_rgb,
"findings": findings,
}
results = []
for site in sorted(p.name for p in SITES_DIR.iterdir() if p.is_dir()):
r = audit(site)
if r:
results.append(r)
print(f"Sites with sub-AA text on dark bg: {len(results)}/59\n")
for r in results:
bg = r["bg_rgb"]
print(f"{r['site']} (bg --{r['bg_name']} = #{bg[0]:02x}{bg[1]:02x}{bg[2]:02x})")
for name, rgb, ratio in sorted(r["findings"], key=lambda x: x[2]):
flag = "FAIL" if ratio < 3.0 else ("WEAK" if ratio < 4.5 else "OK")
print(f" {flag:4} ratio {ratio:5.2f} --{name:24} #{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}")
print()
def audit(site, mode, shared_light):
html_path = SITES_DIR / site / "index.html"
if not html_path.exists():
return None
html = html_path.read_text(errors="ignore")
css = get_style_blocks(html)
if not css:
return None
# Summary: how many distinct sites have FAIL (< 3.0) somewhere
fails = [r for r in results if any(ratio < 3.0 for _, _, ratio in r["findings"])]
print(f"\n=== SUMMARY ===")
print(f"Dark-bg sites with at least one FAIL (<3:1): {len(fails)}")
print(f"Dark-bg sites with WEAK (<4.5 but >=3): {len(results) - len(fails)}")
if mode == "dark":
# :root and [data-theme="dark"] both contribute to dark palette
bodies = block_body(css, r":root") + block_body(css, r'\[data-theme="dark"\]')
merged = "\n".join(bodies) if bodies else css
vars_ = parse_vars(merged)
return audit_palette(vars_, "dark")
if mode == "light":
bodies = block_body(css, r'\[data-theme="light"\]')
if bodies:
site_vars = parse_vars("\n".join(bodies))
else:
site_vars = {}
# Site overrides win. Fall back to shared defaults for missing vars.
# AND fall back to :root for any vars the site only defines once.
root_bodies = block_body(css, r":root")
root_vars = parse_vars("\n".join(root_bodies))
merged = dict(root_vars)
merged.update(shared_light)
merged.update(site_vars)
return audit_palette(merged, "light")
return None
def print_results(mode, results, total):
label = "Light" if mode == "light" else "Dark"
print(f"\n=== {label} mode audit ===")
print(f"Sites with sub-AA text on {label.lower()} bg: {len(results)}/{total}\n")
for r in results:
bg = r["bg_rgb"]
print(f"{r['site']} (bg --{r['bg_name']} = #{bg[0]:02x}{bg[1]:02x}{bg[2]:02x})")
for name, rgb, ratio in sorted(r["findings"], key=lambda x: x[2]):
flag = "FAIL" if ratio < 3.0 else ("WEAK" if ratio < 4.5 else "OK")
print(f" {flag:4} ratio {ratio:5.2f} --{name:24} #{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}")
print()
fails = [r for r in results if any(ratio < 3.0 for _, _, ratio in r["findings"])]
print(f" {label}-bg sites with at least one FAIL (<3:1): {len(fails)}")
print(f" {label}-bg sites with WEAK (<4.5 but >=3): {len(results) - len(fails)}")
def run(mode):
sites = sorted(p.name for p in SITES_DIR.iterdir() if p.is_dir())
shared_light = shared_light_defaults() if mode in ("light", "both") else {}
if mode in ("dark", "both"):
results = []
for site in sites:
r = audit(site, "dark", shared_light)
if r:
r["site"] = site
results.append(r)
print_results("dark", results, len(sites))
if mode in ("light", "both"):
results = []
for site in sites:
r = audit(site, "light", shared_light)
if r:
r["site"] = site
results.append(r)
print_results("light", results, len(sites))
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
grp = ap.add_mutually_exclusive_group()
grp.add_argument("--dark", action="store_const", const="dark", dest="mode", help="Audit dark palette only (default)")
grp.add_argument("--light", action="store_const", const="light", dest="mode", help="Audit light palette only")
grp.add_argument("--both", action="store_const", const="both", dest="mode", help="Audit both palettes")
args = ap.parse_args()
mode = args.mode or "dark"
run(mode)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""Insert per-site [data-theme="light"] override block after :root.
Each entry maps site -> dict of var -> light-value. Vars not listed
fall through to shared/css/theme.css defaults.
"""
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
SITES_DIR = ROOT / "sites"
OVERRIDES = {
"6034.de": {
"--green-dim": "#15803d", # AA 4.7:1 on white
},
"allainallain.de": {
"--text-faint": "#5a608a",
},
"commanderkin.de": {
"--green-dim": "#166e16",
},
"hallofraumaier.de": {
"--text-light": "#6e6452",
},
"heygoldi.de": {
"--text-gentle": "#6e6452",
},
"keinefreun.de": {
"--text-dimmer": "#5a5662",
"--accent-dim": "#5d28b8",
},
"lexsiebels.de": {
"--text-bright": "#1a1a1a", # high-contrast text inverts on light bg
},
"machesdocheinfach.de": {
"--text-faint": "#6e6452",
},
"matthiasbreier.de": {
"--text-light": "#475569",
},
"orakil.de": {
"--gold-dim": "#6c5b1a",
},
"osterai.de": {
"--text-faint": "#6e6452",
},
"patentonkel.de": {
"--warm-dim": "#6e5418",
},
"traihard.de": {
"--muted": "#555568",
},
"wartebitte.de": {
"--text-faint": "#6e685e",
},
}
def find_root_block(html):
"""Find the first `:root {...}` block. Returns (start, end) of `}` char or None."""
m = re.search(r":root\s*\{", html)
if not m:
return None
depth = 1
i = m.end()
while i < len(html) and depth > 0:
if html[i] == "{":
depth += 1
elif html[i] == "}":
depth -= 1
i += 1
if depth != 0:
return None
return i # position right after the closing brace
def detect_root_indent(html, root_end):
"""Look at line containing :root to detect indent."""
# find start of line with :root
m = re.search(r"^([ \t]*):root\s*\{", html, re.MULTILINE)
if m:
return m.group(1)
return " "
def patch_site(site, vars_map):
path = SITES_DIR / site / "index.html"
if not path.exists():
return f"missing index.html"
html = path.read_text()
if '[data-theme="light"]' in html:
return f"already has [data-theme=\"light\"] block — skipped"
end = find_root_block(html)
if end is None:
return f"no :root block found"
indent = detect_root_indent(html, end)
inner = "\n".join(f"{indent} {k}: {v};" for k, v in vars_map.items())
block = f"\n\n{indent}[data-theme=\"light\"] {{\n{inner}\n{indent}}}"
new_html = html[:end] + block + html[end:]
path.write_text(new_html)
return f"patched ({len(vars_map)} vars)"
def main():
for site, vars_map in OVERRIDES.items():
result = patch_site(site, vars_map)
print(f" {site}: {result}")
if __name__ == "__main__":
main()

134
tools/patch-theme.py Normal file
View File

@@ -0,0 +1,134 @@
#!/usr/bin/env python3
"""Patch onepager sites with theme-toggle wiring.
Inserts (idempotent):
1. anti-FOUC inline IIFE after <meta name="viewport">
2. <link rel="stylesheet" href="/shared/css/theme.css"> before first <style>
3. <script src="/shared/theme.js"> before <script src="/shared/i18n.js">
4. <script src="/shared/toggles.js"> after <script src="/shared/i18n.js">
Skips sites that already have any of the markers.
Usage:
python3 tools/patch-theme.py <site-dir> [<site-dir> ...]
python3 tools/patch-theme.py --all # all sites with index.html, skip dynamic
"""
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
SITES_DIR = ROOT / "sites"
ANTI_FOUC = "<script>(function(){try{var t=localStorage.getItem('onepager-theme');if(!t)t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();</script>"
THEME_LINK = '<link rel="stylesheet" href="/shared/css/theme.css">'
THEME_SCRIPT = '<script src="/shared/theme.js"></script>'
TOGGLES_SCRIPT = '<script src="/shared/toggles.js"></script>'
def detect_indent(line):
"""Return leading whitespace prefix of a line."""
m = re.match(r"^(\s*)", line)
return m.group(1) if m else ""
def patch(html):
"""Apply 4 insertions idempotently. Returns (new_html, changed_bool)."""
changed = False
# 1) Anti-FOUC after <meta name="viewport"...>
if "onepager-theme" not in html:
m = re.search(r'^([ \t]*)<meta\s+name="viewport"[^>]*>\s*$', html, re.MULTILINE)
if m:
indent = m.group(1)
insert = f"\n{indent}{ANTI_FOUC}"
html = html[: m.end()] + insert + html[m.end():]
changed = True
else:
return html, False # can't anchor — bail
# 2) <link rel="stylesheet" href="/shared/css/theme.css"> before first <style>
if THEME_LINK not in html:
m = re.search(r'^([ \t]*)<style[ >]', html, re.MULTILINE)
if m:
indent = m.group(1)
insert = f"{indent}{THEME_LINK}\n"
html = html[: m.start()] + insert + html[m.start():]
changed = True
# 3) theme.js before i18n.js
if "/shared/theme.js" not in html:
m = re.search(r'^([ \t]*)<script\s+src="/shared/i18n\.js"></script>\s*$', html, re.MULTILINE)
if m:
indent = m.group(1)
insert = f"{indent}{THEME_SCRIPT}\n"
html = html[: m.start()] + insert + html[m.start():]
changed = True
# 4) toggles.js after i18n.js
if "/shared/toggles.js" not in html:
m = re.search(r'^([ \t]*)<script\s+src="/shared/i18n\.js"></script>\s*$', html, re.MULTILINE)
if m:
indent = m.group(1)
# Insert AFTER the i18n.js line
end = m.end()
# Make sure we put on a new line
insert = f"\n{indent}{TOGGLES_SCRIPT}"
html = html[:end] + insert + html[end:]
changed = True
return html, changed
def main():
if "--all" in sys.argv:
sites = []
for d in sorted(SITES_DIR.iterdir()):
if d.is_dir() and (d / "index.html").exists():
sites.append(d)
else:
sites = []
for arg in sys.argv[1:]:
p = Path(arg)
if not p.is_absolute():
p = SITES_DIR / arg
if not p.is_dir():
print(f"skip: {arg} not a directory", file=sys.stderr)
continue
if not (p / "index.html").exists():
print(f"skip: {p}/index.html missing", file=sys.stderr)
continue
sites.append(p)
if not sites:
print("no sites to patch", file=sys.stderr)
sys.exit(2)
patched, skipped, missing_anchor = 0, 0, []
for site in sites:
path = site / "index.html"
before = path.read_text()
after, changed = patch(before)
if not changed:
if "onepager-theme" in before and "/shared/toggles.js" in before:
skipped += 1
print(f" [unchanged] {site.name}")
else:
missing_anchor.append(site.name)
print(f" [no-anchor] {site.name}")
else:
path.write_text(after)
patched += 1
print(f" [patched] {site.name}")
print(f"\nPatched: {patched}, Already-up-to-date: {skipped}, No-anchor: {len(missing_anchor)}")
if missing_anchor:
print("Missing anchors (manual fix needed):")
for s in missing_anchor:
print(f" - {s}")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""Remove the redundant footer [data-i18n-toggle] block.
After Issue #13 the top-right widget owns toggling. The legacy footer button
+ "Maschinell übersetzt" disclaimer line are now redundant — top-right button
has the same disclaimer in its title tooltip, and ai-disclosure.js still
adds the AI footnote.
Two patterns handled:
Pattern A: <div style="text-align:center;margin-top:16px..."> ... </div>
Pattern B (knzlmgmt, schulfrai): <p class="footer" style="margin-top:12px;">...</p>
Idempotent — sites without the patterns are skipped.
"""
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
SITES_DIR = ROOT / "sites"
# Pattern A: a div wrapping the button + br + small disclaimer.
# Allow extra style segments after margin-top:16px (some sites add ;opacity:1 etc.)
PATTERN_A = re.compile(
r'\s*<div\s+style="text-align:center;margin-top:16px[^"]*">\s*'
r'<button\s+data-i18n-toggle[\s\S]*?</button>\s*'
r'<br>\s*<small[^>]*data-de="Maschinell übersetzt"[^>]*>[\s\S]*?</small>\s*'
r'</div>',
re.IGNORECASE,
)
# Pattern B: <p class="footer" style="margin-top:12px;">...</p>
PATTERN_B = re.compile(
r'\s*<p\s+class="footer"\s+style="margin-top:12px;">'
r'<button\s+data-i18n-toggle[\s\S]*?'
r'</p>',
re.IGNORECASE,
)
# Pattern C: bare button (optionally followed by <br><small>...</small>),
# not enclosed in our standard wrappers. Used by sites that placed the
# toggle inline in a footer paragraph (ichbinotto, kainco, keinefreun,
# omakise) or in a custom-classed div (orakil).
PATTERN_C = re.compile(
r'\s*<button\s+data-i18n-toggle[\s\S]*?</button>'
r'(?:\s*<br>\s*<small[^>]*data-de="Maschinell übersetzt"[^>]*>[\s\S]*?</small>)?',
re.IGNORECASE,
)
# Pattern D: <div class="footer-toggle">...</div> (orakil only, but tolerant)
PATTERN_D = re.compile(
r'\s*<div\s+class="footer-toggle">\s*'
r'<button\s+data-i18n-toggle[\s\S]*?</button>\s*'
r'<br>\s*<small[^>]*data-de="Maschinell übersetzt"[^>]*>[\s\S]*?</small>\s*'
r'</div>',
re.IGNORECASE,
)
def patch(html):
new = html
# Wrappers first (they contain a button); then bare-button fallback.
new, n_a = PATTERN_A.subn("", new)
new, n_b = PATTERN_B.subn("", new)
new, n_d = PATTERN_D.subn("", new)
new, n_c = PATTERN_C.subn("", new)
return new, n_a + n_b + n_c + n_d
def main():
sites = sorted(p for p in SITES_DIR.iterdir() if p.is_dir() and (p / "index.html").exists())
total_removed = 0
sites_changed = 0
sites_with_attr = 0
sites_residual = []
for site in sites:
path = site / "index.html"
before = path.read_text()
if "data-i18n-toggle" not in before:
continue
sites_with_attr += 1
# Count old buttons before removal (we expect exactly 1 footer button per site;
# the new top-right widget injects its own at runtime, not in source).
before_count = len(re.findall(r"data-i18n-toggle", before))
after, removed = patch(before)
after_count = len(re.findall(r"data-i18n-toggle", after))
if removed > 0:
path.write_text(after)
sites_changed += 1
total_removed += removed
print(f" [removed {removed}] {site.name} (data-i18n-toggle: {before_count} -> {after_count})")
if after_count > 0:
sites_residual.append((site.name, after_count))
else:
sites_residual.append((site.name, before_count))
print(f" [no-match] {site.name} (still has {before_count} data-i18n-toggle)")
print(f"\nSites changed: {sites_changed}/{sites_with_attr}")
print(f"Total wrappers removed: {total_removed}")
if sites_residual:
print(f"\nSites with residual data-i18n-toggle attribute (manual review):")
for name, n in sites_residual:
print(f" - {name}: {n}")
if __name__ == "__main__":
main()