feat: initial mono-repo with 30 vanity domain sites
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
This commit is contained in:
423
sites/wartebitte.de/index.html
Normal file
423
sites/wartebitte.de/index.html
Normal file
@@ -0,0 +1,423 @@
|
||||
<!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>
|
||||
4
sites/wartebitte.de/site.yaml
Normal file
4
sites/wartebitte.de/site.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
domain: wartebitte.de
|
||||
template: custom
|
||||
title: "Warte bitte."
|
||||
description: ""
|
||||
Reference in New Issue
Block a user