Files
onepager/sites/wartebitte.de/index.html
m 84b28d64f5 feat: AI/KI disclosure footer — shared/ai-disclosure.js + all 54 sites
Self-injecting script following impressum.js pattern:
- data-tone attribute: playful | serious | minimal | none
- Reads document.documentElement.lang for KI (de) vs AI (en)
- MutationObserver on lang attr for i18n toggle compat
- All tones link to msbls.de/ki
- Injected into all 54 custom sites with data-tone="playful"
- Template infra: base.html includes script, render.sh reads disclosure.tone
- disclosure.tone added to 3 example site.yaml files

Implements m/onepager#2
2026-04-01 13:26:04 +02:00

449 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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>">
<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');
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg: #faf8f5;
--text: #2a2520;
--text-soft: #6b6259;
--text-faint: #a69e94;
--accent: #c4a882;
--accent-soft: rgba(196, 168, 130, 0.15);
--line: rgba(196, 168, 130, 0.2);
}
html { scroll-behavior: smooth; }
body {
font-family: 'Newsreader', Georgia, serif;
background: var(--bg);
color: var(--text);
line-height: 1.8;
-webkit-font-smoothing: antialiased;
min-height: 100vh;
}
.page {
max-width: 540px;
margin: 0 auto;
padding: 120px 24px 80px;
}
/* Teapot */
.icon {
font-size: 2.8rem;
margin-bottom: 48px;
opacity: 0.8;
animation: steam 4s ease-in-out infinite;
}
@keyframes steam {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-4px); }
}
h1 {
font-family: 'Newsreader', Georgia, serif;
font-size: clamp(2.2rem, 5vw, 3rem);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.2;
margin-bottom: 40px;
color: var(--text);
}
.rule {
width: 40px;
height: 1px;
background: var(--accent);
margin-bottom: 40px;
opacity: 0.5;
}
.prose {
margin-bottom: 48px;
}
.prose p {
font-size: 1.08rem;
color: var(--text-soft);
font-weight: 300;
line-height: 1.9;
margin-bottom: 24px;
}
.prose p:last-child {
margin-bottom: 0;
}
.prose em {
font-style: italic;
color: var(--text);
}
.highlight {
background: var(--accent-soft);
padding: 28px 32px;
border-radius: 12px;
border-left: 2px solid var(--accent);
margin: 48px 0;
}
.highlight p {
font-size: 1.05rem;
color: var(--text-soft);
font-weight: 300;
line-height: 1.8;
font-style: italic;
}
.closing {
margin-top: 56px;
padding-top: 40px;
border-top: 1px solid var(--line);
}
.closing p {
font-size: 1rem;
color: var(--text-faint);
font-weight: 300;
line-height: 1.8;
}
.breath-container {
text-align: center;
margin: 56px 0;
font-family: 'Inter', sans-serif;
}
.breath-circle {
width: 140px; height: 140px;
border-radius: 50%;
border: 2px solid var(--accent);
margin: 0 auto 24px;
display: flex; align-items: center; justify-content: center;
transition: transform 0.1s ease, box-shadow 0.1s ease, border-color 0.3s;
cursor: pointer;
position: relative;
}
.breath-circle.active {
animation: breathe var(--cycle-duration, 8s) ease-in-out infinite;
}
.breath-circle .ring {
position: absolute; inset: -8px;
border-radius: 50%;
border: 1px solid transparent;
transition: border-color 0.5s;
}
.breath-circle.active .ring {
border-color: var(--accent-soft);
animation: ringPulse var(--cycle-duration, 8s) ease-in-out infinite;
}
@keyframes breathe {
0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 var(--accent-soft); }
25% { transform: scale(1.35); box-shadow: 0 0 40px 10px var(--accent-soft); }
50% { transform: scale(1.35); box-shadow: 0 0 40px 10px var(--accent-soft); }
75% { transform: scale(1); box-shadow: 0 0 0 0 var(--accent-soft); }
}
@keyframes ringPulse {
0%, 100% { transform: scale(1); opacity: 0.3; }
25% { transform: scale(1.3); opacity: 0.6; }
50% { transform: scale(1.3); opacity: 0.6; }
75% { transform: scale(1); opacity: 0.3; }
}
.breath-label {
font-size: 0.85rem;
color: var(--text-faint);
letter-spacing: 0.15em;
font-weight: 300;
min-height: 1.4em;
transition: opacity 0.5s;
}
.breath-label.inhale { color: var(--accent); }
.breath-timer-select {
display: flex; gap: 8px; justify-content: center;
margin-top: 20px;
}
.breath-timer-select button {
font-family: 'Inter', sans-serif;
font-size: 0.72rem;
font-weight: 400;
color: var(--text-faint);
background: none;
border: 1px solid var(--line);
border-radius: 100px;
padding: 5px 14px;
cursor: pointer;
transition: all 0.2s;
letter-spacing: 0.04em;
}
.breath-timer-select button:hover { color: var(--text-soft); border-color: var(--accent); }
.breath-timer-select button.active { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.breath-countdown {
font-family: 'Inter', sans-serif;
font-size: 0.72rem;
color: var(--text-faint);
margin-top: 12px;
font-weight: 300;
min-height: 1.2em;
}
.dots {
display: flex;
justify-content: center;
gap: 12px;
margin: 48px 0;
}
.dots span {
width: 4px;
height: 4px;
border-radius: 50%;
background: var(--accent);
opacity: 0.4;
animation: pulse 3s ease-in-out infinite;
}
.dots span:nth-child(2) { animation-delay: 1s; }
.dots span:nth-child(3) { animation-delay: 2s; }
@keyframes pulse {
0%, 100% { opacity: 0.2; }
50% { opacity: 0.7; }
}
footer {
margin-top: 80px;
padding-top: 24px;
border-top: 1px solid var(--line);
text-align: center;
}
footer p {
font-family: 'Inter', sans-serif;
font-size: 0.72rem;
color: var(--text-faint);
font-weight: 300;
letter-spacing: 0.04em;
}
/* Fade in */
@keyframes fadeUp {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
.page > * {
animation: fadeUp 1s ease forwards;
opacity: 0;
}
.page > *:nth-child(1) { animation-delay: 0.2s; }
.page > *:nth-child(2) { animation-delay: 0.5s; }
.page > *:nth-child(3) { animation-delay: 0.8s; }
.page > *:nth-child(4) { animation-delay: 1.1s; }
.page > *:nth-child(5) { animation-delay: 1.4s; }
.page > *:nth-child(6) { animation-delay: 1.7s; }
.page > *:nth-child(7) { animation-delay: 2.0s; }
.page > *:nth-child(8) { animation-delay: 2.3s; }
.page > *:nth-child(9) { animation-delay: 2.6s; }
@media (max-width: 540px) {
.page { padding: 80px 20px 60px; }
.highlight { padding: 20px 24px; }
}
</style>
</head>
<body>
<div class="page">
<div class="icon">🫖</div>
<h1 data-de="Warte bitte." data-en="Please wait.">Warte bitte.</h1>
<div class="rule"></div>
<div class="prose">
<p data-de="Ich weiss, du wartest. Und Warten fuehlt sich manchmal an wie
Stillstand. Wie verlorene Zeit. Wie etwas, das nicht sein muesste." data-en="I know you're waiting. And waiting sometimes feels like
standing still. Like lost time. Like something that shouldn't have to be.">
Ich weiss, du wartest. Und Warten fuehlt sich manchmal an wie
Stillstand. Wie verlorene Zeit. Wie etwas, das nicht sein muesste.
</p>
<p data-de="Aber manche Dinge brauchen genau diese Zeit. Nicht weil jemand
troedelt, sondern weil <em>gute Ergebnisse nicht gehetzt werden koennen</em>.
Ein Gedanke muss reifen. Eine Loesung muss stimmen. Nicht nur schnell,
sondern richtig." data-en="But some things need exactly this time. Not because someone
is dawdling, but because <em>good results cannot be rushed</em>.
A thought must ripen. A solution must be right. Not just fast,
but correct.">
Aber manche Dinge brauchen genau diese Zeit. Nicht weil jemand
troedelt, sondern weil <em>gute Ergebnisse nicht gehetzt werden koennen</em>.
Ein Gedanke muss reifen. Eine Loesung muss stimmen. Nicht nur schnell,
sondern richtig.
</p>
</div>
<div class="dots"><span></span><span></span><span></span></div>
<div class="highlight">
<p data-de="Zeit ist unser kostbarstes Gut. Wer sie gibt, gibt das Wertvollste,
was er hat. Und wer sie bekommt, sollte wissen: Da arbeitet jemand
daran, dass es sich lohnt." data-en="Time is our most precious asset. Whoever gives it, gives the most valuable
thing they have. And whoever receives it should know: someone is working
to make it worthwhile.">
Zeit ist unser kostbarstes Gut. Wer sie gibt, gibt das Wertvollste,
was er hat. Und wer sie bekommt, sollte wissen: Da arbeitet jemand
daran, dass es sich lohnt.
</p>
</div>
<div class="prose">
<p data-de="Es ist okay, ungeduldig zu sein. Ungeduld zeigt, dass dir etwas
wichtig ist. Aber lass sie nicht den Blick trueben fuer das,
was gerade im Hintergrund passiert." data-en="It's okay to be impatient. Impatience shows that something matters to you.
But don't let it cloud your view of what's happening in the background right now.">
Es ist okay, ungeduldig zu sein. Ungeduld zeigt, dass dir etwas
wichtig ist. Aber lass sie nicht den Blick trueben fuer das,
was gerade im Hintergrund passiert.
</p>
<p data-de="Gute Dinge brauchen Raum. Einen ruhigen Moment.
Einen zweiten Blick. Manchmal einen dritten." data-en="Good things need space. A quiet moment.
A second look. Sometimes a third.">
Gute Dinge brauchen Raum. Einen ruhigen Moment.
Einen zweiten Blick. Manchmal einen dritten.
</p>
</div>
<div class="breath-container">
<div class="breath-circle" id="breathCircle" onclick="toggleBreathing()">
<div class="ring"></div>
<span class="breath-label" id="breathLabel">start</span>
</div>
<div class="breath-timer-select">
<button onclick="setTimer(1)" id="t1" data-de="1 min" data-en="1 min">1 min</button>
<button onclick="setTimer(2)" id="t2" class="active" data-de="2 min" data-en="2 min">2 min</button>
<button onclick="setTimer(5)" id="t5" data-de="5 min" data-en="5 min">5 min</button>
<button onclick="setTimer(0)" id="t0" data-de="endlos" data-en="endless">endlos</button>
</div>
<div class="breath-countdown" id="countdown"></div>
</div>
<script>
let breathing = false;
let timer = null;
let duration = 120;
let remaining = 0;
let phaseTimer = null;
const cycle = 8000;
const phases = [
{ label: 'einatmen', duration: 2000, cls: 'inhale' },
{ label: 'halten', duration: 2000, cls: '' },
{ label: 'ausatmen', duration: 2000, cls: '' },
{ label: 'ruhe', duration: 2000, cls: '' },
];
function setTimer(min) {
duration = min * 60;
document.querySelectorAll('.breath-timer-select button').forEach(b => b.classList.remove('active'));
document.getElementById('t' + min).classList.add('active');
if (breathing) { stopBreathing(); startBreathing(); }
}
function toggleBreathing() {
if (breathing) stopBreathing(); else startBreathing();
}
function startBreathing() {
breathing = true;
remaining = duration;
const circle = document.getElementById('breathCircle');
circle.style.setProperty('--cycle-duration', cycle + 'ms');
circle.classList.add('active');
runPhases();
if (duration > 0) {
updateCountdown();
timer = setInterval(() => {
remaining--;
updateCountdown();
if (remaining <= 0) stopBreathing();
}, 1000);
}
}
function stopBreathing() {
breathing = false;
document.getElementById('breathCircle').classList.remove('active');
document.getElementById('breathLabel').textContent = 'start';
document.getElementById('breathLabel').className = 'breath-label';
document.getElementById('countdown').textContent = '';
clearInterval(timer);
clearTimeout(phaseTimer);
}
function runPhases() {
let i = 0;
function next() {
if (!breathing) return;
const p = phases[i % phases.length];
const label = document.getElementById('breathLabel');
label.textContent = p.label;
label.className = 'breath-label' + (p.cls ? ' ' + p.cls : '');
i++;
phaseTimer = setTimeout(next, p.duration);
}
next();
}
function updateCountdown() {
if (duration === 0) { document.getElementById('countdown').textContent = ''; return; }
const m = Math.floor(remaining / 60);
const s = remaining % 60;
document.getElementById('countdown').textContent = m + ':' + String(s).padStart(2, '0');
}
</script>
<div class="closing">
<p data-de="Du bekommst, was du brauchst. Nur nicht genau jetzt.
Und das ist voellig in Ordnung." data-en="You'll get what you need. Just not right now.
And that's perfectly fine.">
Du bekommst, was du brauchst. Nur nicht genau jetzt.
Und das ist voellig in Ordnung.
</p>
</div>
<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/i18n.js"></script>
</body>
</html>