feat(website): fully redesign website and invitation page with premium responsive layouts, inline SVG icons, and a sleek vector Globe language selector

This commit is contained in:
Timo
2026-05-18 20:31:18 +02:00
parent 6774b2bfb7
commit 05e1801653
5 changed files with 1174 additions and 57 deletions
+86 -6
View File
@@ -74,8 +74,14 @@ document.addEventListener('DOMContentLoaded', () => {
const isFirefox = navigator.userAgent.includes('Firefox');
if (isFirefox) {
actions.innerHTML = `
<a href="https://addons.mozilla.org/de/firefox/addon/koalasync/" class="primary btn-firefox" style="text-align:center; text-decoration:none; display:block; padding: 1.2rem; border-radius: 12px; font-weight: 700;">GET IT ON MOZILLA ADD-ONS</a>
<a href="https://github.com/shik3i/KoalaSync" style="text-align:center; color:#e66000; text-decoration:underline; font-size:0.85rem; margin-top:0.8rem; display:block; font-weight: 600;">Download via GitHub</a>
<a href="https://addons.mozilla.org/de/firefox/addon/koalasync/" class="primary btn-firefox" style="text-align:center; text-decoration:none; display:flex; align-items:center; justify-content:center; gap: 8px; padding: 1.2rem; border-radius: 12px; font-weight: 700;">
<span>🦊</span>
<span lang="en">GET IT ON MOZILLA ADD-ONS</span><span lang="de">IM FIREFOX ADD-ON STORE HERUNTERLADEN</span>
</a>
<a href="https://github.com/shik3i/KoalaSync" style="text-align:center; text-decoration:none; display:flex; align-items:center; justify-content:center; gap: 8px; padding: 0.8rem; background: rgba(255, 255, 255, 0.04); border: 1px solid var(--glass-border); color: #e66000; border-radius: 12px; font-weight: 700; font-size: 0.85rem; margin-top: 0.5rem; transition: background 0.2s;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
<span lang="en">Download via GitHub</span><span lang="de">Über GitHub herunterladen</span>
</a>
<p style="text-align:center; font-size:0.8rem; opacity:0.7; margin-top: 1.2rem; color: var(--text-muted);">
<span lang="en">The extension is required to join and sync videos.</span>
<span lang="de">Die Erweiterung ist erforderlich, um beizutreten und Videos zu synchronisieren.</span>
@@ -83,8 +89,14 @@ document.addEventListener('DOMContentLoaded', () => {
`;
} else {
actions.innerHTML = `
<a href="#" class="primary" style="text-align:center; text-decoration:none; display:block; padding: 1.2rem; background: var(--accent); color: white; border-radius: 12px; font-weight: 700;">GET IT ON CHROME WEBSTORE</a>
<a href="https://github.com/shik3i/KoalaSync" style="text-align:center; color:var(--accent); text-decoration:underline; font-size:0.85rem; margin-top:0.8rem; display:block; font-weight: 600;">Download via GitHub</a>
<a href="#" class="primary" style="text-align:center; text-decoration:none; display:flex; align-items:center; justify-content:center; gap: 8px; padding: 1.2rem; background: var(--accent); color: white; border-radius: 12px; font-weight: 700;">
<img src="assets/chrome.svg" width="20" style="display: block;">
<span lang="en">GET IT ON CHROME WEBSTORE</span><span lang="de">IM CHROME WEB STORE HERUNTERLADEN</span>
</a>
<a href="https://github.com/shik3i/KoalaSync" style="text-align:center; text-decoration:none; display:flex; align-items:center; justify-content:center; gap: 8px; padding: 0.8rem; background: rgba(255, 255, 255, 0.04); border: 1px solid var(--glass-border); color: var(--accent); border-radius: 12px; font-weight: 700; font-size: 0.85rem; margin-top: 0.5rem; transition: background 0.2s;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
<span lang="en">Download via GitHub</span><span lang="de">Über GitHub herunterladen</span>
</a>
<p style="text-align:center; font-size:0.8rem; opacity:0.7; margin-top: 1.2rem; color: var(--text-muted);">
<span lang="en">The extension is required to join and sync videos.</span>
<span lang="de">Die Erweiterung ist erforderlich, um beizutreten und Videos zu synchronisieren.</span>
@@ -95,8 +107,12 @@ document.addEventListener('DOMContentLoaded', () => {
actions.innerHTML = `
<div class="joining-spinner" style="text-align:center; padding: 1rem;">
<div style="font-size: 1.2rem; margin-bottom: 0.5rem;">🚀</div>
<div style="font-weight: 600; color: var(--accent);">Joining room automatically...</div>
<p style="font-size: 0.75rem; color: var(--text-muted); margin-top: 0.5rem;">Your extension is taking care of it.</p>
<div style="font-weight: 600; color: var(--accent);">
<span lang="en">Joining room automatically...</span><span lang="de">Raum wird automatisch betreten...</span>
</div>
<p style="font-size: 0.75rem; color: var(--text-muted); margin-top: 0.5rem;">
<span lang="en">Your extension is taking care of it.</span><span lang="de">Deine Erweiterung kümmert sich darum.</span>
</p>
</div>
`;
@@ -270,6 +286,70 @@ document.addEventListener('DOMContentLoaded', () => {
}
};
// Extension Mockup Tab Switcher
const mockTabs = document.querySelectorAll('.mock-tab');
const mockScreens = document.querySelectorAll('.mock-screen');
mockTabs.forEach(tab => {
tab.addEventListener('click', () => {
mockTabs.forEach(t => t.classList.remove('active'));
mockScreens.forEach(s => s.classList.remove('active'));
tab.classList.add('active');
const targetId = tab.getAttribute('data-target');
const targetScreen = document.getElementById(targetId);
if (targetScreen) {
targetScreen.classList.add('active');
}
});
});
// Terminal Tab Switcher
const termTabBtns = document.querySelectorAll('.terminal-tab-btn');
const termPanes = document.querySelectorAll('.terminal-pane');
termTabBtns.forEach(btn => {
btn.addEventListener('click', () => {
termTabBtns.forEach(b => b.classList.remove('active'));
termPanes.forEach(p => p.classList.remove('active'));
btn.classList.add('active');
const targetPaneId = btn.getAttribute('data-tab');
const targetPane = document.getElementById(targetPaneId);
if (targetPane) {
targetPane.classList.add('active');
}
});
});
// Terminal Clipboard Copy
const copyBtn = document.querySelector('.terminal-copy-btn');
if (copyBtn) {
copyBtn.addEventListener('click', () => {
const activePane = document.querySelector('.terminal-pane.active');
if (!activePane) return;
const codeElement = activePane.querySelector('code');
if (!codeElement) return;
const textToCopy = codeElement.innerText || codeElement.textContent;
navigator.clipboard.writeText(textToCopy).then(() => {
const isDE = document.documentElement.classList.contains('lang-de');
const originalHTML = copyBtn.innerHTML;
copyBtn.innerHTML = isDE ? '✅ Kopiert!' : '✅ Copied!';
copyBtn.disabled = true;
setTimeout(() => {
copyBtn.innerHTML = originalHTML;
copyBtn.disabled = false;
}, 2000);
}).catch(err => {
console.error('Failed to copy text: ', err);
});
});
}
checkInvite();
updateDynamicVersion();
});
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>

After

Width:  |  Height:  |  Size: 835 B

+308 -22
View File
@@ -56,8 +56,14 @@
<div class="nav-links">
<a href="#features"><span lang="en">Features</span><span lang="de">Funktionen</span></a>
<a href="#how-it-works"><span lang="en">How it works</span><span lang="de">So funktioniert's</span></a>
<a href="https://github.com/Shik3i/KoalaSync" target="_blank">GitHub</a>
<a class="lang-toggle" onclick="toggleLanguage(event)">🌍 EN/DE</a>
<a href="https://github.com/Shik3i/KoalaSync" target="_blank" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
GitHub
</a>
<a class="lang-toggle" onclick="toggleLanguage(event)" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width: 16px; height: 16px; display: block;"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><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-10 15.3 15.3 0 0 1 4-10z"></path></svg>
EN/DE
</a>
</div>
</div>
</nav>
@@ -71,8 +77,8 @@
<span lang="de">Gemeinsam schauen.<br>Perfekt synchron.</span>
</h1>
<h2 class="hero-subtitle" data-reveal>
<span lang="en">The ultimate free watch party extension for YouTube, Twitch, and local MP4s.</span>
<span lang="de">Die ultimative, kostenlose Watch-Party-Erweiterung für YouTube, Twitch und lokale MP4s.</span>
<span lang="en">The ultimate free watch party extension for YouTube, Twitch, and local MP4s. Built for extreme precision and data sovereignty.</span>
<span lang="de">Die ultimative, kostenlose Watch-Party-Erweiterung für YouTube, Twitch und lokale MP4s. Entwickelt für extreme Präzision und Datenhoheit.</span>
</h2>
<div class="cta-group" data-reveal>
<a href="#" class="btn btn-primary">
@@ -83,11 +89,151 @@
🦊
<span lang="en">Add to Firefox</span><span lang="de">Zu Firefox hinzufügen</span>
</a>
<a href="https://github.com/Shik3i/KoalaSync" class="btn btn-secondary">
<a href="https://github.com/Shik3i/KoalaSync" class="btn btn-secondary" style="display: inline-flex; align-items: center; gap: 8px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
GitHub Repo
</a>
</div>
</div>
<!-- Interactive CSS Extension Mockup -->
<div class="hero-mockup-wrapper" data-reveal>
<div class="extension-mockup">
<div class="mock-header">
<img src="assets/logo.png" alt="KoalaSync">
<span>KOALASYNC</span>
</div>
<div class="mock-tabs">
<button class="mock-tab active" data-target="mock-room">Room</button>
<button class="mock-tab" data-target="mock-sync">Sync</button>
<button class="mock-tab" data-target="mock-settings">Settings</button>
<button class="mock-tab" data-target="mock-dev">Dev</button>
</div>
<div class="mock-body">
<!-- ROOM TAB -->
<div id="mock-room" class="mock-screen active">
<div class="mock-joined-room">
<span class="mock-room-name">brave-eagle-80</span>
<span class="mock-server-badge">Official Server</span>
</div>
<div class="mock-card">
<div class="mock-label">Invite Link</div>
<div class="mock-invite-box">
<input type="text" class="mock-input" value="https://sync.koalastuff.net/join.html#join:brave-eagle-80:pass" readonly>
<button class="mock-btn" style="padding: 0.35rem 0.5rem;" onclick="navigator.clipboard.writeText('https://sync.koalastuff.net/join.html#join:brave-eagle-80:pass')">Copy</button>
</div>
</div>
<div class="mock-card" style="flex: 1; display: flex; flex-direction: column; overflow: hidden;">
<div class="mock-label">Peers in Room</div>
<div class="mock-peer-list">
<div class="mock-peer-item">
<div class="mock-peer-header">
<span class="mock-peer-name">CoolUsername</span>
<span class="mock-peer-meta">YOU</span>
</div>
<div class="mock-peer-tab">🎬 Germany's Next Flopmodel - S21E12</div>
<div class="mock-peer-status"><span style="color:#22c55e;"></span> 0:55 • Playing</div>
</div>
<div class="mock-peer-item">
<div class="mock-peer-header">
<span class="mock-peer-name">KoalaPC</span>
<span class="mock-peer-meta">Peer</span>
</div>
<div class="mock-peer-tab">🎬 Germany's Next Flopmodel - S21E12</div>
<div class="mock-peer-status"><span style="color:#22c55e;"></span> 0:55 • Playing</div>
</div>
</div>
</div>
<button class="mock-btn" style="background:#ef4444; width:100%; margin-top:auto;">Leave Room</button>
</div>
<!-- SYNC TAB -->
<div id="mock-sync" class="mock-screen">
<div class="mock-target-box">
⭐ MATCH: Germany's Next Flopmodel - S21E12
</div>
<div class="mock-card">
<div class="mock-label">Remote Control</div>
<div class="mock-ctrl-buttons">
<button class="mock-btn mock-btn-play">▶ Play</button>
<button class="mock-btn mock-btn-pause">⏸ Pause</button>
<button class="mock-btn mock-btn-force">⚡ Force Sync Everyone</button>
</div>
</div>
<div class="mock-card">
<div class="mock-label">Last Activity Status</div>
<div class="mock-status-display">
<span style="font-weight: 700;">PAUSE</span>
<span class="mock-status-pill">● In Sync</span>
</div>
<div class="mock-log-item">
Pause triggered by CoolUsername
</div>
</div>
</div>
<!-- SETTINGS TAB -->
<div id="mock-settings" class="mock-screen">
<div class="mock-card">
<div class="mock-label">Your Username</div>
<input type="text" class="mock-input" value="CoolUsername" readonly>
</div>
<div class="mock-card" style="flex: 1;">
<div class="mock-label">Preferences</div>
<div class="mock-settings-row">
<input type="checkbox" id="chk-noise" checked disabled>
<div>
<label for="chk-noise">Filter Noise Tabs</label>
<p>Hide common non-video websites from the target selector.</p>
</div>
</div>
<div class="mock-settings-row">
<input type="checkbox" id="chk-binge" checked disabled>
<div>
<label for="chk-binge">Auto-Sync Next Episode</label>
<p>Automatically sync transitions between series episodes.</p>
</div>
</div>
</div>
<button class="mock-btn" style="background:#4b5563; width:100%;">Regenerate Peer ID</button>
</div>
<!-- DEV TAB -->
<div id="mock-dev" class="mock-screen">
<div class="mock-card">
<div class="mock-label">Diagnostics</div>
<div class="mock-diagnostic-grid">
<div class="mock-diag-box">
<div class="mock-label" style="margin-bottom:0.1rem; font-size:0.55rem;">Ready State</div>
<span class="diag-val">HAVE_ENOUGH_DATA</span>
</div>
<div class="mock-diag-box">
<div class="mock-label" style="margin-bottom:0.1rem; font-size:0.55rem;">Seek Delta</div>
<span class="diag-val">0.05s</span>
</div>
<div class="mock-diag-box">
<div class="mock-label" style="margin-bottom:0.1rem; font-size:0.55rem;">Buffered</div>
<span class="diag-val">300.2s</span>
</div>
<div class="mock-diag-box">
<div class="mock-label" style="margin-bottom:0.1rem; font-size:0.55rem;">Engine State</div>
<span class="diag-val" style="color:#22c55e;">SYNCED</span>
</div>
</div>
</div>
<div class="mock-card" style="flex: 1; overflow: hidden;">
<div class="mock-label">Event Stream</div>
<div style="font-family:monospace; font-size:0.65rem; color:#10b981; line-height:1.4;">
<div>[18:37:32] EVENT: PLAY -&gt; ACK</div>
<div>[18:37:32] FORCE_SYNC -&gt; 0.05s diff</div>
<div>[18:37:38] EVENT: PAUSE -&gt; BROADCAST</div>
<div style="color:var(--text-muted);">[18:37:42] content_heartbeat: active</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</header>
@@ -97,33 +243,46 @@
<span lang="en">Why KoalaSync?</span><span lang="de">Warum KoalaSync?</span>
</h2>
<p style="text-align: center; color: var(--text-muted); margin-bottom: 4rem;">
<span lang="en">Built for performance, privacy, and simplicity.</span><span lang="de">Entwickelt für Leistung, Privatsphäre und Einfachheit.</span>
<span lang="en">Built for frame-perfect performance, absolute privacy, and total simplicity.</span>
<span lang="de">Entwickelt für bildgenaue Leistung, absolute Privatsphäre und totale Einfachheit.</span>
</p>
<div class="features-grid">
<div class="feature-card" data-reveal>
<div class="feature-icon"></div>
<h3><span lang="en">Real-time Sync</span><span lang="de">Echtzeit-Synchronisation</span></h3>
<p lang="en">Proprietary two-phase synchronization protocol ensures sub-millisecond precision across all peers.</p>
<p lang="de">Das proprietäre Zwei-Phasen-Synchronisationsprotokoll gewährleistet Sub-Millisekunden-Präzision für alle Teilnehmer.</p>
<h3>
<span lang="en">Full Control / Instant Sync</span>
<span lang="de">Volle Kontrolle & Echtzeit</span>
</h3>
<p lang="en">One pauses, everyone pauses. One seeks, everyone follows. Our custom two-phase sync protocol coordinates playback with sub-millisecond precision across all participants.</p>
<p lang="de">Einer pausiert, alle pausieren. Einer spult, alle folgen. Unser Zwei-Phasen-Synchronisationsprotokoll koordiniert die Wiedergabe in Echtzeit mit Sub-Millisekunden-Präzision.</p>
</div>
<div class="feature-card" data-reveal>
<div class="feature-icon">🎬</div>
<h3><span lang="en">Episode Auto-Sync</span><span lang="de">Automatische Episoden-Synchronisation</span></h3>
<p lang="en">Perfectly sync series binges. All peers wait until everyone has loaded the next episode.</p>
<p lang="de">Perfekt synchronisiertes Serien-Bingeing. Alle warten, bis jeder die nächste Episode geladen hat.</p>
<h3>
<span lang="en">Endless Binge-Watching</span>
<span lang="de">Grenzenloses Bingen</span>
</h3>
<p lang="en">Autoplay perfectly in sync. KoalaSync automatically detects episode transitions and holds playback until all peers have successfully loaded the next video.</p>
<p lang="de">Nächste Episode startet für jeden zeitgleich. KoalaSync erkennt den Episodenwechsel und pausiert, bis jeder Teilnehmer das neue Video fertig geladen hat.</p>
</div>
<div class="feature-card" data-reveal>
<div class="feature-icon">🛡️</div>
<h3><span lang="en">Privacy First</span><span lang="de">Privatsphäre zuerst</span></h3>
<p lang="en">Zero data persistence. Our relay server runs entirely in RAM and collects no telemetry or logs.</p>
<p lang="de">Keine Datenspeicherung. Unser Relay-Server läuft komplett im RAM und sammelt weder Telemetrie noch Logs.</p>
<h3>
<span lang="en">Zero Accounts / Pure Privacy</span>
<span lang="de">Keine Accounts / Datenschutz</span>
</h3>
<p lang="en">No registration, no tracking, and no database persistence. The server runs entirely in RAM, collects no telemetry or logs, and completely forgets your room when you leave.</p>
<p lang="de">Keine Registrierung, kein Tracking und keine Datenspeicherung. Der Server läuft flüchtig im RAM, sammelt keine Logs und vergisst deinen Raum sofort nach dem Verlassen.</p>
</div>
<div class="feature-card" data-reveal>
<div class="feature-icon"></div>
<h3><span lang="en">Smart Match</span><span lang="de">Intelligente Zuordnung</span></h3>
<p lang="en">Find the right tab instantly. KoalaSync highlights and sorts matching video tabs for you.</p>
<p lang="de">Finden Sie sofort den richtigen Tab. KoalaSync markiert und sortiert passende Video-Tabs für Sie.</p>
<div class="feature-icon">🌐</div>
<h3>
<span lang="en">Universal HTML5 Support</span>
<span lang="de">Universeller HTML5-Support</span>
</h3>
<p lang="en">Works on YouTube, Twitch, Netflix, Disney+, Jellyfin, Emby, and any standard webpage containing a HTML5 video element. Perfect for custom self-hosted setups.</p>
<p lang="de">Unterstützt YouTube, Twitch, Netflix, Disney+, Jellyfin, Emby und jede beliebige Webseite mit einem HTML5-Video-Tag. Ideal auch für eigene Medienbibliotheken.</p>
</div>
</div>
@@ -144,7 +303,25 @@
<p lang="en">Add KoalaSync to your browser from the Chrome Web Store, Firefox Add-ons, or download the latest developer ZIP from GitHub.</p>
<p lang="de">Füge KoalaSync aus dem Chrome Web Store, den Firefox Add-ons oder über die Entwickler-ZIP von GitHub zu deinem Browser hinzu.</p>
</div>
<div style="background: var(--card); height: 200px; border-radius: 20px; border: 1px solid var(--glass-border);"></div>
<!-- Custom Step 1 Illustration -->
<div class="step-illustration-1" data-reveal>
<div class="illus-browser-bar">
<div class="illus-browser-header">
<div class="illus-dots">
<span class="illus-dot"></span>
<span class="illus-dot"></span>
<span class="illus-dot"></span>
</div>
<div class="illus-address-bar"></div>
<div class="illus-extension-icon">🐨</div>
</div>
<div class="illus-browser-body">
<div class="illus-line-placeholder" style="width: 80%;"></div>
<div class="illus-line-placeholder" style="width: 50%;"></div>
<div class="illus-line-placeholder" style="width: 90%;"></div>
</div>
</div>
</div>
</div>
<div class="step">
<div class="step-text" data-reveal>
@@ -153,7 +330,20 @@
<p lang="en">Open the extension, enter a Room ID and Password, and hit Join. Your secure synchronization space is ready.</p>
<p lang="de">Öffnen Sie die Erweiterung, geben Sie eine Raum-ID und ein Passwort ein und klicken Sie auf Beitreten. Ihr sicherer Synchronisationsraum ist bereit.</p>
</div>
<div style="background: var(--card); height: 200px; border-radius: 20px; border: 1px solid var(--glass-border);"></div>
<!-- Custom Step 2 Illustration -->
<div class="step-illustration-2" data-reveal>
<div class="illus-form-box">
<div class="illus-form-field">
<div class="illus-line-placeholder" style="width: 30%; height: 6px;"></div>
<div class="illus-form-input">
<div class="illus-typing-text">brave-eagle-80</div>
</div>
</div>
<div class="illus-form-btn">
<span lang="en">CREATE ROOM</span><span lang="de">RAUM ERSTELLEN</span>
</div>
</div>
</div>
</div>
<div class="step">
<div class="step-text" data-reveal>
@@ -162,7 +352,103 @@
<p lang="en">Send the invite link to your friends. Once they join, select your video tab and enjoy perfectly synced playback.</p>
<p lang="de">Senden Sie den Einladungslink an Ihre Freunde. Sobald sie beitreten, wählen Sie Ihren Video-Tab aus und genießen Sie die perfekt synchronisierte Wiedergabe.</p>
</div>
<div style="background: var(--card); height: 200px; border-radius: 20px; border: 1px solid var(--glass-border);"></div>
<!-- Custom Step 3 Illustration -->
<div class="step-illustration-3" data-reveal>
<div class="illus-players-container">
<div class="illus-mini-player">
<div class="illus-video-screen">
<div class="illus-play-icon"></div>
</div>
<div class="illus-player-timeline">
<div class="illus-timeline-bar">
<div class="illus-timeline-progress"></div>
</div>
</div>
</div>
<div class="illus-sync-connection">
<div class="illus-sync-line">
<div class="illus-sync-pulse"></div>
</div>
<span class="illus-sync-label">IN SYNC</span>
</div>
<div class="illus-mini-player">
<div class="illus-video-screen">
<div class="illus-play-icon"></div>
</div>
<div class="illus-player-timeline">
<div class="illus-timeline-bar">
<div class="illus-timeline-progress"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Self Hosters Paradise Section -->
<section id="self-hosting" style="background: rgba(15, 23, 42, 0.4); border-top: 1px solid var(--glass-border); border-bottom: 1px solid var(--glass-border);">
<div class="container">
<h2 style="font-size: 2.5rem; text-align: center; margin-bottom: 1rem;">
<span lang="en">Self-Hosters Paradise</span><span lang="de">Das Paradies für Self-Hoster</span>
</h2>
<p style="text-align: center; color: var(--text-muted); margin-bottom: 3rem; max-width: 600px; margin-left: auto; margin-right: auto;">
<span lang="en">Maintain absolute data sovereignty. Deploy your own private high-performance relay server in under 60 seconds.</span>
<span lang="de">Behalte die absolute Datenhoheit. Richte deinen eigenen privaten Hochleistungs-Relay-Server in unter 60 Sekunden ein.</span>
</p>
<div class="terminal-container" data-reveal>
<div class="terminal-header">
<div class="terminal-dots">
<span class="terminal-dot red"></span>
<span class="terminal-dot yellow"></span>
<span class="terminal-dot green"></span>
</div>
<div class="terminal-tabs">
<button class="terminal-tab-btn active" data-tab="pane-docker">docker-compose.yml</button>
<button class="terminal-tab-btn" data-tab="pane-caddy">Caddyfile</button>
</div>
</div>
<div class="terminal-body">
<button class="terminal-copy-btn">
📋 <span lang="en">Copy Code</span><span lang="de">Code kopieren</span>
</button>
<div id="pane-docker" class="terminal-pane active">
<pre><code><span class="t-key">version:</span> <span class="t-str">"3.8"</span>
<span class="t-key">services:</span>
<span class="t-key">koalasync:</span>
<span class="t-key">image:</span> <span class="t-str">ghcr.io/shik3i/koalasync:latest</span>
<span class="t-key">container_name:</span> <span class="t-str">koalasync-relay</span>
<span class="t-key">ports:</span>
<span class="t-key">-</span> <span class="t-str">"3000:3000"</span>
<span class="t-key">environment:</span>
<span class="t-key">-</span> <span class="t-str">NODE_ENV=production</span>
<span class="t-key">-</span> <span class="t-str">PORT=3000</span>
<span class="t-key">restart:</span> <span class="t-str">always</span>
<span class="t-key">logging:</span>
<span class="t-key">driver:</span> <span class="t-str">"json-file"</span>
<span class="t-key">options:</span>
<span class="t-key">max-size:</span> <span class="t-str">"10m"</span></code></pre>
</div>
<div id="pane-caddy" class="terminal-pane">
<pre><code><span class="t-comment"># Reverse proxy for your secure WebSocket relay server</span>
<span class="t-key">sync.yourdomain.com</span> {
<span class="t-key">reverse_proxy</span> <span class="t-val">localhost:3000</span>
<span class="t-key">header</span> {
<span class="t-comment"># Security policies</span>
<span class="t-key">Permissions-Policy</span> <span class="t-str">"interest-cohort=()"</span>
<span class="t-key">Strict-Transport-Security</span> <span class="t-str">"max-age=31536000; includeSubDomains; preload"</span>
<span class="t-key">X-Content-Type-Options</span> <span class="t-str">"nosniff"</span>
<span class="t-key">X-Frame-Options</span> <span class="t-str">"DENY"</span>
}
}</code></pre>
</div>
</div>
</div>
</div>
+8 -2
View File
@@ -48,8 +48,14 @@
<button class="hamburger" onclick="document.querySelector('.nav-links').classList.toggle('open')" aria-label="Menu"></button>
<div class="nav-links">
<a href="index.html">Home</a>
<a href="https://github.com/shik3i/KoalaSync" target="_blank">GitHub</a>
<a class="lang-toggle" onclick="toggleLanguage(event)">🌍 EN/DE</a>
<a href="https://github.com/shik3i/KoalaSync" target="_blank" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
GitHub
</a>
<a class="lang-toggle" onclick="toggleLanguage(event)" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width: 16px; height: 16px; display: block;"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><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-10 15.3 15.3 0 0 1 4-10z"></path></svg>
EN/DE
</a>
</div>
</div>
</nav>
+771 -27
View File
@@ -156,43 +156,45 @@ nav {
display: flex;
align-items: center;
padding-top: 8rem;
text-align: center;
padding-bottom: 4rem;
}
.hero-grid {
display: flex;
flex-direction: column;
display: grid;
grid-template-columns: 1.2fr 1fr;
align-items: center;
gap: 2rem;
gap: 4rem;
text-align: left;
width: 100%;
}
.hero-text h1 {
font-size: 5rem;
font-size: 4.5rem;
font-weight: 800;
line-height: 1;
line-height: 1.1;
margin-bottom: 1.5rem;
background: linear-gradient(to bottom, #fff 30%, #6366f1);
background: linear-gradient(to bottom, #fff 40%, #6366f1);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.hero-text p, .hero-subtitle {
font-size: 1.5rem;
font-size: 1.25rem;
color: var(--text-muted);
margin-bottom: 3rem;
max-width: 700px;
margin-left: auto;
margin-right: auto;
margin-bottom: 2.5rem;
max-width: 600px;
font-weight: 400;
line-height: 1.6;
}
.cta-group {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.btn {
padding: 1rem 2rem;
padding: 0.85rem 1.75rem;
border-radius: 12px;
font-weight: 600;
text-decoration: none;
@@ -200,6 +202,7 @@ nav {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 0.95rem;
}
.btn-primary {
@@ -240,9 +243,9 @@ nav {
display: inline-block;
background: rgba(99, 102, 241, 0.1);
color: var(--accent);
padding: 0.5rem 1.25rem;
padding: 0.4rem 1rem;
border-radius: 99px;
font-size: 0.85rem;
font-size: 0.8rem;
font-weight: 700;
margin-bottom: 1.5rem;
border: 1px solid rgba(99, 102, 241, 0.3);
@@ -250,15 +253,334 @@ nav {
text-transform: uppercase;
}
/* --- Interactive CSS Extension Mockup --- */
.hero-mockup-wrapper {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.hero-image {
.extension-mockup {
width: 320px;
height: 490px;
background: #0f172a;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4), 0 0 20px rgba(99, 102, 241, 0.15);
display: flex;
flex-direction: column;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
overflow: hidden;
position: relative;
user-select: none;
}
.mock-header {
background: #1e293b;
padding: 0.85rem 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.mock-header img {
width: 20px;
height: 20px;
}
.mock-header span {
font-weight: 800;
font-size: 0.95rem;
letter-spacing: 0.5px;
color: var(--text);
}
.mock-tabs {
display: grid;
grid-template-columns: repeat(4, 1fr);
background: #1e293b;
padding: 0.25rem;
gap: 0.25rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.mock-tab {
background: none;
border: none;
color: var(--text-muted);
font-size: 0.75rem;
font-weight: 600;
padding: 0.4rem 0;
cursor: pointer;
border-radius: 6px;
transition: all 0.2s;
}
.mock-tab:hover {
color: var(--text);
}
.mock-tab.active {
background: var(--accent);
color: white;
}
.mock-body {
flex: 1;
overflow-y: auto;
padding: 0.95rem;
display: flex;
flex-direction: column;
gap: 0.85rem;
font-size: 0.8rem;
}
.mock-screen {
display: none;
flex-direction: column;
gap: 0.85rem;
height: 100%;
}
.mock-screen.active {
display: flex;
}
/* Common Mockup Components */
.mock-card {
background: #1e293b;
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 10px;
padding: 0.75rem;
}
.mock-label {
font-size: 0.65rem;
font-weight: 700;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.4rem;
}
.mock-input {
background: #0f172a;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 6px;
color: white;
padding: 0.45rem;
font-size: 0.75rem;
width: 100%;
outline: none;
}
.mock-btn {
background: var(--accent);
color: white;
border: none;
border-radius: 6px;
padding: 0.5rem;
font-weight: 600;
font-size: 0.75rem;
cursor: pointer;
transition: background 0.2s;
text-align: center;
}
.mock-btn:hover {
background: #4f46e5;
}
/* Room Tab Details */
.mock-joined-room {
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(99, 102, 241, 0.1);
border: 1px solid rgba(99, 102, 241, 0.2);
border-radius: 8px;
padding: 0.6rem 0.75rem;
}
.mock-room-name {
font-weight: 700;
color: var(--accent);
font-size: 0.85rem;
}
.mock-server-badge {
background: rgba(99, 102, 241, 0.2);
color: var(--accent);
font-size: 0.6rem;
font-weight: 700;
padding: 2px 6px;
border-radius: 4px;
}
.mock-invite-box {
display: flex;
gap: 0.4rem;
}
.mock-invite-box input {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mock-peer-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.mock-peer-item {
display: flex;
flex-direction: column;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.03);
border-radius: 8px;
padding: 0.5rem 0.6rem;
}
.mock-peer-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.25rem;
}
.mock-peer-name {
font-weight: 700;
color: var(--text);
}
.mock-peer-meta {
font-size: 0.65rem;
color: var(--text-muted);
}
.mock-peer-tab {
font-size: 0.7rem;
color: var(--accent);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mock-peer-status {
font-size: 0.65rem;
color: var(--text-muted);
display: flex;
align-items: center;
gap: 0.25rem;
}
/* Sync Tab Details */
.mock-target-box {
display: flex;
align-items: center;
gap: 0.4rem;
background: rgba(234, 179, 8, 0.1);
border: 1px solid rgba(234, 179, 8, 0.2);
color: #facc15;
padding: 0.5rem 0.6rem;
border-radius: 8px;
font-weight: 600;
font-size: 0.7rem;
}
.mock-ctrl-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.5rem;
}
.mock-btn-play { background: #22c55e; }
.mock-btn-play:hover { background: #16a34a; }
.mock-btn-pause { background: #ef4444; }
.mock-btn-pause:hover { background: #dc2626; }
.mock-btn-force { background: var(--accent); grid-column: span 2; }
.mock-status-display {
display: flex;
align-items: center;
justify-content: space-between;
}
.mock-status-pill {
display: inline-flex;
align-items: center;
gap: 0.3rem;
background: rgba(34, 197, 94, 0.1);
border: 1px solid rgba(34, 197, 94, 0.2);
color: #22c55e;
padding: 2px 8px;
border-radius: 99px;
font-size: 0.65rem;
font-weight: 700;
}
.mock-log-item {
font-size: 0.7rem;
color: var(--text-muted);
border-left: 2px solid var(--accent);
padding-left: 0.4rem;
margin-top: 0.4rem;
}
/* Settings Tab Details */
.mock-settings-row {
display: flex;
align-items: flex-start;
gap: 0.5rem;
margin-bottom: 0.6rem;
}
.mock-settings-row input[type="checkbox"] {
margin-top: 0.2rem;
cursor: pointer;
}
.mock-settings-row label {
cursor: pointer;
font-weight: 600;
}
.mock-settings-row p {
font-size: 0.65rem;
color: var(--text-muted);
margin-top: 0.1rem;
}
/* Dev Tab Details */
.mock-diagnostic-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.4rem;
}
.mock-diag-box {
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.04);
border-radius: 6px;
padding: 0.4rem;
}
.mock-diag-box .diag-val {
font-family: monospace;
font-weight: 700;
color: var(--accent);
font-size: 0.75rem;
}
/* --- Features --- */
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 2rem;
margin-top: 4rem;
}
@@ -268,17 +590,33 @@ nav {
padding: 2.5rem;
border-radius: 24px;
border: 1px solid var(--glass-border);
transition: transform 0.3s;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
}
.feature-card::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
border-radius: 24px;
padding: 1px;
background: linear-gradient(to bottom right, rgba(99, 102, 241, 0.25), transparent);
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.feature-card:hover {
transform: translateY(-10px);
transform: translateY(-5px);
box-shadow: 0 15px 30px rgba(99, 102, 241, 0.15);
border-color: rgba(99, 102, 241, 0.2);
}
.feature-icon {
width: 48px;
height: 48px;
background: var(--accent-glow);
background: rgba(99, 102, 241, 0.1);
color: var(--accent);
display: flex;
align-items: center;
@@ -286,28 +624,32 @@ nav {
border-radius: 12px;
margin-bottom: 1.5rem;
font-size: 1.5rem;
border: 1px solid rgba(99, 102, 241, 0.2);
}
.feature-card h3 {
margin-bottom: 1rem;
font-size: 1.5rem;
margin-bottom: 0.75rem;
font-size: 1.35rem;
font-weight: 700;
}
.feature-card p {
color: var(--text-muted);
font-size: 0.95rem;
line-height: 1.5;
}
/* --- How it works --- */
.steps {
display: flex;
flex-direction: column;
gap: 4rem;
gap: 6rem;
}
.step {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
grid-template-columns: 1fr 1.1fr;
gap: 6rem;
align-items: center;
}
@@ -316,11 +658,412 @@ nav {
}
.step-num {
font-size: 5rem;
font-size: 4rem;
font-weight: 800;
color: var(--accent-glow);
color: rgba(99, 102, 241, 0.15);
line-height: 1;
margin-bottom: 0.5rem;
}
.step-text h3 {
font-size: 1.85rem;
margin-bottom: 1rem;
font-weight: 800;
}
.step-text p {
color: var(--text-muted);
font-size: 1.05rem;
line-height: 1.6;
}
/* Custom CSS Mockups for step illustrations */
.step-illustration-1, .step-illustration-2, .step-illustration-3 {
background: #1e293b;
height: 240px;
border-radius: 20px;
border: 1px solid var(--glass-border);
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
}
/* Step 1 Illustration: Browser & Extension icon trigger */
.illus-browser-bar {
width: 85%;
background: #0f172a;
border: 1px solid rgba(255,255,255,0.05);
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 20px rgba(0,0,0,0.4);
}
.illus-browser-header {
background: #1e293b;
padding: 0.5rem 0.75rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255,255,255,0.05);
}
.illus-dots {
display: flex;
gap: 4px;
}
.illus-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(255,255,255,0.2);
}
.illus-address-bar {
background: #0f172a;
border: 1px solid rgba(255,255,255,0.05);
padding: 2px 10px;
border-radius: 4px;
width: 60%;
height: 14px;
}
.illus-extension-icon {
position: relative;
width: 20px;
height: 20px;
background: rgba(99, 102, 241, 0.2);
border: 1px solid var(--accent);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
animation: pulse-border 2s infinite alternate;
}
@keyframes pulse-border {
0% { box-shadow: 0 0 0 rgba(99, 102, 241, 0); border-color: rgba(99, 102, 241, 0.4); }
100% { box-shadow: 0 0 8px rgba(99, 102, 241, 0.6); border-color: var(--accent); }
}
.illus-browser-body {
padding: 1.5rem;
height: 80px;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.illus-line-placeholder {
height: 8px;
background: rgba(255,255,255,0.05);
border-radius: 4px;
}
/* Step 2 Illustration: Typing in Form Fields */
.illus-form-box {
width: 70%;
display: flex;
flex-direction: column;
gap: 0.75rem;
background: rgba(15, 23, 42, 0.6);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 1rem;
backdrop-filter: blur(10px);
}
.illus-form-field {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.illus-form-input {
background: #0f172a;
border: 1px solid rgba(99, 102, 241, 0.3);
padding: 0.45rem;
border-radius: 6px;
height: 28px;
position: relative;
overflow: hidden;
}
.illus-typing-text {
font-family: monospace;
font-weight: 700;
color: var(--text);
font-size: 0.8rem;
display: flex;
align-items: center;
}
.illus-typing-text::after {
content: '|';
animation: blink-cursor 0.8s infinite;
color: var(--accent);
}
@keyframes blink-cursor {
50% { opacity: 0; }
}
.illus-form-btn {
background: var(--accent);
color: white;
border: none;
border-radius: 6px;
padding: 0.45rem;
font-weight: 700;
font-size: 0.75rem;
text-align: center;
box-shadow: 0 4px 10px rgba(99, 102, 241, 0.3);
}
/* Step 3 Illustration: Multi-player Sync simulation */
.illus-players-container {
display: flex;
align-items: center;
gap: 1.5rem;
width: 90%;
}
.illus-mini-player {
flex: 1;
background: #0f172a;
border: 1px solid rgba(255,255,255,0.05);
border-radius: 10px;
height: 120px;
position: relative;
overflow: hidden;
}
.illus-video-screen {
width: 100%;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
background: radial-gradient(circle, rgba(99,102,241,0.15) 0%, transparent 80%);
}
.illus-play-icon {
width: 32px;
height: 32px;
border-radius: 50%;
background: rgba(99, 102, 241, 0.9);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.85rem;
box-shadow: 0 0 12px rgba(99, 102, 241, 0.5);
position: relative;
}
.illus-player-timeline {
height: 20px;
background: #1e293b;
display: flex;
align-items: center;
padding: 0 0.5rem;
border-top: 1px solid rgba(255,255,255,0.05);
}
.illus-timeline-bar {
flex: 1;
height: 3px;
background: rgba(255,255,255,0.1);
position: relative;
border-radius: 2px;
}
.illus-timeline-progress {
position: absolute;
top: 0; left: 0;
width: 60%;
height: 100%;
background: var(--accent);
border-radius: 2px;
animation: timeline-grow 4s infinite linear;
}
@keyframes timeline-grow {
0% { width: 0%; }
100% { width: 100%; }
}
.illus-sync-connection {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.25rem;
color: var(--accent);
font-size: 1.25rem;
}
.illus-sync-line {
width: 40px;
height: 2px;
background: linear-gradient(to right, var(--accent), var(--success));
position: relative;
}
.illus-sync-pulse {
position: absolute;
top: -3px;
left: 0;
width: 8px;
height: 8px;
background: #fff;
border-radius: 50%;
box-shadow: 0 0 8px #fff;
animation: slide-pulse 2s infinite linear;
}
@keyframes slide-pulse {
0% { left: 0%; }
100% { left: 100%; }
}
.illus-sync-label {
font-size: 0.55rem;
font-weight: 700;
background: rgba(34, 197, 94, 0.15);
color: #22c55e;
padding: 1px 6px;
border-radius: 4px;
border: 1px solid rgba(34, 197, 94, 0.25);
white-space: nowrap;
}
/* --- Self Hosters Terminal --- */
.terminal-container {
max-width: 850px;
margin: 3rem auto 0 auto;
background: #0f172a;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
overflow: hidden;
box-shadow: 0 20px 45px rgba(0,0,0,0.5);
text-align: left;
}
.terminal-header {
background: #1e293b;
padding: 0.85rem 1.25rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255,255,255,0.06);
}
.terminal-dots {
display: flex;
gap: 6px;
}
.terminal-dot {
width: 10px;
height: 10px;
border-radius: 50%;
}
.terminal-dot.red { background: #ef4444; }
.terminal-dot.yellow { background: #f59e0b; }
.terminal-dot.green { background: #10b981; }
.terminal-tabs {
display: flex;
gap: 0.5rem;
}
.terminal-tab-btn {
background: none;
border: none;
color: var(--text-muted);
font-weight: 600;
font-size: 0.8rem;
padding: 0.35rem 0.75rem;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
}
.terminal-tab-btn:hover {
color: var(--text);
background: rgba(255, 255, 255, 0.03);
}
.terminal-tab-btn.active {
color: var(--accent);
background: rgba(99, 102, 241, 0.1);
border: 1px solid rgba(99, 102, 241, 0.2);
}
.terminal-body {
position: relative;
padding: 1.5rem;
}
.terminal-pane {
display: none;
margin: 0;
}
.terminal-pane.active {
display: block;
}
.terminal-pane pre {
margin: 0;
overflow-x: auto;
}
.terminal-pane code {
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
font-size: 0.85rem;
line-height: 1.6;
color: #e2e8f0;
}
/* Syntax Highlighting */
.t-comment { color: #64748b; font-style: italic; }
.t-key { color: #f472b6; }
.t-val { color: #38bdf8; }
.t-str { color: #34d399; }
.t-num { color: #fbbf24; }
.terminal-copy-btn {
position: absolute;
top: 1rem;
right: 1.5rem;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
color: var(--text-muted);
padding: 0.35rem 0.75rem;
font-size: 0.75rem;
font-weight: 600;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 0.3rem;
}
.terminal-copy-btn:hover {
background: rgba(255, 255, 255, 0.08);
color: var(--text);
border-color: rgba(255, 255, 255, 0.15);
}
.terminal-copy-btn:active {
transform: scale(0.97);
}
/* --- Footer --- */
@@ -343,6 +1086,7 @@ footer {
transform: translateY(0);
}
/* --- Legal Pages --- */
.legal-content {
max-width: 800px;