Scaffold complete repo structure: - 28 static sites extracted from running containers on mlake - 2 dynamic sites (dasbes.de, dumusst.com) marked for separate handling - Template system with 6 templates (person-dark/light, product-dark, editorial, fun, minimal) - Shared CSS (variables, responsive, animations, noise overlay) - nginx config generator with multi-domain alias support - Build script with Docker-based nginx validation - add-site.sh helper for scaffolding new domains - Dockerfile for single nginx:alpine container Sites: clemensplassmann.de, danosi.de, deinesei.de, derkaiseristnackt.de, elefantenhor.de, fragina.de, frenchkis.de, ichbinaufbali.de, ichbinaufbarley.de, insain.de, julietensity.de, kainco.de (+keinco.de), kainstress.de, keinefreun.de, knzlmgmt.de, kopffrai.de, legalais.de, machesdocheinfach.de, mai-otto.de (+otto.flexsiebels.de, ottomatisch.de, ichbinotto.de), martinsiebels.de, matthiasbreier.de, osterai.de, paragraphenraiter.de, schulfrai.de, smartin3.de, sorgenfrai.de, vonschraitter.de, wartebitte.de Refs: otto#341
424 lines
11 KiB
HTML
424 lines
11 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>Warte bitte.</h1>
|
|
|
|
<div class="rule"></div>
|
|
|
|
<div class="prose">
|
|
<p>
|
|
Ich weiss, du wartest. Und Warten fuehlt sich manchmal an wie
|
|
Stillstand. Wie verlorene Zeit. Wie etwas, das nicht sein muesste.
|
|
</p>
|
|
<p>
|
|
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>
|
|
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>
|
|
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>
|
|
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">1 min</button>
|
|
<button onclick="setTimer(2)" id="t2" class="active">2 min</button>
|
|
<button onclick="setTimer(5)" id="t5">5 min</button>
|
|
<button onclick="setTimer(0)" id="t0">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>
|
|
Du bekommst, was du brauchst. Nur nicht genau jetzt.
|
|
Und das ist voellig in Ordnung.
|
|
</p>
|
|
</div>
|
|
|
|
<footer>
|
|
<p>wartebitte.de</p>
|
|
</footer>
|
|
|
|
</div>
|
|
|
|
</body>
|
|
</html>
|