Files
oikos/docs/index.html
T
Ulas Kalayci 82f1a76a5d Update GitHub Page design and screenshots
- Implement modern design with DM Serif Display + DM Sans fonts
- Switch accent color to purple/violet (#6C3AED / #A78BFA)
- Add hero section with desktop + mobile mockups
- Add feature showcase with alternating layouts
- Add screenshot carousel
- Update all screenshots to new versions
- Maintain dark/light mode and EN/DE language support

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-05 12:36:20 +02:00

721 lines
44 KiB
HTML
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Oikos — The Self-Hosted Family Planner</title>
<meta name="description" content="A privacy-first, self-hosted family planner with tasks, shopping lists, meal planning, calendar sync, budget tracking, and more.">
<meta property="og:type" content="website">
<meta property="og:title" content="Oikos — The Self-Hosted Family Planner">
<meta property="og:description" content="Tasks, shopping, meals, calendar, budget — all self-hosted, no cloud dependency.">
<meta property="og:image" content="https://ulsklyc.github.io/oikos/og-image.png">
<meta property="og:url" content="https://ulsklyc.github.io/oikos/">
<meta name="twitter:card" content="summary_large_image">
<link rel="icon" type="image/svg+xml" href="logo.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,300..800;1,9..40,300..800&family=DM+Serif+Display&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--ff-display: 'DM Serif Display', Georgia, serif;
--ff-body: 'DM Sans', system-ui, -apple-system, sans-serif;
--bg: #FAFAFB;
--bg-alt: #F2F1F6;
--surface: #FFFFFF;
--border: #E5E4EA;
--text-1: #181620;
--text-2: #5E5C6B;
--text-3: #8F8D9A;
--accent: #6C3AED;
--accent-hover: #5B21B6;
--accent-soft: #EDE9FE;
--accent-glow: rgba(108, 58, 237, 0.12);
--blue: #0A84FF;
--code-bg: #1C1B22;
--code-text: #E2E1EC;
--radius: 16px;
--shadow-card: 0 1px 3px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.03);
--shadow-lg: 0 8px 32px rgba(0,0,0,0.08);
--shadow-hero: 0 20px 60px rgba(0,0,0,0.12), 0 4px 16px rgba(0,0,0,0.06);
}
[data-theme="dark"] {
--bg: #111015;
--bg-alt: #1A1920;
--surface: #222130;
--border: #33323E;
--text-1: #F0EFF5;
--text-2: #A09FB0;
--text-3: #706F80;
--accent: #A78BFA;
--accent-hover: #8B5CF6;
--accent-soft: #2D2250;
--accent-glow: rgba(167, 139, 250, 0.15);
--code-bg: #0D0C12;
--code-text: #C8C7D3;
--shadow-card: 0 1px 3px rgba(0,0,0,0.2);
--shadow-lg: 0 8px 32px rgba(0,0,0,0.35);
--shadow-hero: 0 20px 60px rgba(0,0,0,0.4);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #111015; --bg-alt: #1A1920; --surface: #222130; --border: #33323E;
--text-1: #F0EFF5; --text-2: #A09FB0; --text-3: #706F80;
--accent: #A78BFA; --accent-hover: #8B5CF6; --accent-soft: #2D2250;
--accent-glow: rgba(167, 139, 250, 0.15);
--code-bg: #0D0C12; --code-text: #C8C7D3;
--shadow-card: 0 1px 3px rgba(0,0,0,0.2); --shadow-lg: 0 8px 32px rgba(0,0,0,0.35);
--shadow-hero: 0 20px 60px rgba(0,0,0,0.4);
}
}
html { scroll-behavior: smooth; }
body {
font-family: var(--ff-body);
background: var(--bg);
color: var(--text-1);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
}
a { color: var(--accent); text-decoration: none; }
img { max-width: 100%; height: auto; display: block; }
.wrap { max-width: 1200px; margin: 0 auto; padding: 0 24px; }
/* ===== NAV ===== */
nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
background: color-mix(in srgb, var(--bg) 80%, transparent);
backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
border-bottom: 1px solid var(--border);
height: 60px;
}
nav .wrap { display: flex; align-items: center; justify-content: space-between; height: 100%; }
.nav-logo { display: flex; align-items: center; gap: 10px; font-family: var(--ff-display); font-size: 1.3rem; color: var(--text-1); }
.nav-logo svg { width: 32px; height: 32px; }
.nav-right { display: flex; align-items: center; gap: 8px; }
.nav-btn {
background: none; border: 1px solid var(--border); color: var(--text-2);
cursor: pointer; padding: 7px 12px; border-radius: 10px; font-size: 0.8125rem;
font-family: var(--ff-body); display: flex; align-items: center; gap: 5px;
transition: all 0.15s ease;
}
.nav-btn:hover { background: var(--surface); color: var(--text-1); border-color: var(--text-3); }
.nav-btn svg { width: 16px; height: 16px; }
.nav-gh {
background: var(--text-1); color: var(--bg); border-color: var(--text-1); font-weight: 600;
}
.nav-gh:hover { opacity: 0.85; color: var(--bg); }
/* ===== HERO ===== */
.hero {
padding: 140px 0 100px;
text-align: center;
position: relative;
}
.hero::before {
content: ''; position: absolute; top: -200px; left: 50%; transform: translateX(-50%);
width: 900px; height: 900px;
background: radial-gradient(ellipse at center, var(--accent-glow) 0%, transparent 65%);
pointer-events: none;
}
.hero > * { position: relative; }
.hero-badge {
display: inline-flex; align-items: center; gap: 6px;
padding: 6px 16px; border-radius: 999px; font-size: 0.8125rem; font-weight: 500;
background: var(--accent-soft); color: var(--accent); margin-bottom: 24px;
}
.hero h1 {
font-family: var(--ff-display); font-size: clamp(2.8rem, 7vw, 4.5rem);
font-weight: 400; line-height: 1.08; letter-spacing: -0.02em; margin-bottom: 20px;
}
.hero h1 em { font-style: italic; color: var(--accent); }
.hero-sub {
font-size: clamp(1.05rem, 2.2vw, 1.25rem); color: var(--text-2);
max-width: 540px; margin: 0 auto 36px; line-height: 1.7;
}
.hero-actions { display: flex; justify-content: center; gap: 12px; flex-wrap: wrap; }
.btn-primary {
display: inline-flex; align-items: center; gap: 8px;
background: var(--accent); color: #fff; padding: 14px 28px;
border-radius: 12px; font-weight: 600; font-size: 1rem;
transition: all 0.2s ease; box-shadow: 0 2px 8px var(--accent-glow);
}
.btn-primary:hover { background: var(--accent-hover); transform: translateY(-1px); box-shadow: 0 4px 16px var(--accent-glow); }
.btn-secondary {
display: inline-flex; align-items: center; gap: 8px;
background: var(--surface); color: var(--text-1); padding: 14px 28px;
border-radius: 12px; font-weight: 600; font-size: 1rem; border: 1px solid var(--border);
transition: all 0.2s ease;
}
.btn-secondary:hover { border-color: var(--text-3); transform: translateY(-1px); }
.btn-primary svg, .btn-secondary svg { width: 18px; height: 18px; }
.hero-tags {
display: flex; justify-content: center; gap: 8px; margin-top: 20px; flex-wrap: wrap;
}
.tag {
padding: 4px 12px; border-radius: 999px; font-size: 0.75rem; font-weight: 500;
background: var(--surface); color: var(--text-3); border: 1px solid var(--border);
}
/* ===== HERO MOCKUP ===== */
.hero-mockup {
margin-top: 72px; position: relative; display: flex; justify-content: center; align-items: flex-end;
gap: 32px; perspective: 1200px;
}
.mockup-desktop {
width: 720px; border-radius: 12px; overflow: hidden;
box-shadow: var(--shadow-hero); border: 1px solid var(--border);
transform: rotateY(-2deg) rotateX(2deg);
transition: transform 0.4s ease;
}
.mockup-desktop:hover { transform: rotateY(0) rotateX(0); }
.mockup-desktop img { width: 100%; display: block; }
.mockup-phone {
width: 200px; border-radius: 24px; overflow: hidden;
box-shadow: var(--shadow-hero); border: 4px solid var(--text-1);
position: relative; z-index: 2;
transform: rotateY(3deg) rotateX(1deg) translateY(-20px);
transition: transform 0.4s ease;
}
.mockup-phone:hover { transform: rotateY(0) rotateX(0) translateY(-20px); }
.mockup-phone img { width: 100%; display: block; }
/* ===== FEATURES SHOWCASE ===== */
.showcase { padding: 100px 0; }
.showcase-header { text-align: center; margin-bottom: 64px; }
.section-label {
font-size: 0.8125rem; font-weight: 600; text-transform: uppercase;
letter-spacing: 0.06em; color: var(--accent); margin-bottom: 8px;
}
.section-title {
font-family: var(--ff-display); font-size: clamp(1.8rem, 4vw, 2.8rem);
font-weight: 400; line-height: 1.15; margin-bottom: 12px;
}
.section-desc { font-size: 1.0625rem; color: var(--text-2); max-width: 520px; margin: 0 auto; line-height: 1.7; }
/* Feature rows */
.feat-row {
display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: center;
margin-bottom: 80px;
}
.feat-row.reverse .feat-visual { order: -1; }
.feat-info { max-width: 460px; }
.feat-info h3 {
font-family: var(--ff-display); font-size: 1.6rem; font-weight: 400; margin-bottom: 12px;
}
.feat-info p { color: var(--text-2); font-size: 0.9375rem; line-height: 1.7; }
.feat-visual { position: relative; }
.feat-visual img {
border-radius: var(--radius); box-shadow: var(--shadow-lg);
border: 1px solid var(--border);
}
.feat-mobile-img {
width: 160px !important; border-radius: 20px !important;
border: 3px solid var(--text-1) !important;
position: absolute; bottom: -24px; right: -24px; z-index: 2;
}
/* ===== FEATURE GRID ===== */
.features-alt { background: var(--bg-alt); padding: 100px 0; }
.feat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
.feat-card {
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
padding: 28px; transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.feat-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.feat-card-icon {
width: 44px; height: 44px; border-radius: 12px; display: flex; align-items: center;
justify-content: center; margin-bottom: 16px; background: var(--accent-soft); color: var(--accent);
}
.feat-card-icon svg { width: 22px; height: 22px; }
.feat-card h3 { font-size: 1rem; font-weight: 600; margin-bottom: 6px; }
.feat-card p { font-size: 0.8375rem; color: var(--text-2); line-height: 1.6; }
/* ===== PHILOSOPHY ===== */
.philosophy { padding: 100px 0; }
.phil-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; }
.phil-card {
display: flex; gap: 16px; padding: 28px; border-radius: var(--radius);
background: var(--surface); border: 1px solid var(--border);
}
.phil-icon {
flex-shrink: 0; width: 44px; height: 44px; border-radius: 12px;
display: flex; align-items: center; justify-content: center;
background: var(--accent-soft); color: var(--accent);
}
.phil-icon svg { width: 20px; height: 20px; }
.phil-card h3 { font-size: 0.9375rem; font-weight: 600; margin-bottom: 4px; }
.phil-card p { font-size: 0.8375rem; color: var(--text-2); line-height: 1.6; }
/* ===== SETUP ===== */
.setup { background: var(--bg-alt); padding: 100px 0; }
.setup-inner { max-width: 680px; }
.code-block {
background: var(--code-bg); color: var(--code-text); border-radius: var(--radius);
padding: 24px 28px; font-family: 'SF Mono', 'Fira Code', monospace;
font-size: 0.8125rem; line-height: 2; overflow-x: auto; margin: 24px 0;
}
.code-block .c { color: #706F80; }
.code-block .h { color: var(--accent); }
.setup-note { font-size: 0.875rem; color: var(--text-2); line-height: 1.7; }
.setup-note code { background: var(--surface); padding: 2px 6px; border-radius: 5px; font-size: 0.8125rem; border: 1px solid var(--border); }
/* ===== SCREENSHOT CAROUSEL ===== */
.carousel-section { padding: 100px 0; overflow: hidden; }
.carousel-track {
display: flex; gap: 20px; overflow-x: auto; padding: 8px 24px 24px;
scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-item {
flex: 0 0 auto; scroll-snap-align: center; text-align: center;
}
.carousel-item img {
height: 440px; width: auto; border-radius: 20px;
box-shadow: var(--shadow-lg); border: 3px solid var(--text-1);
}
.carousel-item span {
display: block; margin-top: 12px; font-size: 0.8125rem;
font-weight: 500; color: var(--text-3);
}
/* ===== CTA ===== */
.cta-section {
padding: 100px 0; text-align: center;
}
.cta-box {
background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
border-radius: 24px; padding: 64px 40px; max-width: 720px; margin: 0 auto;
}
.cta-box h2 { font-family: var(--ff-display); font-size: clamp(1.5rem, 3.5vw, 2.2rem); margin-bottom: 12px; }
.cta-box p { color: var(--text-2); margin-bottom: 28px; max-width: 420px; margin-left: auto; margin-right: auto; }
/* ===== FOOTER ===== */
footer {
border-top: 1px solid var(--border); padding: 40px 0; text-align: center;
}
footer p { font-size: 0.875rem; color: var(--text-2); margin-bottom: 12px; }
.footer-links { display: flex; justify-content: center; gap: 24px; font-size: 0.8125rem; }
.footer-links a { color: var(--text-3); }
.footer-links a:hover { color: var(--accent); }
/* ===== ANIMATIONS ===== */
.reveal { opacity: 0; transform: translateY(24px); transition: opacity 0.6s ease, transform 0.6s ease; }
.reveal.vis { opacity: 1; transform: none; }
.reveal-d1 { transition-delay: 0.1s; }
.reveal-d2 { transition-delay: 0.2s; }
.reveal-d3 { transition-delay: 0.3s; }
/* ===== RESPONSIVE ===== */
@media (max-width: 900px) {
.feat-row { grid-template-columns: 1fr; gap: 32px; }
.feat-row.reverse .feat-visual { order: 0; }
.feat-mobile-img { width: 120px !important; bottom: -16px; right: -8px; }
.hero-mockup { flex-direction: column; align-items: center; gap: 24px; }
.mockup-desktop { width: 100%; max-width: 560px; transform: none; }
.mockup-phone { width: 160px; transform: none; position: absolute; bottom: -30px; right: 10%; }
.phil-grid { grid-template-columns: 1fr; }
}
@media (max-width: 600px) {
.hero { padding: 110px 0 60px; }
.hero-mockup { margin-top: 48px; }
.mockup-phone { width: 130px; right: 4%; }
.carousel-item img { height: 360px; }
.feat-grid { grid-template-columns: 1fr; }
.cta-box { padding: 40px 24px; }
.nav-gh span { display: none; }
}
</style>
</head>
<body>
<nav>
<div class="wrap">
<a href="#" class="nav-logo">
<svg viewBox="0 0 160 160" fill="none"><defs><linearGradient id="g" x1="0" y1="0" x2="160" y2="160" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#8B5CF6"></stop><stop offset="100%" stop-color="#6C3AED"></stop></linearGradient></defs><rect width="160" height="160" rx="36" fill="url(#g)"></rect><path d="M80 36L36 72V120C36 122.2 37.8 124 40 124H68V96H92V124H120C122.2 124 124 122.2 124 120V72L80 36Z" fill="white"></path><rect x="100" y="46" width="12" height="22" rx="2" fill="white"></rect></svg>
Oikos
</a>
<div class="nav-right">
<button class="nav-btn" id="langBtn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M2 12h20"></path><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10A15.3 15.3 0 0 1 12 2z"></path></svg><span id="langLbl">DE</span></button>
<button class="nav-btn" id="themeBtn" type="button"><svg id="sunIco" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: none;"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg><svg id="moonIco" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block; width: 16px; height: 16px;"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></button>
<a href="https://github.com/ulsklyc/oikos" class="nav-btn nav-gh" target="_blank" rel="noopener"><svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"></path></svg><span data-t="nav_gh">GitHub</span></a>
</div>
</div>
</nav>
<!-- HERO -->
<header class="hero">
<div class="wrap">
<div class="hero-badge" data-t="hero_badge">Open Source · Self-Hosted · Private</div>
<h1><span data-t="hero_h1_pre">Your household,</span><br><em data-t="hero_h1_em">organized together.</em></h1>
<p class="hero-sub" data-t="hero_sub">Tasks, shopping, meals, calendar, budget — everything your family needs in one place. Self-hosted on your own server. No cloud, no tracking, no subscriptions.</p>
<div class="hero-actions">
<a href="https://github.com/ulsklyc/oikos" class="btn-primary" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"></path></svg>
<span data-t="hero_cta">View on GitHub</span>
</a>
<a href="install.html" class="btn-secondary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
<span data-t="hero_install">Installation Guide</span>
</a>
</div>
<div class="hero-tags">
<span class="tag">MIT License</span>
<span class="tag">Docker</span>
<span class="tag">PWA</span>
<span class="tag" data-t="tag_nocloud">No Cloud Required</span>
</div>
<div class="hero-mockup">
<div class="mockup-desktop">
<img class="sc" data-light="screenshots/01-web-light.png" data-dark="screenshots/01-web-dark.png" src="screenshots/01-web-light.png" alt="Oikos Dashboard Desktop">
</div>
<div class="mockup-phone">
<img class="sc" data-light="screenshots/01-mobile-light.png" data-dark="screenshots/01-mobile-dark.png" src="screenshots/01-mobile-light.png" alt="Oikos Dashboard Mobile">
</div>
</div>
</div>
</header>
<!-- FEATURE SHOWCASE -->
<section class="showcase">
<div class="wrap">
<div class="showcase-header reveal vis">
<p class="section-label" data-t="feat_label">Features</p>
<h2 class="section-title" data-t="feat_title">Everything your household needs</h2>
<p class="section-desc" data-t="feat_desc">A complete set of tools designed for families — seamlessly working together.</p>
</div>
<div class="feat-row reveal vis">
<div class="feat-info">
<h3 data-t="f_tasks_t">Task Management</h3>
<p data-t="f_tasks_d">Shared tasks with deadlines, priorities, subtasks, recurring schedules, and a Kanban board. Assign to family members with one-tap status changes.</p>
</div>
<div class="feat-visual">
<img class="sc" data-light="screenshots/02-web-light.png" data-dark="screenshots/02-web-dark.png" src="screenshots/02-web-light.png" alt="Tasks">
<img class="sc feat-mobile-img" data-light="screenshots/02-mobile-light.png" data-dark="screenshots/02-mobile-dark.png" src="screenshots/02-mobile-light.png" alt="Tasks Mobile">
</div>
</div>
<div class="feat-row reverse reveal vis">
<div class="feat-info">
<h3 data-t="f_meals_t">Meal Planning</h3>
<p data-t="f_meals_d">Weekly drag-and-drop planner with ingredient lists. Automatically export ingredients to your shopping list with one click.</p>
</div>
<div class="feat-visual">
<img class="sc" data-light="screenshots/04-web-light.png" data-dark="screenshots/04-web-dark.png" src="screenshots/04-web-light.png" alt="Meals">
<img class="sc feat-mobile-img" data-light="screenshots/04-mobile-light.png" data-dark="screenshots/04-mobile-dark.png" src="screenshots/04-mobile-light.png" alt="Meals Mobile" style="left:-24px;right:auto">
</div>
</div>
<div class="feat-row reveal vis">
<div class="feat-info">
<h3 data-t="f_cal_t">Calendar Sync</h3>
<p data-t="f_cal_d">Two-way sync with Google Calendar and Apple iCloud. See all family events in one unified view — month, week, day, or agenda.</p>
</div>
<div class="feat-visual">
<img class="sc" data-light="screenshots/05-web-light.png" data-dark="screenshots/05-web-dark.png" src="screenshots/05-web-light.png" alt="Calendar">
<img class="sc feat-mobile-img" data-light="screenshots/03-mobile-light.png" data-dark="screenshots/03-mobile-dark.png" src="screenshots/03-mobile-light.png" alt="Calendar Mobile">
</div>
</div>
</div>
</section>
<!-- MORE FEATURES GRID -->
<section class="features-alt">
<div class="wrap">
<div class="showcase-header reveal vis">
<p class="section-label" data-t="more_label">And more</p>
<h2 class="section-title" data-t="more_title">Built for real family life</h2>
</div>
<div class="feat-grid">
<div class="feat-card reveal vis">
<div class="feat-card-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="21" r="1"></circle><circle cx="20" cy="21" r="1"></circle><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path></svg></div>
<h3 data-t="f_shop_t">Shopping Lists</h3>
<p data-t="f_shop_d">Collaborative lists with aisle categories. Import ingredients directly from your meal plans.</p>
</div>
<div class="feat-card reveal reveal-d1 vis">
<div class="feat-card-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="1" x2="12" y2="23"></line><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path></svg></div>
<h3 data-t="f_budget_t">Budget Tracking</h3>
<p data-t="f_budget_d">Track income and expenses with 13 currency options, recurring entries, monthly trends, and CSV export.</p>
</div>
<div class="feat-card reveal reveal-d2 vis">
<div class="feat-card-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg></div>
<h3 data-t="f_notes_t">Notes</h3>
<p data-t="f_notes_d">Colored sticky notes with Markdown. Perfect for recipes, family memos, and quick reminders.</p>
</div>
<div class="feat-card reveal reveal-d3 vis">
<div class="feat-card-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg></div>
<h3 data-t="f_contacts_t">Contacts</h3>
<p data-t="f_contacts_d">Shared family contact directory with vCard import and export.</p>
</div>
<div class="feat-card reveal vis">
<div class="feat-card-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg></div>
<h3 data-t="f_pwa_t">Works Everywhere</h3>
<p data-t="f_pwa_d">Installable PWA on any device. Offline support, dark mode, fully responsive from phone to desktop.</p>
</div>
<div class="feat-card reveal reveal-d1 vis">
<div class="feat-card-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg></div>
<h3 data-t="f_board_t">Kanban Board</h3>
<p data-t="f_board_d">Visual board view for tasks and projects. Drag cards between columns to track progress.</p>
</div>
</div>
</div>
</section>
<!-- ALL SCREENS CAROUSEL -->
<section class="carousel-section">
<div class="wrap">
<div class="showcase-header reveal vis">
<p class="section-label" data-t="screens_label">Preview</p>
<h2 class="section-title" data-t="screens_title">Every screen, beautifully crafted</h2>
<p class="section-desc" data-t="screens_desc">A clean, intuitive interface that adapts to your device and preferred theme.</p>
</div>
</div>
<div class="carousel-track">
<div class="carousel-item"><img class="sc" data-light="screenshots/01-mobile-light.png" data-dark="screenshots/01-mobile-dark.png" src="screenshots/01-mobile-light.png" alt="Dashboard"><span>Dashboard</span></div>
<div class="carousel-item"><img class="sc" data-light="screenshots/02-mobile-light.png" data-dark="screenshots/02-mobile-dark.png" src="screenshots/02-mobile-light.png" alt="Tasks"><span data-t="f_tasks_t2">Tasks</span></div>
<div class="carousel-item"><img class="sc" data-light="screenshots/03-mobile-light.png" data-dark="screenshots/03-mobile-dark.png" src="screenshots/03-mobile-light.png" alt="Calendar"><span data-t="f_cal_t2">Calendar</span></div>
<div class="carousel-item"><img class="sc" data-light="screenshots/04-mobile-light.png" data-dark="screenshots/04-mobile-dark.png" src="screenshots/04-mobile-light.png" alt="Shopping"><span data-t="f_shop_t2">Shopping</span></div>
<div class="carousel-item"><img class="sc" data-light="screenshots/05-mobile-light.png" data-dark="screenshots/05-mobile-dark.png" src="screenshots/05-mobile-light.png" alt="Meals"><span data-t="f_meals_t2">Meals</span></div>
<div class="carousel-item"><img class="sc" data-light="screenshots/06-mobile-light.png" data-dark="screenshots/06-mobile-dark.png" src="screenshots/06-mobile-light.png" alt="Budget"><span data-t="f_budget_t2">Budget</span></div>
<div class="carousel-item"><img class="sc" data-light="screenshots/07-mobile-light.png" data-dark="screenshots/07-mobile-dark.png" src="screenshots/07-mobile-light.png" alt="Notes"><span data-t="f_notes_t2">Notes</span></div>
<div class="carousel-item"><img class="sc" data-light="screenshots/08-mobile-light.png" data-dark="screenshots/08-mobile-dark.png" src="screenshots/08-mobile-light.png" alt="Contacts"><span data-t="f_contacts_t2">Contacts</span></div>
<div class="carousel-item"><img class="sc" data-light="screenshots/09-mobile-light.png" data-dark="screenshots/09-mobile-dark.png" src="screenshots/09-mobile-light.png" alt="Settings"><span data-t="f_settings_t">Settings</span></div>
</div>
</section>
<!-- PHILOSOPHY -->
<section class="philosophy">
<div class="wrap">
<div class="showcase-header reveal vis">
<p class="section-label" data-t="phil_label">Philosophy</p>
<h2 class="section-title" data-t="phil_title">Built different, on purpose</h2>
<p class="section-desc" data-t="phil_desc">No subscriptions, no vendor lock-in, no data leaving your home.</p>
</div>
<div class="phil-grid">
<div class="phil-card reveal vis">
<div class="phil-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg></div>
<div>
<h3 data-t="p_priv_t">Privacy First</h3>
<p data-t="p_priv_d">AES-256 encrypted database with SQLCipher. Zero telemetry. Your data never leaves your server.</p>
</div>
</div>
<div class="phil-card reveal reveal-d1 vis">
<div class="phil-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect><rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect><line x1="6" y1="6" x2="6.01" y2="6"></line><line x1="6" y1="18" x2="6.01" y2="18"></line></svg></div>
<div>
<h3 data-t="p_self_t">Fully Self-Hosted</h3>
<p data-t="p_self_d">Runs on a Raspberry Pi, a NAS, or any server. Docker makes setup a one-liner.</p>
</div>
</div>
<div class="phil-card reveal reveal-d2 vis">
<div class="phil-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"></path></svg></div>
<div>
<h3 data-t="p_build_t">Zero Build Step</h3>
<p data-t="p_build_d">Pure ES modules, vanilla JS, plain CSS. No bundler, no transpiler, no framework. Ships what you write.</p>
</div>
</div>
<div class="phil-card reveal reveal-d3 vis">
<div class="phil-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"></path><line x1="4" y1="22" x2="4" y2="15"></line></svg></div>
<div>
<h3 data-t="p_open_t">Open Source</h3>
<p data-t="p_open_d">MIT licensed. Inspect, modify, extend, contribute. Built in the open for families who care about transparency.</p>
</div>
</div>
</div>
</div>
</section>
<!-- SETUP -->
<section class="setup" id="setup">
<div class="wrap">
<div class="section-label reveal vis" data-t="setup_label">Get Started</div>
<h2 class="section-title reveal vis" data-t="setup_title">Up and running in minutes</h2>
<div class="setup-inner reveal vis">
<div class="code-block"><span class="c"># Pull and start with Docker</span>
curl -O https://raw.githubusercontent.com/ulsklyc/oikos/main/docker-compose.yml
curl -O https://raw.githubusercontent.com/ulsklyc/oikos/main/.env.example
cp .env.example .env
<span class="c"># Set SESSION_SECRET and DB_ENCRYPTION_KEY in .env</span>
<span class="h">docker compose up -d</span>
<span class="h">docker compose exec oikos node setup.js</span></div>
<p class="setup-note" data-t="setup_note">Then open <code>http://localhost:3000</code> and log in. Need a step-by-step guide, HTTPS setup, or troubleshooting? See the <a href="https://github.com/ulsklyc/oikos/blob/main/docs/installation.md" target="_blank" rel="noopener">Installation Guide</a>.</p>
</div>
</div>
</section>
<!-- CTA -->
<section class="cta-section">
<div class="wrap">
<div class="cta-box reveal vis">
<h2 data-t="cta_title">Ready to take back control?</h2>
<p data-t="cta_desc">Oikos is free, open-source, and built for families who value their privacy.</p>
<div class="hero-actions">
<a href="https://github.com/ulsklyc/oikos" class="btn-primary" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"></path></svg>
<span data-t="cta_btn">Get Started on GitHub</span>
</a>
</div>
</div>
</div>
</section>
<footer>
<div class="wrap">
<p data-t="footer_heart">Built with care for families who value privacy and simplicity.</p>
<div class="footer-links">
<a href="https://github.com/ulsklyc/oikos" target="_blank" rel="noopener">GitHub</a>
<a href="https://github.com/ulsklyc/oikos/blob/main/LICENSE" target="_blank" rel="noopener">MIT License</a>
<a href="https://github.com/ulsklyc/oikos/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener" data-t="footer_contrib">Contributing</a>
</div>
</div>
</footer>
<script>
(function(){
'use strict';
var T = {
en: {
nav_gh:'GitHub', hero_badge:'Open Source · Self-Hosted · Private',
hero_h1_pre:'Your household,', hero_h1_em:'organized together.',
hero_sub:'Tasks, shopping, meals, calendar, budget — everything your family needs in one place. Self-hosted on your own server. No cloud, no tracking, no subscriptions.',
hero_cta:'View on GitHub', hero_install:'Installation Guide', tag_nocloud:'No Cloud Required',
feat_label:'Features', feat_title:'Everything your household needs',
feat_desc:'A complete set of tools designed for families — seamlessly working together.',
f_tasks_t:'Task Management', f_tasks_d:'Shared tasks with deadlines, priorities, subtasks, recurring schedules, and a Kanban board. Assign to family members with one-tap status changes.',
f_meals_t:'Meal Planning', f_meals_d:'Weekly drag-and-drop planner with ingredient lists. Automatically export ingredients to your shopping list with one click.',
f_cal_t:'Calendar Sync', f_cal_d:'Two-way sync with Google Calendar and Apple iCloud. See all family events in one unified view — month, week, day, or agenda.',
more_label:'And more', more_title:'Built for real family life',
f_shop_t:'Shopping Lists', f_shop_d:'Collaborative lists with aisle categories. Import ingredients directly from your meal plans.',
f_budget_t:'Budget Tracking', f_budget_d:'Track income and expenses with 13 currency options, recurring entries, monthly trends, and CSV export.',
f_notes_t:'Notes', f_notes_d:'Colored sticky notes with Markdown. Perfect for recipes, family memos, and quick reminders.',
f_contacts_t:'Contacts', f_contacts_d:'Shared family contact directory with vCard import and export.',
f_pwa_t:'Works Everywhere', f_pwa_d:'Installable PWA on any device. Offline support, dark mode, fully responsive from phone to desktop.',
f_board_t:'Kanban Board', f_board_d:'Visual board view for tasks and projects. Drag cards between columns to track progress.',
f_tasks_t2:'Tasks', f_cal_t2:'Calendar', f_shop_t2:'Shopping', f_meals_t2:'Meals',
f_budget_t2:'Budget', f_notes_t2:'Notes', f_contacts_t2:'Contacts', f_settings_t:'Settings',
screens_label:'Preview', screens_title:'Every screen, beautifully crafted',
screens_desc:'A clean, intuitive interface that adapts to your device and preferred theme.',
phil_label:'Philosophy', phil_title:'Built different, on purpose',
phil_desc:'No subscriptions, no vendor lock-in, no data leaving your home.',
p_priv_t:'Privacy First', p_priv_d:'AES-256 encrypted database with SQLCipher. Zero telemetry. Your data never leaves your server.',
p_self_t:'Fully Self-Hosted', p_self_d:'Runs on a Raspberry Pi, a NAS, or any server. Docker makes setup a one-liner.',
p_build_t:'Zero Build Step', p_build_d:'Pure ES modules, vanilla JS, plain CSS. No bundler, no transpiler, no framework. Ships what you write.',
p_open_t:'Open Source', p_open_d:'MIT licensed. Inspect, modify, extend, contribute. Built in the open for families who care about transparency.',
setup_label:'Get Started', setup_title:'Up and running in minutes',
setup_note:'Then open <code>http://localhost:3000</code> and log in. Need a step-by-step guide, HTTPS setup, or troubleshooting? See the <a href="https://github.com/ulsklyc/oikos/blob/main/docs/installation.md" target="_blank" rel="noopener">Installation Guide</a>.',
cta_title:'Ready to take back control?', cta_desc:'Oikos is free, open-source, and built for families who value their privacy.',
cta_btn:'Get Started on GitHub',
footer_heart:'Built with care for families who value privacy and simplicity.', footer_contrib:'Contributing'
},
de: {
nav_gh:'GitHub', hero_badge:'Open Source · Self-Hosted · Privat',
hero_h1_pre:'Euer Haushalt,', hero_h1_em:'gemeinsam organisiert.',
hero_sub:'Aufgaben, Einkauf, Mahlzeiten, Kalender, Budget — alles, was eure Familie braucht, an einem Ort. Selbstgehostet auf eurem eigenen Server. Keine Cloud, kein Tracking, keine Abos.',
hero_cta:'Auf GitHub ansehen', hero_install:'Installationsanleitung', tag_nocloud:'Keine Cloud nötig',
feat_label:'Funktionen', feat_title:'Alles, was euer Haushalt braucht',
feat_desc:'Ein vollständiges Werkzeugset für Familien — nahtlos aufeinander abgestimmt.',
f_tasks_t:'Aufgabenverwaltung', f_tasks_d:'Gemeinsame Aufgaben mit Fristen, Prioritäten, Unteraufgaben, wiederkehrenden Terminen und Kanban-Board mit Ein-Tipp-Status.',
f_meals_t:'Mahlzeitenplanung', f_meals_d:'Wöchentlicher Drag-and-Drop-Planer mit Zutatenlisten. Zutaten per Klick auf die Einkaufsliste exportieren.',
f_cal_t:'Kalender-Sync', f_cal_d:'Zwei-Wege-Sync mit Google Calendar und Apple iCloud. Alle Familientermine in einer Ansicht — Monat, Woche, Tag oder Agenda.',
more_label:'Und mehr', more_title:'Gebaut für echtes Familienleben',
f_shop_t:'Einkaufslisten', f_shop_d:'Gemeinsame Listen mit Gang-Kategorien. Zutaten direkt aus Mahlzeitenplänen importieren.',
f_budget_t:'Budgetverwaltung', f_budget_d:'Einnahmen und Ausgaben verfolgen mit 13 Währungsoptionen, wiederkehrenden Einträgen, monatlichen Trends und CSV-Export.',
f_notes_t:'Notizen', f_notes_d:'Farbige Haftnotizen mit Markdown. Perfekt für Rezepte, Familien-Memos und schnelle Erinnerungen.',
f_contacts_t:'Kontakte', f_contacts_d:'Gemeinsames Familien-Kontaktverzeichnis mit vCard-Import und -Export.',
f_pwa_t:'Überall nutzbar', f_pwa_d:'Installierbare PWA auf jedem Gerät. Offline-Support, Dark Mode, voll responsive vom Handy bis Desktop.',
f_board_t:'Kanban Board', f_board_d:'Visuelle Board-Ansicht für Aufgaben und Projekte. Karten per Drag & Drop zwischen Spalten verschieben.',
f_tasks_t2:'Aufgaben', f_cal_t2:'Kalender', f_shop_t2:'Einkauf', f_meals_t2:'Mahlzeiten',
f_budget_t2:'Budget', f_notes_t2:'Notizen', f_contacts_t2:'Kontakte', f_settings_t:'Einstellungen',
screens_label:'Vorschau', screens_title:'Jeder Screen, schön gestaltet',
screens_desc:'Eine klare, intuitive Oberfläche, die sich an euer Gerät und bevorzugtes Theme anpasst.',
phil_label:'Philosophie', phil_title:'Bewusst anders gebaut',
phil_desc:'Keine Abos, kein Vendor-Lock-in, keine Daten, die euer Zuhause verlassen.',
p_priv_t:'Datenschutz zuerst', p_priv_d:'AES-256-verschlüsselte Datenbank mit SQLCipher. Keine Telemetrie. Eure Daten verlassen nie euren Server.',
p_self_t:'Vollständig selbstgehostet', p_self_d:'Läuft auf einem Raspberry Pi, NAS oder jedem Server. Docker macht das Setup zum Einzeiler.',
p_build_t:'Kein Build-Schritt', p_build_d:'Pure ES-Module, Vanilla JS, reines CSS. Kein Bundler, kein Transpiler, kein Framework.',
p_open_t:'Open Source', p_open_d:'MIT-lizenziert. Einsehen, anpassen, erweitern, beitragen. Offen gebaut für Familien, die Transparenz schätzen.',
setup_label:'Loslegen', setup_title:'In Minuten einsatzbereit',
setup_note:'Dann <code>http://localhost:3000</code> öffnen und einloggen. Schritt-für-Schritt-Anleitung, HTTPS oder Hilfe bei Problemen? Zur <a href="https://github.com/ulsklyc/oikos/blob/main/docs/installation.md" target="_blank" rel="noopener">Installationsanleitung</a>.',
cta_title:'Bereit, die Kontrolle zurückzugewinnen?', cta_desc:'Oikos ist kostenlos, open-source und gebaut für Familien, die ihre Privatsphäre schätzen.',
cta_btn:'Auf GitHub starten',
footer_heart:'Mit Sorgfalt gebaut für Familien, die Privatsphäre und Einfachheit schätzen.', footer_contrib:'Mitmachen'
}
};
var lang = localStorage.getItem('oikos-lang') || 'en';
var theme = localStorage.getItem('oikos-theme');
function isDark() {
if (theme === 'dark') return true;
if (theme === 'light') return false;
return matchMedia('(prefers-color-scheme:dark)').matches;
}
function applyTheme() {
var d = isDark();
if (theme) document.documentElement.setAttribute('data-theme', theme);
else document.documentElement.removeAttribute('data-theme');
document.getElementById('sunIco').style.display = d ? 'none' : 'block';
document.getElementById('moonIco').style.display = d ? 'block' : 'none';
document.querySelectorAll('.sc').forEach(function(img) {
var v = d ? 'dark' : 'light';
if (img.dataset[v]) img.src = img.dataset[v];
});
}
function applyLang() {
document.documentElement.lang = lang;
var s = T[lang];
document.querySelectorAll('[data-t]').forEach(function(el) {
var k = el.getAttribute('data-t');
if (s[k]) {
if (k === 'setup_note') {
el.textContent = '';
el.insertAdjacentHTML('afterbegin', s[k]);
} else {
el.textContent = s[k];
}
}
});
document.getElementById('langLbl').textContent = lang === 'en' ? 'DE' : 'EN';
document.title = lang === 'en' ? 'Oikos — The Self-Hosted Family Planner' : 'Oikos — Der selbstgehostete Familienplaner';
}
document.getElementById('themeBtn').onclick = function() {
theme = isDark() ? 'light' : 'dark';
localStorage.setItem('oikos-theme', theme);
applyTheme();
};
document.getElementById('langBtn').onclick = function() {
lang = lang === 'en' ? 'de' : 'en';
localStorage.setItem('oikos-lang', lang);
applyLang();
};
matchMedia('(prefers-color-scheme:dark)').addEventListener('change', function() { if (!theme) applyTheme(); });
// Reveal
if ('IntersectionObserver' in window) {
var obs = new IntersectionObserver(function(entries) {
entries.forEach(function(e) { if (e.isIntersecting) { e.target.classList.add('vis'); obs.unobserve(e.target); } });
}, { threshold: 0.08, rootMargin: '0px 0px -40px 0px' });
document.querySelectorAll('.reveal').forEach(function(el) { obs.observe(el); });
} else {
document.querySelectorAll('.reveal').forEach(function(el) { el.classList.add('vis'); });
}
applyTheme();
applyLang();
})();
</script>
</body></html>