Enhance forest scene with mid bamboo layer, butterflies, click confetti, and demo scene polish

This commit is contained in:
Timo
2026-07-11 06:08:12 +02:00
parent 21383b8b9c
commit 04df35bbe6
12 changed files with 1060 additions and 182 deletions
+16
View File
@@ -18,6 +18,14 @@
# # Clean URLs support (resolves /join to join.html, etc.) # # Clean URLs support (resolves /join to join.html, etc.)
# try_files {path} {path}.html {path}/ # try_files {path} {path}.html {path}/
# file_server # file_server
#
# # Serve the themed 404 page for unknown URLs (without this block,
# # Caddy returns an empty 404 response)
# handle_errors {
# @notfound expression {err.status_code} == 404
# rewrite @notfound /404.html
# file_server
# }
# } # }
# #
# syncserver.koalastuff.net { # syncserver.koalastuff.net {
@@ -57,6 +65,14 @@ sync.koalastuff.net {
try_files {path} {path}.html {path}/ try_files {path} {path}.html {path}/
file_server file_server
# Serve the themed 404 page for unknown URLs (without this block,
# Caddy returns an empty 404 response)
handle_errors {
@notfound expression {err.status_code} == 404
rewrite @notfound /404.html
file_server
}
# Static Caching for high-performance PageSpeed (1 year with validation) # Static Caching for high-performance PageSpeed (1 year with validation)
@static { @static {
file file
+169 -8
View File
@@ -3,6 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>KoalaSync</title> <title>KoalaSync</title>
<script src="theme-init.js"></script>
<style> <style>
@font-face { @font-face {
font-family: 'Twemoji Country Flags'; font-family: 'Twemoji Country Flags';
@@ -26,6 +27,7 @@
--accent-terracotta: oklch(0.62 0.14 45); --accent-terracotta: oklch(0.62 0.14 45);
--danger: oklch(0.55 0.15 25); --danger: oklch(0.55 0.15 25);
--text-on-green: oklch(0.14 0.02 90); --text-on-green: oklch(0.14 0.02 90);
--text-on-warm: oklch(0.16 0.025 45);
--bg: var(--surface-deep); --bg: var(--surface-deep);
--card: var(--surface); --card: var(--surface);
@@ -37,6 +39,25 @@
--error: var(--danger); --error: var(--danger);
--radius: 12px; --radius: 12px;
--star: oklch(0.68 0.14 45); --star: oklch(0.68 0.14 45);
color-scheme: dark;
}
html.theme-light {
--bg-base: oklch(0.965 0.012 105);
--surface: oklch(0.995 0.006 100);
--surface-deep: oklch(0.935 0.018 110);
--surface-alt: oklch(0.91 0.022 115);
--border-soft: oklch(0.28 0.035 140 / 0.10);
--border-strong: oklch(0.73 0.035 125);
--text-primary: oklch(0.21 0.03 140);
--text-secondary: oklch(0.43 0.03 135);
--accent-green: oklch(0.56 0.13 150);
--accent-green-hover: oklch(0.49 0.13 150);
--accent-terracotta: oklch(0.64 0.14 45);
--danger: oklch(0.57 0.17 25);
--text-on-green: oklch(0.16 0.03 140);
--text-on-warm: oklch(0.18 0.03 45);
color-scheme: light;
} }
body { body {
@@ -47,6 +68,16 @@
color: var(--text); color: var(--text);
font-family: 'Twemoji Country Flags', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-family: 'Twemoji Country Flags', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 14px; font-size: 14px;
background-image:
radial-gradient(circle at 12% -8%, oklch(0.68 0.13 150 / 0.12), transparent 42%),
radial-gradient(circle at 105% 12%, oklch(0.62 0.14 45 / 0.08), transparent 34%);
transition: background-color 0.25s ease, color 0.25s ease;
}
html.theme-light body {
background-image:
radial-gradient(circle at 8% -5%, oklch(0.72 0.11 145 / 0.24), transparent 40%),
radial-gradient(circle at 105% 5%, oklch(0.82 0.12 65 / 0.2), transparent 38%);
} }
.header-row { .header-row {
@@ -81,7 +112,7 @@
font-size: 10px; font-size: 10px;
color: var(--text-muted); color: var(--text-muted);
text-decoration: none; text-decoration: none;
opacity: 0.5; opacity: 0.72;
transition: opacity 0.2s, color 0.2s; transition: opacity 0.2s, color 0.2s;
} }
.popup-version:hover { .popup-version:hover {
@@ -113,7 +144,8 @@
cursor: pointer; cursor: pointer;
font-weight: 600; font-weight: 600;
border-radius: 8px; border-radius: 8px;
transition: all 0.2s; min-height: 36px;
transition: background-color 0.2s, color 0.2s, box-shadow 0.2s;
} }
.tab-btn.active { .tab-btn.active {
@@ -154,7 +186,7 @@
box-sizing: border-box; box-sizing: border-box;
background: var(--card); background: var(--card);
border: 1px solid var(--border-strong); border: 1px solid var(--border-strong);
color: white; color: var(--text);
padding: 10px; padding: 10px;
border-radius: 8px; border-radius: 8px;
outline: none; outline: none;
@@ -189,7 +221,7 @@
font-weight: 700; font-weight: 700;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: background-color 0.2s, box-shadow 0.2s, transform 0.2s;
margin-top: 8px; margin-top: 8px;
} }
@@ -203,13 +235,44 @@
padding: 10px; padding: 10px;
background: var(--border-strong); background: var(--border-strong);
border: none; border: none;
color: white; color: var(--text);
font-weight: 600; font-weight: 600;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
margin-top: 8px; margin-top: 8px;
} }
button.secondary:hover {
background: oklch(0.38 0.035 125);
}
details > summary:focus-visible {
outline: 2px solid var(--accent-hover) !important;
outline-offset: 3px;
border-radius: 4px;
}
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible {
outline: 2px solid var(--accent-hover);
outline-offset: 2px;
}
button:disabled {
cursor: not-allowed;
opacity: 0.48;
transform: none !important;
}
.copy-success {
background: var(--success) !important;
border-color: var(--success) !important;
color: var(--text-on-green) !important;
opacity: 1 !important;
}
/* Info Cards */ /* Info Cards */
.info-card { .info-card {
background: var(--card); background: var(--card);
@@ -289,7 +352,7 @@
.toast-success { background: var(--success); color: var(--text-on-green); } .toast-success { background: var(--success); color: var(--text-on-green); }
.toast-error { background: var(--error); color: white; } .toast-error { background: var(--error); color: white; }
.toast-info { background: var(--accent); color: var(--text-on-green); } .toast-info { background: var(--accent); color: var(--text-on-green); }
.toast-warning { background: var(--accent-terracotta); color: white; } .toast-warning { background: var(--accent-terracotta); color: var(--text-on-warm); }
@keyframes toastSlideIn { @keyframes toastSlideIn {
from { opacity: 0; transform: translateY(-20px); } from { opacity: 0; transform: translateY(-20px); }
@@ -300,6 +363,59 @@
to { opacity: 0; transform: translateY(-10px); } to { opacity: 0; transform: translateY(-10px); }
} }
/* Eucalyptus confetti: tiny leaves bursting from the copy-invite
button (spawned by popup.js, mirrors the website's effect). The
outer span owns the flight path — fast burst that brakes at its
peak, then sinks — while the inner leaf rocks around its stem. */
.click-leaf {
position: fixed;
z-index: 3000;
margin: -5px 0 0 -4px;
pointer-events: none;
opacity: 0;
animation: leafBurst var(--leaf-dur, 1s) forwards;
will-change: transform, opacity;
}
.click-leaf i {
display: block;
width: 7px;
height: 10px;
border-radius: 0 70% 0 70%;
background: linear-gradient(135deg, oklch(0.68 0.13 150 / 0.95), oklch(0.5 0.1 145 / 0.85));
transform: rotate(var(--leaf-rot, 0deg));
animation: leafRock var(--flutter-dur, 0.55s) ease-in-out infinite alternate;
}
.click-leaf-sage i {
background: linear-gradient(135deg, oklch(0.75 0.09 140 / 0.9), oklch(0.58 0.08 145 / 0.8));
}
.click-leaf-amber i {
background: linear-gradient(135deg, oklch(0.68 0.14 55 / 0.95), oklch(0.55 0.13 40 / 0.85));
}
@keyframes leafBurst {
0% {
opacity: 0;
transform: translate(0, 0) scale(var(--leaf-scale, 1));
animation-timing-function: cubic-bezier(0.16, 0.84, 0.44, 1);
}
10% { opacity: 1; }
45% {
opacity: 1;
transform: translate(var(--leaf-x, 0), var(--leaf-y, -14px)) scale(var(--leaf-scale, 1));
animation-timing-function: cubic-bezier(0.55, 0.06, 0.68, 0.19);
}
100% {
opacity: 0;
transform: translate(calc(var(--leaf-x, 0) * 1.4), calc(var(--leaf-y, -14px) + 30px)) scale(calc(var(--leaf-scale, 1) * 0.92));
}
}
@keyframes leafRock {
from { transform: rotate(calc(var(--leaf-rot, 0deg) - 24deg)); }
to { transform: rotate(calc(var(--leaf-rot, 0deg) + 24deg)); }
}
@media (prefers-reduced-motion: reduce) {
.click-leaf { display: none; }
}
@keyframes fadeSlideIn { @keyframes fadeSlideIn {
from { opacity: 0; transform: translateX(5px); } from { opacity: 0; transform: translateX(5px); }
to { opacity: 1; transform: translateX(0); } to { opacity: 1; transform: translateX(0); }
@@ -353,7 +469,7 @@
padding-top: 10px; padding-top: 10px;
color: var(--text-muted); color: var(--text-muted);
font-size: 10px; font-size: 10px;
opacity: 0.6; opacity: 0.78;
} }
.popup-footer a { .popup-footer a {
color: var(--text-muted); color: var(--text-muted);
@@ -364,6 +480,42 @@
color: var(--accent); color: var(--accent);
opacity: 1; opacity: 1;
} }
/* Light theme polish: preserve the same depth hierarchy without
carrying dark-only inline colors into form controls and cards. */
html.theme-light .tabs,
html.theme-light .info-card,
html.theme-light details.form-group,
html.theme-light .form-group {
box-shadow: 0 8px 24px oklch(0.22 0.035 140 / 0.07);
}
html.theme-light input,
html.theme-light select,
html.theme-light option {
color: var(--text) !important;
background-color: var(--surface) !important;
}
html.theme-light button.secondary {
background: var(--surface-alt);
color: var(--text);
border: 1px solid var(--border-strong);
}
html.theme-light button.secondary:hover {
background: oklch(0.86 0.035 125);
}
html.theme-light .mock-card,
html.theme-light .peer-item {
border-color: var(--border-strong);
}
html.theme-light .slider:before {
background-color: oklch(0.62 0.025 125);
}
html.theme-light input:checked + .slider:before {
background-color: white;
}
html.theme-light .popup-footer {
border-top-color: var(--border-soft);
}
.feature-hint { .feature-hint {
display: inline-block; display: inline-block;
width: 8px; width: 8px;
@@ -466,7 +618,7 @@
<label title="Share this link with friends so they can join" data-i18n="LABEL_INVITE_LINK" data-i18n-title="LABEL_INVITE_LINK_TOOLTIP">Invite Link</label> <label title="Share this link with friends so they can join" data-i18n="LABEL_INVITE_LINK" data-i18n-title="LABEL_INVITE_LINK_TOOLTIP">Invite Link</label>
<div class="invite-box"> <div class="invite-box">
<input type="text" id="inviteLink" readonly> <input type="text" id="inviteLink" readonly>
<button id="copyInvite" class="secondary">📋</button> <button id="copyInvite" class="secondary" title="Copy invite link" aria-label="Copy invite link">📋</button>
</div> </div>
</div> </div>
@@ -561,6 +713,15 @@
<!-- Settings Tab --> <!-- Settings Tab -->
<div id="tab-settings" class="tab-content"> <div id="tab-settings" class="tab-content">
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);">
<label id="themeLabel" style="margin-bottom: 0;" title="Follow the system theme or choose a fixed appearance">Appearance</label>
<select id="themeSelector" aria-label="Appearance" style="width: 165px; background: var(--bg); border: 1px solid var(--border-strong); color: var(--text); padding: 6px 10px; border-radius: 8px; font-size: 13px; cursor: pointer; outline: none; font-family: inherit;">
<option id="themeOptionSystem" value="system">💻 System</option>
<option id="themeOptionDark" value="dark">🌙 Dark</option>
<option id="themeOptionLight" value="light">☀️ Light</option>
</select>
</div>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);"> <div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);">
<label style="margin-bottom: 0;" title="Username helps others identify you." data-i18n="LABEL_USERNAME" data-i18n-title="LABEL_USERNAME_TOOLTIP">Your Username</label> <label style="margin-bottom: 0;" title="Username helps others identify you." data-i18n="LABEL_USERNAME" data-i18n-title="LABEL_USERNAME_TOOLTIP">Your Username</label>
<input type="text" id="username" data-i18n-placeholder="PLACEHOLDER_USERNAME" placeholder="Anonymous Koala" maxlength="20" style="width: 150px;"> <input type="text" id="username" data-i18n-placeholder="PLACEHOLDER_USERNAME" placeholder="Anonymous Koala" maxlength="20" style="width: 150px;">
+83 -12
View File
@@ -64,6 +64,7 @@ const elements = {
autoCopyInvite: document.getElementById('autoCopyInvite'), autoCopyInvite: document.getElementById('autoCopyInvite'),
cancelLobbyBtn: document.getElementById('cancelLobbyBtn'), cancelLobbyBtn: document.getElementById('cancelLobbyBtn'),
langSelector: document.getElementById('langSelector'), langSelector: document.getElementById('langSelector'),
themeSelector: document.getElementById('themeSelector'),
audioSettingsLink: document.getElementById('audioSettingsLink'), audioSettingsLink: document.getElementById('audioSettingsLink'),
settingsSupportLink: document.getElementById('settingsSupportLink'), settingsSupportLink: document.getElementById('settingsSupportLink'),
@@ -132,6 +133,29 @@ function openAudioSettingsPage() {
chrome.tabs.create({ url: chrome.runtime.getURL('audio-options.html') }); chrome.tabs.create({ url: chrome.runtime.getURL('audio-options.html') });
} }
function localizeThemeSelector(locale) {
const labels = {
de: ['Darstellung', 'System', 'Dunkel', 'Hell'], fr: ['Apparence', 'Système', 'Sombre', 'Clair'],
es: ['Apariencia', 'Sistema', 'Oscuro', 'Claro'], it: ['Aspetto', 'Sistema', 'Scuro', 'Chiaro'],
nl: ['Weergave', 'Systeem', 'Donker', 'Licht'], pl: ['Wygląd', 'System', 'Ciemny', 'Jasny'],
pt: ['Aparência', 'Sistema', 'Escuro', 'Claro'], 'pt-BR': ['Aparência', 'Sistema', 'Escuro', 'Claro'],
tr: ['Görünüm', 'Sistem', 'Koyu', 'Açık'], ru: ['Оформление', 'Системная', 'Тёмная', 'Светлая'],
ja: ['外観', 'システム', 'ダーク', 'ライト'], ko: ['테마', '시스템', '다크', '라이트'],
zh: ['外观', '跟随系统', '深色', '浅色'], uk: ['Вигляд', 'Системна', 'Темна', 'Світла'],
en: ['Appearance', 'System', 'Dark', 'Light']
};
const [label, system, dark, light] = labels[locale] || labels.en;
const labelEl = document.getElementById('themeLabel');
const systemEl = document.getElementById('themeOptionSystem');
const darkEl = document.getElementById('themeOptionDark');
const lightEl = document.getElementById('themeOptionLight');
if (labelEl) labelEl.textContent = label;
if (systemEl) systemEl.textContent = `💻 ${system}`;
if (darkEl) darkEl.textContent = `🌙 ${dark}`;
if (lightEl) lightEl.textContent = `☀️ ${light}`;
if (elements.themeSelector) elements.themeSelector.setAttribute('aria-label', label);
}
async function updateFeatureHints() { async function updateFeatureHints() {
const data = await chrome.storage.sync.get(['dismissedHints']); const data = await chrome.storage.sync.get(['dismissedHints']);
const dismissed = Array.isArray(data.dismissedHints) ? data.dismissedHints : []; const dismissed = Array.isArray(data.dismissedHints) ? data.dismissedHints : [];
@@ -204,7 +228,7 @@ function setRoomRefreshCooldown() {
async function init() { async function init() {
// Local-only by design — settings and room credentials never come from // Local-only by design — settings and room credentials never come from
// storage.sync (only onboardingComplete + dismissedHints live there). // storage.sync (only onboardingComplete + dismissedHints live there).
const localData = await chrome.storage.local.get(['serverUrl', 'useCustomServer', 'roomId', 'password', 'username', 'filterNoise', 'autoSyncNextEpisode', 'sendTabTitle', 'mediaTitlePrivacyMode', 'titlePrivacyMode', 'forceSyncMode', 'browserNotifications', 'autoCopyInvite', 'locale', 'audioSettings', 'activeTab']); const localData = await chrome.storage.local.get(['serverUrl', 'useCustomServer', 'roomId', 'password', 'username', 'filterNoise', 'autoSyncNextEpisode', 'sendTabTitle', 'mediaTitlePrivacyMode', 'titlePrivacyMode', 'forceSyncMode', 'browserNotifications', 'autoCopyInvite', 'locale', 'audioSettings', 'activeTab', 'themeMode']);
let activeLang = localData.locale; let activeLang = localData.locale;
if (!activeLang) { if (!activeLang) {
@@ -214,8 +238,10 @@ async function init() {
await loadLocale(activeLang); await loadLocale(activeLang);
translateDOM(); translateDOM();
localizeThemeSelector(activeLang);
if (elements.langSelector) elements.langSelector.value = activeLang; if (elements.langSelector) elements.langSelector.value = activeLang;
if (elements.themeSelector) elements.themeSelector.value = ['dark', 'light'].includes(localData.themeMode) ? localData.themeMode : 'system';
let username = localData.username; let username = localData.username;
if (!username) { if (!username) {
@@ -538,7 +564,7 @@ function updateLastActionUI(state, peers) {
peerItem.style.cssText = `display:flex; flex-direction:column; align-items:center; opacity: ${isAcked ? 1 : 0.6};`; peerItem.style.cssText = `display:flex; flex-direction:column; align-items:center; opacity: ${isAcked ? 1 : 0.6};`;
const dot = document.createElement('div'); const dot = document.createElement('div');
dot.style.cssText = `width:18px; height:18px; border-radius:50%; background:${color}; color:white; display:flex; align-items:center; justify-content:center; font-size:9px; font-weight:bold; margin-bottom:1px;`; dot.style.cssText = `width:18px; height:18px; border-radius:50%; background:${color}; color:${isAcked ? 'var(--text-on-green)' : 'var(--text-primary)'}; display:flex; align-items:center; justify-content:center; font-size:9px; font-weight:bold; margin-bottom:1px;`;
dot.textContent = icon; dot.textContent = icon;
const nameSpan = document.createElement('span'); const nameSpan = document.createElement('span');
@@ -630,7 +656,10 @@ function renderEmpty(container, type) {
copyBtn.title = getMessage('BTN_COPY_INVITE_TOOLTIP'); copyBtn.title = getMessage('BTN_COPY_INVITE_TOOLTIP');
copyBtn.addEventListener('click', () => { copyBtn.addEventListener('click', () => {
navigator.clipboard.writeText(elements.inviteLink.value) navigator.clipboard.writeText(elements.inviteLink.value)
.then(() => showToast(getMessage('TOAST_INVITE_COPIED'), 'success', 2000)) .then(() => {
showToast(getMessage('TOAST_INVITE_COPIED'), 'success', 2000);
spawnLeafBurst(copyBtn);
})
.catch(() => showToast(getMessage('TOAST_COPY_FAILED'), 'error')); .catch(() => showToast(getMessage('TOAST_COPY_FAILED'), 'error'));
}); });
wrapper.appendChild(copyBtn); wrapper.appendChild(copyBtn);
@@ -725,7 +754,7 @@ function updatePeerList(peers) {
// Host Control Mode: show "Solo" badge for peers watching on their own. // Host Control Mode: show "Solo" badge for peers watching on their own.
if (typeof p === 'object' && p.desynced) { if (typeof p === 'object' && p.desynced) {
const solo = document.createElement('span'); const solo = document.createElement('span');
solo.style.cssText = 'font-size:10px; color:#fff; background:#c96736; padding:2px 6px; border-radius:6px; font-weight:600;'; solo.style.cssText = 'font-size:10px; color:var(--text-on-warm); background:var(--accent-terracotta); padding:2px 6px; border-radius:6px; font-weight:700;';
const soloText = getMessage('BADGE_DESYNCED') || 'Solo'; const soloText = getMessage('BADGE_DESYNCED') || 'Solo';
solo.textContent = soloText; solo.textContent = soloText;
solo.title = getMessage('TOOLTIP_PEER_DESYNCED') || soloText; solo.title = getMessage('TOOLTIP_PEER_DESYNCED') || soloText;
@@ -742,7 +771,7 @@ function updatePeerList(peers) {
const isController = !isOwner && hcmControllers.includes(pId); const isController = !isOwner && hcmControllers.includes(pId);
if (isOwner || isController) { if (isOwner || isController) {
const roleBadge = document.createElement('span'); const roleBadge = document.createElement('span');
roleBadge.style.cssText = 'font-size:10px; color:#fff; background:var(--accent); padding:2px 6px; border-radius:6px; font-weight:600;'; roleBadge.style.cssText = 'font-size:10px; color:var(--text-on-green); background:var(--accent); padding:2px 6px; border-radius:6px; font-weight:700;';
roleBadge.textContent = isOwner ? (getMessage('BADGE_HOST') || 'Host') : (getMessage('BADGE_CONTROLLER') || 'Controller'); roleBadge.textContent = isOwner ? (getMessage('BADGE_HOST') || 'Host') : (getMessage('BADGE_CONTROLLER') || 'Controller');
rightGroup.appendChild(roleBadge); rightGroup.appendChild(roleBadge);
} }
@@ -755,7 +784,7 @@ function updatePeerList(peers) {
btn.title = revoke ? (getMessage('BTN_REVOKE_CONTROL') || 'Revoke') : (getMessage('BTN_GIVE_CONTROL') || 'Give control'); btn.title = revoke ? (getMessage('BTN_REVOKE_CONTROL') || 'Revoke') : (getMessage('BTN_GIVE_CONTROL') || 'Give control');
btn.addEventListener('mouseenter', () => { btn.addEventListener('mouseenter', () => {
btn.style.background = revoke ? 'var(--text-muted)' : 'var(--accent)'; btn.style.background = revoke ? 'var(--text-muted)' : 'var(--accent)';
btn.style.color = '#fff'; btn.style.color = 'var(--text-on-green)';
}); });
btn.addEventListener('mouseleave', () => { btn.addEventListener('mouseleave', () => {
btn.style.background = 'transparent'; btn.style.background = 'transparent';
@@ -1273,6 +1302,14 @@ if (elements.autoCopyInvite) {
}); });
} }
if (elements.themeSelector) {
elements.themeSelector.addEventListener('change', () => {
const themeMode = elements.themeSelector.value;
window.koalaTheme?.setMode(themeMode);
chrome.storage.local.set({ themeMode });
});
}
if (elements.audioSettingsLink) { if (elements.audioSettingsLink) {
elements.audioSettingsLink.addEventListener('click', async (event) => { elements.audioSettingsLink.addEventListener('click', async (event) => {
event.preventDefault(); event.preventDefault();
@@ -1305,6 +1342,7 @@ if (elements.langSelector) {
await chrome.storage.local.set({ locale: selectedLang }); await chrome.storage.local.set({ locale: selectedLang });
await loadLocale(selectedLang); await loadLocale(selectedLang);
translateDOM(); translateDOM();
localizeThemeSelector(selectedLang);
configureFooterLinks(); configureFooterLinks();
await updateFeatureHints(); await updateFeatureHints();
@@ -1389,6 +1427,39 @@ function showToast(message, type = 'info', duration = 3000) {
setTimeout(() => toast.remove(), duration); setTimeout(() => toast.remove(), duration);
} }
// Eucalyptus confetti: a few tiny leaves burst from the given element
// (matches the website's copy-invite moment). Subtle by design — few,
// tiny, short-lived — and skipped entirely under reduced motion.
function spawnLeafBurst(sourceEl) {
if (!sourceEl || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const rect = sourceEl.getBoundingClientRect();
if (!rect.width) return;
const originY = rect.top + rect.height / 2;
const count = 4 + Math.floor(Math.random() * 3);
for (let i = 0; i < count; i++) {
const leaf = document.createElement('span');
// Three tones of the palette: deep green, sage, a dash of amber
const tone = Math.random();
leaf.className = 'click-leaf' + (tone < 0.25 ? ' click-leaf-amber' : tone < 0.55 ? ' click-leaf-sage' : '');
// Fan out upward to a burst peak spread across the button's width;
// the keyframes brake there and let each leaf sink while it rocks.
const angle = (-90 + (Math.random() - 0.5) * 150) * Math.PI / 180;
const dist = 18 + Math.random() * 24;
const dur = 0.85 + Math.random() * 0.4;
leaf.style.left = (rect.left + 4 + Math.random() * Math.max(1, rect.width - 8)) + 'px';
leaf.style.top = (originY - rect.height * 0.2) + 'px';
leaf.style.setProperty('--leaf-x', (Math.cos(angle) * dist).toFixed(1) + 'px');
leaf.style.setProperty('--leaf-y', (Math.sin(angle) * dist).toFixed(1) + 'px');
leaf.style.setProperty('--leaf-rot', ((Math.random() - 0.5) * 160).toFixed(0) + 'deg');
leaf.style.setProperty('--leaf-scale', (0.55 + Math.random() * 0.55).toFixed(2));
leaf.style.setProperty('--leaf-dur', dur.toFixed(2) + 's');
leaf.style.setProperty('--flutter-dur', (0.35 + Math.random() * 0.3).toFixed(2) + 's');
leaf.appendChild(document.createElement('i'));
document.body.appendChild(leaf);
setTimeout(() => leaf.remove(), dur * 1000 + 150);
}
}
function showError(msg) { function showError(msg) {
if (!elements.roomError) return; if (!elements.roomError) return;
const currentToken = ++errorToken; const currentToken = ++errorToken;
@@ -1775,13 +1846,12 @@ elements.copyInvite.addEventListener('click', () => {
navigator.clipboard.writeText(elements.inviteLink.value).then(() => { navigator.clipboard.writeText(elements.inviteLink.value).then(() => {
const original = elements.copyInvite.textContent; const original = elements.copyInvite.textContent;
elements.copyInvite.textContent = '✓'; elements.copyInvite.textContent = '✓';
elements.copyInvite.style.background = 'var(--success)'; elements.copyInvite.classList.add('copy-success');
elements.copyInvite.style.color = 'white';
showToast(getMessage('TOAST_INVITE_COPIED'), 'success', 2000); showToast(getMessage('TOAST_INVITE_COPIED'), 'success', 2000);
spawnLeafBurst(elements.copyInvite);
setTimeout(() => { setTimeout(() => {
elements.copyInvite.textContent = original; elements.copyInvite.textContent = original;
elements.copyInvite.style.background = ''; elements.copyInvite.classList.remove('copy-success');
elements.copyInvite.style.color = '';
}, 2000); }, 2000);
}).catch(() => { }).catch(() => {
showToast(getMessage('TOAST_COPY_FAILED'), 'error'); showToast(getMessage('TOAST_COPY_FAILED'), 'error');
@@ -1793,11 +1863,12 @@ if (elements.syncTabCopyInvite) {
navigator.clipboard.writeText(elements.inviteLink.value).then(() => { navigator.clipboard.writeText(elements.inviteLink.value).then(() => {
const original = elements.syncTabCopyInvite.textContent; const original = elements.syncTabCopyInvite.textContent;
elements.syncTabCopyInvite.textContent = '✓'; elements.syncTabCopyInvite.textContent = '✓';
elements.syncTabCopyInvite.style.color = 'var(--success)'; elements.syncTabCopyInvite.classList.add('copy-success');
showToast(getMessage('TOAST_INVITE_COPIED'), 'success', 2000); showToast(getMessage('TOAST_INVITE_COPIED'), 'success', 2000);
spawnLeafBurst(elements.syncTabCopyInvite);
setTimeout(() => { setTimeout(() => {
elements.syncTabCopyInvite.textContent = original; elements.syncTabCopyInvite.textContent = original;
elements.syncTabCopyInvite.style.color = ''; elements.syncTabCopyInvite.classList.remove('copy-success');
}, 2000); }, 2000);
}); });
}); });
+29
View File
@@ -0,0 +1,29 @@
(() => {
const systemTheme = window.matchMedia('(prefers-color-scheme: light)');
let mode = 'system';
const normalize = value => ['system', 'dark', 'light'].includes(value) ? value : 'system';
const apply = value => {
mode = normalize(value);
const light = mode === 'light' || (mode === 'system' && systemTheme.matches);
document.documentElement.classList.toggle('theme-light', light);
document.documentElement.dataset.theme = light ? 'light' : 'dark';
document.documentElement.style.colorScheme = light ? 'light' : 'dark';
};
apply('system');
window.koalaTheme = {
getMode: () => mode,
setMode: apply
};
systemTheme.addEventListener('change', () => {
if (mode === 'system') apply('system');
});
if (!globalThis.chrome?.storage?.local) return;
chrome.storage.local.get(['themeMode'], data => apply(data.themeMode));
chrome.storage.onChanged.addListener((changes, area) => {
if (area === 'local' && changes.themeMode) apply(changes.themeMode.newValue);
});
})();
+146
View File
@@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="en" class="lang-en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 Page Not Found | KoalaSync</title>
<!-- Absolute asset paths: Caddy rewrites any missing URL (also nested ones
like /foo/bar) to this file, so relative paths would break. -->
<link rel="preload" href="/style.min.css" as="style">
<link rel="stylesheet" href="/style.min.css">
<link rel="icon" type="image/webp" href="/assets/NewLogoIcon_64.webp">
<meta name="description" content="This page could not be found.">
<meta name="robots" content="noindex, follow">
<!-- Mobile Browser Theme Styling -->
<meta name="theme-color" content="#10190e">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<script src="/lang-init.min.js"></script>
</head>
<body>
<div class="bg-nature" aria-hidden="true">
<div class="bg-dusk-tint"></div>
<div class="bg-depth-day">
<div class="bg-godrays">
<span class="godray godray-1"></span>
<span class="godray godray-2"></span>
<span class="godray godray-3"></span>
</div>
<div class="bg-canopy"></div>
<span class="canopy-arc canopy-arc-1"></span>
<span class="canopy-arc canopy-arc-2"></span>
<span class="canopy-arc canopy-arc-3"></span>
<span class="canopy-arc canopy-arc-4"></span>
</div>
<div id="bamboo-far">
<span class="bamboo-stalk far-1"></span>
<span class="bamboo-stalk far-2"></span>
<span class="bamboo-stalk far-3"></span>
<span class="bamboo-stalk far-4"></span>
<span class="bamboo-stalk far-5"></span>
</div>
<div id="bamboo-near">
<div class="bamboo-stalk near-1">
<i class="bamboo-node node-1"></i>
<i class="bamboo-node node-2"></i>
<i class="bamboo-node node-3"></i>
<i class="bamboo-leaf leaf-1"></i>
<i class="bamboo-leaf leaf-2"></i>
</div>
<div class="bamboo-stalk near-2">
<i class="bamboo-node node-1"></i>
<i class="bamboo-node node-2"></i>
<i class="bamboo-node node-3"></i>
<i class="bamboo-leaf leaf-1"></i>
</div>
<div class="bamboo-stalk near-3">
<i class="bamboo-node node-1"></i>
<i class="bamboo-node node-2"></i>
<i class="bamboo-node node-3"></i>
<i class="bamboo-leaf leaf-1"></i>
</div>
</div>
<span class="fall-leaf fall-leaf-1"><i></i></span>
<span class="fall-leaf fall-leaf-2"><i></i></span>
<span class="fall-leaf fall-leaf-3"><i></i></span>
<span class="fall-leaf fall-leaf-4"><i></i></span>
<span class="fall-leaf fall-leaf-5"><i></i></span>
<span class="fall-leaf fall-leaf-6"><i></i></span>
<span class="fall-leaf fall-leaf-7"><i></i></span>
<span class="fall-leaf fall-leaf-8"><i></i></span>
<span class="bg-grass bg-grass-1"></span>
<span class="bg-grass bg-grass-2"></span>
<span class="bg-grass bg-grass-3"></span>
<span class="bg-grass bg-grass-4"></span>
<div class="bg-depth-dusk">
<div class="bg-mist bg-mist-1"></div>
<div class="bg-mist bg-mist-2"></div>
<div class="bg-horizon"></div>
<span class="firefly-wrap fw-1"><span class="firefly"></span></span>
<span class="firefly-wrap fw-2"><span class="firefly"></span></span>
<span class="firefly-wrap fw-3"><span class="firefly"></span></span>
<span class="firefly-wrap fw-4"><span class="firefly"></span></span>
<span class="firefly-wrap fw-5"><span class="firefly"></span></span>
<span class="firefly-wrap fw-6"><span class="firefly"></span></span>
</div>
<div class="bg-light-sweep"></div>
<div class="bg-grain"></div>
</div>
<nav>
<div class="container nav-content">
<a href="/" class="logo-area" style="text-decoration: none;">
<img src="/assets/NewLogoIcon.webp" alt="KoalaSync Logo" width="40" height="40">
<span>KoalaSync</span>
</a>
<div class="nav-links">
<a href="/" 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" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
<span>Home</span>
</a>
<a href="https://github.com/Shik3i/KoalaSync" target="_blank" rel="noopener" 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" aria-hidden="true" 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>
</div>
</div>
</nav>
<main class="legal-content">
<div class="legal-card" data-reveal style="padding: 2.5rem 2rem; text-align: center;">
<div style="display: flex; justify-content: center;">
<img src="/assets/KoalaSearching.webp" alt="A cute koala looking through a telescope, searching for the missing page" class="legal-mascot" width="180" height="180">
</div>
<h1 style="margin-bottom: 0.5rem;">404</h1>
<p style="text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.8rem; border-bottom: 1px solid var(--glass-border); padding-bottom: 1.5rem; margin-bottom: 1.5rem;">
Page not found
</p>
<p style="max-width: 34rem; margin: 0 auto;">
This page wandered off into the bamboo forest. Our koala is still looking for it — in the meantime, head back to safety.
</p>
<div style="display: flex; justify-content: center; gap: 1rem; margin-top: 2rem; flex-wrap: wrap;">
<a href="/" class="btn btn-primary">
Back to home
</a>
<a href="https://github.com/Shik3i/KoalaSync/issues" target="_blank" rel="noopener" class="btn btn-secondary">
Report a broken link
</a>
</div>
</div>
</main>
<footer>
<div class="container">
<p>&copy; 2026 KoalaSync. Open source under the MIT License.</p>
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<a href="/imprint" style="color: var(--text-muted); text-decoration: none;">Legal Notice</a>
<a href="/privacy" style="color: var(--text-muted); text-decoration: none;">Privacy Policy</a>
</div>
</div>
</footer>
<script src="/app.min.js"></script>
</body>
</html>
+7
View File
@@ -75,6 +75,13 @@ sync.koalastuff.net {
file_server file_server
encode zstd gzip encode zstd gzip
# Themed 404 page (Caddy returns an empty 404 without this)
handle_errors {
@notfound expression {err.status_code} == 404
rewrite @notfound /404.html
file_server
}
@static { @static {
file file
path *.ico *.css *.js *.png *.svg *.webp *.avif path *.ico *.css *.js *.png *.svg *.webp *.avif
+91 -4
View File
@@ -184,6 +184,19 @@ document.addEventListener('DOMContentLoaded', () => {
// Scroll Reveal Logic (IntersectionObserver for performance) // Scroll Reveal Logic (IntersectionObserver for performance)
const revealElements = document.querySelectorAll('[data-reveal]'); const revealElements = document.querySelectorAll('[data-reveal]');
// Hash navigation must never land on an intentionally transparent state.
// Reveal the destination before the smooth scroll begins; the observer
// continues to animate sections reached through ordinary scrolling.
const revealHashTarget = () => {
const id = decodeURIComponent(window.location.hash.slice(1));
const target = id ? document.getElementById(id) : null;
if (!target) return;
if (target.matches('[data-reveal]')) target.classList.add('revealed');
target.querySelectorAll('[data-reveal]').forEach(el => el.classList.add('revealed'));
};
revealHashTarget();
window.addEventListener('hashchange', revealHashTarget);
if ('IntersectionObserver' in window) { if ('IntersectionObserver' in window) {
const revealObserver = new IntersectionObserver((entries) => { const revealObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => { entries.forEach(entry => {
@@ -193,7 +206,7 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}); });
}, { }, {
rootMargin: '0px 0px -30px 0px', rootMargin: '0px 0px 12% 0px',
threshold: 0.05 threshold: 0.05
}); });
@@ -231,6 +244,7 @@ document.addEventListener('DOMContentLoaded', () => {
// value crossfades the scene from sunlit canopy into misty dusk, and on // value crossfades the scene from sunlit canopy into misty dusk, and on
// pointer devices the layers tilt slightly toward the cursor. // pointer devices the layers tilt slightly toward the cursor.
const bambooFar = document.getElementById('bamboo-far'); const bambooFar = document.getElementById('bamboo-far');
const bambooMid = document.getElementById('bamboo-mid');
const bambooNear = document.getElementById('bamboo-near'); const bambooNear = document.getElementById('bamboo-near');
const forestScene = document.querySelector('.bg-nature'); const forestScene = document.querySelector('.bg-nature');
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
@@ -241,6 +255,7 @@ document.addEventListener('DOMContentLoaded', () => {
const fireflies = Array.from(document.querySelectorAll('.firefly-wrap'), (el) => ({ el, x: 0, y: 0 })); const fireflies = Array.from(document.querySelectorAll('.firefly-wrap'), (el) => ({ el, x: 0, y: 0 }));
const canHover = window.matchMedia('(hover: hover)').matches; const canHover = window.matchMedia('(hover: hover)').matches;
let mouseX = 0; let mouseX = 0;
let mouseY = 0;
let pointerX = -9999; let pointerX = -9999;
let pointerY = -9999; let pointerY = -9999;
let depthStep = ''; let depthStep = '';
@@ -251,9 +266,16 @@ document.addEventListener('DOMContentLoaded', () => {
const doc = document.documentElement; const doc = document.documentElement;
const maxScroll = Math.max(1, doc.scrollHeight - window.innerHeight); const maxScroll = Math.max(1, doc.scrollHeight - window.innerHeight);
const depth = Math.min(1, window.scrollY / maxScroll); const depth = Math.min(1, window.scrollY / maxScroll);
const shift = window.innerHeight * 0.14; // Couple the forest directly to the scroll distance so it visibly
bambooFar.style.transform = `translate(${(-mouseX * 5).toFixed(1)}px, ${(depth * shift).toFixed(1)}px)`; // travels down instead of being overpowered by upward-moving content.
bambooNear.style.transform = `translate(${(mouseX * 11).toFixed(1)}px, ${(-depth * shift).toFixed(1)}px)`; const shift = Math.min(window.innerHeight * 0.55, window.scrollY * 0.14);
// All bamboo layers travel in the same vertical direction. Depth
// comes from their different speeds; reversing one layer makes
// the forest look mechanically disconnected while scrolling.
bambooFar.style.transform = `translate(${(-mouseX * 5).toFixed(1)}px, ${(shift * 0.42).toFixed(1)}px)`;
bambooNear.style.transform = `translate(${(mouseX * 11).toFixed(1)}px, ${shift.toFixed(1)}px)`;
if (bambooMid) bambooMid.style.transform = `translate(${(mouseX * 4).toFixed(1)}px, ${(shift * 0.7).toFixed(1)}px)`;
if (depthDay) depthDay.style.transform = `translate(${(-mouseX * 2.5).toFixed(1)}px, ${(mouseY * 1.5).toFixed(1)}px)`;
if (depthDay) depthDay.style.opacity = (1 - depth * 0.75).toFixed(3); if (depthDay) depthDay.style.opacity = (1 - depth * 0.75).toFixed(3);
if (depthDusk) depthDusk.style.opacity = (0.35 + depth * 0.65).toFixed(3); if (depthDusk) depthDusk.style.opacity = (0.35 + depth * 0.65).toFixed(3);
if (duskTint) duskTint.style.opacity = (depth * 0.85).toFixed(3); if (duskTint) duskTint.style.opacity = (depth * 0.85).toFixed(3);
@@ -316,6 +338,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (canHover) { if (canHover) {
window.addEventListener('pointermove', (e) => { window.addEventListener('pointermove', (e) => {
mouseX = (e.clientX / window.innerWidth - 0.5) * 2; mouseX = (e.clientX / window.innerWidth - 0.5) * 2;
mouseY = (e.clientY / window.innerHeight - 0.5) * 2;
pointerX = e.clientX; pointerX = e.clientX;
pointerY = e.clientY; pointerY = e.clientY;
requestForestFrame(); requestForestFrame();
@@ -345,6 +368,68 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} }
// Eucalyptus click confetti: leaves burst from wherever the visitor
// presses. Buttons and other controls shed leaves along their whole
// width; anywhere else a small fan rises from the pointer itself. The
// hero walkthrough's scripted cursor calls burstFromElement directly so
// its fake clicks scatter leaves too. Rate-capped, pointer-events:none —
// it can never block a click or flood the DOM.
let burstFromElement = () => {};
if (!prefersReducedMotion) {
const spawnLeaf = (x, y) => {
const leaf = document.createElement('span');
// Three tones of the palette: deep green, sage, a dash of amber
const tone = Math.random();
leaf.className = 'click-leaf' + (tone < 0.25 ? ' click-leaf-amber' : tone < 0.55 ? ' click-leaf-sage' : '');
// Fan out upward to a burst peak; the keyframes brake there and
// let the leaf sink past it while the inner element rocks.
const angle = (-90 + (Math.random() - 0.5) * 150) * Math.PI / 180;
const dist = 26 + Math.random() * 38;
const dur = 0.9 + Math.random() * 0.5;
leaf.style.left = x + 'px';
leaf.style.top = y + 'px';
leaf.style.setProperty('--leaf-x', (Math.cos(angle) * dist).toFixed(1) + 'px');
leaf.style.setProperty('--leaf-y', (Math.sin(angle) * dist).toFixed(1) + 'px');
leaf.style.setProperty('--leaf-rot', ((Math.random() - 0.5) * 160).toFixed(0) + 'deg');
leaf.style.setProperty('--leaf-scale', (0.55 + Math.random() * 0.6).toFixed(2));
leaf.style.setProperty('--leaf-dur', dur.toFixed(2) + 's');
leaf.style.setProperty('--flutter-dur', (0.4 + Math.random() * 0.35).toFixed(2) + 's');
leaf.appendChild(document.createElement('i'));
document.body.appendChild(leaf);
setTimeout(() => leaf.remove(), dur * 1000 + 150);
};
burstFromElement = (el) => {
if (!el) return;
const r = el.getBoundingClientRect();
if (!r.width || r.bottom < 0 || r.top > window.innerHeight) return;
// Leaf count scales with the control's width so a wide demo card
// sheds a few more than a compact icon button, capped for subtlety.
const count = Math.max(4, Math.min(9, Math.round(r.width / 26)));
for (let i = 0; i < count; i++) {
const x = r.left + 4 + Math.random() * Math.max(1, r.width - 8);
// Spawn along the upper edge, like leaves knocked off the top
const y = r.top + r.height * (0.1 + Math.random() * 0.4);
spawnLeaf(x, y);
}
};
let lastBurst = 0;
document.addEventListener('pointerdown', (e) => {
if (e.button !== 0 || !e.isPrimary) return;
const now = Date.now();
if (now - lastBurst < 180) return;
lastBurst = now;
const control = e.target.closest('button, [role="button"], .btn, .mock-tab, select, .demo-progress');
if (control) {
burstFromElement(control);
} else {
const count = 4 + Math.floor(Math.random() * 3);
for (let i = 0; i < count; i++) spawnLeaf(e.clientX, e.clientY);
}
}, { passive: true });
}
// Invite Detection & Bridge // Invite Detection & Bridge
const checkInvite = () => { const checkInvite = () => {
const isJoinPage = window.location.pathname.includes('join'); const isJoinPage = window.location.pathname.includes('join');
@@ -1136,6 +1221,8 @@ document.addEventListener('DOMContentLoaded', () => {
await wait(180); await wait(180);
if (userTookOver || demoRunId !== myRunId) return false; if (userTookOver || demoRunId !== myRunId) return false;
if (action) action(); else el.click(); if (action) action(); else el.click();
// The scripted cursor's clicks scatter leaves like real ones
burstFromElement(el);
await wait(320 + (pause || 0)); await wait(320 + (pause || 0));
return !userTookOver && demoRunId === myRunId; return !userTookOver && demoRunId === myRunId;
}; };
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

+7 -4
View File
@@ -371,7 +371,8 @@ async function compile() {
{ src: 'impressum-de.html', dest: 'de/impressum.html' }, { src: 'impressum-de.html', dest: 'de/impressum.html' },
{ src: 'datenschutz-de.html', dest: 'de/datenschutz.html' }, { src: 'datenschutz-de.html', dest: 'de/datenschutz.html' },
{ src: 'impressum.html', dest: 'impressum.html' }, { src: 'impressum.html', dest: 'impressum.html' },
{ src: 'datenschutz.html', dest: 'datenschutz.html' } { src: 'datenschutz.html', dest: 'datenschutz.html' },
{ src: '404.html', dest: '404.html' }
]; ];
for (const mapping of staticMappings) { for (const mapping of staticMappings) {
const src = path.join(websiteDir, mapping.src); const src = path.join(websiteDir, mapping.src);
@@ -457,13 +458,15 @@ async function compile() {
let html = fs.readFileSync(filePath, 'utf8'); let html = fs.readFileSync(filePath, 'utf8');
// 8a. Replace hashed asset refs and inject SRI (Subresource Integrity) // 8a. Replace hashed asset refs and inject SRI (Subresource Integrity)
html = html.replace(/(<link\b[^>]*?\bhref=")((?:\.\.\/)*)style\.min\.css"/g, (m, before, prefix) => { // The prefix is either any number of "../" segments or a single "/"
// (404.html must use absolute paths: it is served for arbitrary URLs).
html = html.replace(/(<link\b[^>]*?\bhref=")((?:\.\.\/)*\/?)style\.min\.css"/g, (m, before, prefix) => {
return `${before}${prefix}${styleName}" integrity="${styleSRI}" crossorigin="anonymous"`; return `${before}${prefix}${styleName}" integrity="${styleSRI}" crossorigin="anonymous"`;
}); });
html = html.replace(/(<script\b[^>]*?\bsrc=")((?:\.\.\/)*)app\.min\.js"/g, (m, before, prefix) => { html = html.replace(/(<script\b[^>]*?\bsrc=")((?:\.\.\/)*\/?)app\.min\.js"/g, (m, before, prefix) => {
return `${before}${prefix}${appName}" integrity="${appSRI}" crossorigin="anonymous"`; return `${before}${prefix}${appName}" integrity="${appSRI}" crossorigin="anonymous"`;
}); });
html = html.replace(/(<script\b[^>]*?\bsrc=")((?:\.\.\/)*)lang-init\.min\.js"/g, (m, before, prefix) => { html = html.replace(/(<script\b[^>]*?\bsrc=")((?:\.\.\/)*\/?)lang-init\.min\.js"/g, (m, before, prefix) => {
return `${before}${prefix}${langName}" integrity="${langSRI}" crossorigin="anonymous"`; return `${before}${prefix}${langName}" integrity="${langSRI}" crossorigin="anonymous"`;
}); });
+124 -124
View File
@@ -3,31 +3,31 @@
xmlns:xhtml="http://www.w3.org/1999/xhtml"> xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url> <url>
<loc>https://sync.koalastuff.net/imprint</loc> <loc>https://sync.koalastuff.net/imprint</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>monthly</changefreq> <changefreq>monthly</changefreq>
<priority>0.3</priority> <priority>0.3</priority>
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/privacy</loc> <loc>https://sync.koalastuff.net/privacy</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>monthly</changefreq> <changefreq>monthly</changefreq>
<priority>0.3</priority> <priority>0.3</priority>
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/de/impressum</loc> <loc>https://sync.koalastuff.net/de/impressum</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>monthly</changefreq> <changefreq>monthly</changefreq>
<priority>0.3</priority> <priority>0.3</priority>
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/de/datenschutz</loc> <loc>https://sync.koalastuff.net/de/datenschutz</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>monthly</changefreq> <changefreq>monthly</changefreq>
<priority>0.3</priority> <priority>0.3</priority>
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/</loc> <loc>https://sync.koalastuff.net/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -49,7 +49,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/de/</loc> <loc>https://sync.koalastuff.net/de/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -71,7 +71,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/fr/</loc> <loc>https://sync.koalastuff.net/fr/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -93,7 +93,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/es/</loc> <loc>https://sync.koalastuff.net/es/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -115,7 +115,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt-BR/</loc> <loc>https://sync.koalastuff.net/pt-BR/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -137,7 +137,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ru/</loc> <loc>https://sync.koalastuff.net/ru/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -159,7 +159,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/it/</loc> <loc>https://sync.koalastuff.net/it/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -181,7 +181,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pl/</loc> <loc>https://sync.koalastuff.net/pl/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -203,7 +203,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/tr/</loc> <loc>https://sync.koalastuff.net/tr/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -225,7 +225,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/nl/</loc> <loc>https://sync.koalastuff.net/nl/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -247,7 +247,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ja/</loc> <loc>https://sync.koalastuff.net/ja/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -269,7 +269,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ko/</loc> <loc>https://sync.koalastuff.net/ko/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -291,7 +291,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/zh/</loc> <loc>https://sync.koalastuff.net/zh/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -313,7 +313,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/uk/</loc> <loc>https://sync.koalastuff.net/uk/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -335,7 +335,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt/</loc> <loc>https://sync.koalastuff.net/pt/</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/"/>
@@ -357,7 +357,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/alternatives</loc> <loc>https://sync.koalastuff.net/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -379,7 +379,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/de/alternatives</loc> <loc>https://sync.koalastuff.net/de/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -401,7 +401,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/fr/alternatives</loc> <loc>https://sync.koalastuff.net/fr/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -423,7 +423,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/es/alternatives</loc> <loc>https://sync.koalastuff.net/es/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -445,7 +445,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt-BR/alternatives</loc> <loc>https://sync.koalastuff.net/pt-BR/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -467,7 +467,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ru/alternatives</loc> <loc>https://sync.koalastuff.net/ru/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -489,7 +489,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/it/alternatives</loc> <loc>https://sync.koalastuff.net/it/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -511,7 +511,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pl/alternatives</loc> <loc>https://sync.koalastuff.net/pl/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -533,7 +533,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/tr/alternatives</loc> <loc>https://sync.koalastuff.net/tr/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -555,7 +555,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/nl/alternatives</loc> <loc>https://sync.koalastuff.net/nl/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -577,7 +577,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ja/alternatives</loc> <loc>https://sync.koalastuff.net/ja/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -599,7 +599,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ko/alternatives</loc> <loc>https://sync.koalastuff.net/ko/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -621,7 +621,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/zh/alternatives</loc> <loc>https://sync.koalastuff.net/zh/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -643,7 +643,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/uk/alternatives</loc> <loc>https://sync.koalastuff.net/uk/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -665,7 +665,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt/alternatives</loc> <loc>https://sync.koalastuff.net/pt/alternatives</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives"/>
@@ -687,7 +687,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -709,7 +709,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/de/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/de/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -731,7 +731,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/fr/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/fr/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -753,7 +753,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/es/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/es/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -775,7 +775,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt-BR/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/pt-BR/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -797,7 +797,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ru/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/ru/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -819,7 +819,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/it/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/it/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -841,7 +841,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pl/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/pl/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -863,7 +863,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/tr/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/tr/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -885,7 +885,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/nl/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/nl/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -907,7 +907,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ja/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/ja/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -929,7 +929,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ko/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/ko/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -951,7 +951,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/zh/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/zh/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -973,7 +973,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/uk/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/uk/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -995,7 +995,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt/alternatives/teleparty</loc> <loc>https://sync.koalastuff.net/pt/alternatives/teleparty</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty"/>
@@ -1017,7 +1017,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1039,7 +1039,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/de/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/de/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1061,7 +1061,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/fr/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/fr/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1083,7 +1083,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/es/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/es/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1105,7 +1105,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt-BR/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/pt-BR/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1127,7 +1127,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ru/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/ru/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1149,7 +1149,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/it/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/it/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1171,7 +1171,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pl/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/pl/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1193,7 +1193,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/tr/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/tr/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1215,7 +1215,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/nl/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/nl/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1237,7 +1237,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ja/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/ja/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1259,7 +1259,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ko/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/ko/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1281,7 +1281,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/zh/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/zh/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1303,7 +1303,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/uk/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/uk/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1325,7 +1325,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt/alternatives/screen-sharing</loc> <loc>https://sync.koalastuff.net/pt/alternatives/screen-sharing</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing"/>
@@ -1347,7 +1347,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1369,7 +1369,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/de/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/de/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1391,7 +1391,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/fr/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/fr/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1413,7 +1413,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/es/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/es/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1435,7 +1435,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt-BR/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/pt-BR/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1457,7 +1457,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ru/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/ru/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1479,7 +1479,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/it/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/it/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1501,7 +1501,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pl/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/pl/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1523,7 +1523,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/tr/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/tr/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1545,7 +1545,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/nl/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/nl/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1567,7 +1567,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ja/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/ja/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1589,7 +1589,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ko/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/ko/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1611,7 +1611,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/zh/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/zh/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1633,7 +1633,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/uk/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/uk/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1655,7 +1655,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt/alternatives/watch2gether</loc> <loc>https://sync.koalastuff.net/pt/alternatives/watch2gether</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether"/>
@@ -1677,7 +1677,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/alternatives/scener</loc> <loc>https://sync.koalastuff.net/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1699,7 +1699,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/de/alternatives/scener</loc> <loc>https://sync.koalastuff.net/de/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1721,7 +1721,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/fr/alternatives/scener</loc> <loc>https://sync.koalastuff.net/fr/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1743,7 +1743,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/es/alternatives/scener</loc> <loc>https://sync.koalastuff.net/es/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1765,7 +1765,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt-BR/alternatives/scener</loc> <loc>https://sync.koalastuff.net/pt-BR/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1787,7 +1787,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ru/alternatives/scener</loc> <loc>https://sync.koalastuff.net/ru/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1809,7 +1809,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/it/alternatives/scener</loc> <loc>https://sync.koalastuff.net/it/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1831,7 +1831,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pl/alternatives/scener</loc> <loc>https://sync.koalastuff.net/pl/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1853,7 +1853,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/tr/alternatives/scener</loc> <loc>https://sync.koalastuff.net/tr/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1875,7 +1875,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/nl/alternatives/scener</loc> <loc>https://sync.koalastuff.net/nl/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1897,7 +1897,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ja/alternatives/scener</loc> <loc>https://sync.koalastuff.net/ja/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1919,7 +1919,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ko/alternatives/scener</loc> <loc>https://sync.koalastuff.net/ko/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1941,7 +1941,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/zh/alternatives/scener</loc> <loc>https://sync.koalastuff.net/zh/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1963,7 +1963,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/uk/alternatives/scener</loc> <loc>https://sync.koalastuff.net/uk/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -1985,7 +1985,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt/alternatives/scener</loc> <loc>https://sync.koalastuff.net/pt/alternatives/scener</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/scener"/>
@@ -2007,7 +2007,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2029,7 +2029,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/de/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/de/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2051,7 +2051,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/fr/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/fr/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2073,7 +2073,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/es/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/es/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2095,7 +2095,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt-BR/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/pt-BR/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2117,7 +2117,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ru/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/ru/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2139,7 +2139,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/it/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/it/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2161,7 +2161,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pl/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/pl/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2183,7 +2183,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/tr/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/tr/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2205,7 +2205,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/nl/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/nl/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2227,7 +2227,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ja/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/ja/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2249,7 +2249,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ko/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/ko/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2271,7 +2271,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/zh/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/zh/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2293,7 +2293,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/uk/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/uk/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2315,7 +2315,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt/alternatives/kosmi</loc> <loc>https://sync.koalastuff.net/pt/alternatives/kosmi</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/kosmi"/>
@@ -2337,7 +2337,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2359,7 +2359,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/de/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/de/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2381,7 +2381,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/fr/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/fr/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2403,7 +2403,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/es/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/es/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2425,7 +2425,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt-BR/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/pt-BR/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2447,7 +2447,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ru/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/ru/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2469,7 +2469,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/it/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/it/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2491,7 +2491,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pl/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/pl/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2513,7 +2513,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/tr/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/tr/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2535,7 +2535,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/nl/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/nl/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2557,7 +2557,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ja/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/ja/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2579,7 +2579,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/ko/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/ko/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2601,7 +2601,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/zh/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/zh/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2623,7 +2623,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/uk/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/uk/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
@@ -2645,7 +2645,7 @@
</url> </url>
<url> <url>
<loc>https://sync.koalastuff.net/pt/alternatives/twoseven</loc> <loc>https://sync.koalastuff.net/pt/alternatives/twoseven</loc>
<lastmod>2026-07-10</lastmod> <lastmod>2026-07-11</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
<xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/> <xhtml:link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/twoseven"/>
+382 -30
View File
@@ -163,8 +163,8 @@ body {
#bamboo-far .bamboo-stalk { #bamboo-far .bamboo-stalk {
position: absolute; position: absolute;
top: -20%; top: -70%;
height: 140%; height: 240%;
border-radius: 999px; border-radius: 999px;
filter: blur(5.5px); filter: blur(5.5px);
transition: filter 1.2s ease; transition: filter 1.2s ease;
@@ -183,8 +183,8 @@ body {
#bamboo-far::after { #bamboo-far::after {
content: ''; content: '';
position: absolute; position: absolute;
top: -20%; top: -70%;
height: 140%; height: 240%;
border-radius: 999px; border-radius: 999px;
filter: blur(8px); filter: blur(8px);
opacity: 0.28; opacity: 0.28;
@@ -199,6 +199,71 @@ body {
.bg-nature.depth-mid #bamboo-far .bamboo-stalk { filter: blur(6.5px); } .bg-nature.depth-mid #bamboo-far .bamboo-stalk { filter: blur(6.5px); }
.bg-nature.depth-deep #bamboo-far .bamboo-stalk { filter: blur(8px); } .bg-nature.depth-deep #bamboo-far .bamboo-stalk { filter: blur(8px); }
/* (a2) Mid bamboo layer: sits between far and near with its own parallax
rate (app.js) and half the near layer's sway, so the three layers drift
apart subtly instead of moving as one sheet. Landing page only — the
static pages keep the lighter two-layer scene. */
#bamboo-mid {
position: absolute;
inset: 0;
will-change: transform;
}
#bamboo-mid .bamboo-stalk {
position: absolute;
top: -70%;
height: 240%;
border-radius: 999px;
filter: blur(2.5px);
transition: filter 1.2s ease;
/* Segment rings + a soft left-of-centre sheen give the stalks culm
structure without any extra elements; the blur keeps both faint. */
background:
repeating-linear-gradient(180deg,
transparent 0, transparent 84px,
oklch(0.46 0.075 145 / 0.28) 84px, oklch(0.46 0.075 145 / 0.28) 87px),
linear-gradient(90deg, transparent 14%, oklch(0.58 0.07 145 / 0.14) 38%, transparent 68%),
linear-gradient(180deg, oklch(0.36 0.06 145 / 0.45), oklch(0.25 0.045 140 / 0.2));
transform-origin: bottom center;
animation: bambooSwayHalf 13s ease-in-out infinite;
}
#bamboo-mid .mid-1 { left: 17%; width: 15px; animation-duration: 12s; }
#bamboo-mid .mid-2 { left: 31%; width: 9px; animation-delay: -5s; opacity: 0.55; }
#bamboo-mid .mid-3 { right: 15.5%; width: 17px; animation-duration: 14.5s; animation-delay: -8s; }
#bamboo-mid .mid-4 { right: 30%; width: 8px; animation-duration: 11.5s; animation-delay: -2.5s; opacity: 0.5; }
/* A few leaves sprout from the mid stalks (pseudo-elements, so they ride
the stalk's sway for free). The slim centre stalks stay bare. */
#bamboo-mid .bamboo-stalk::before,
#bamboo-mid .bamboo-stalk::after {
content: '';
position: absolute;
width: 24px;
height: 11px;
border-radius: 0 100% 0 100%;
background: oklch(0.5 0.1 145 / 0.32);
}
#bamboo-mid .mid-1::before { top: 31%; left: 13px; transform: rotate(20deg); }
#bamboo-mid .mid-1::after { top: 55%; left: -26px; transform: rotate(-158deg); width: 20px; height: 9px; }
#bamboo-mid .mid-3::before { top: 26%; left: -27px; transform: rotate(-156deg); }
#bamboo-mid .mid-3::after { top: 49%; left: 15px; transform: rotate(24deg); width: 21px; height: 10px; }
#bamboo-mid .mid-2::before,
#bamboo-mid .mid-2::after,
#bamboo-mid .mid-4::before,
#bamboo-mid .mid-4::after {
display: none;
}
.bg-nature.depth-mid #bamboo-mid .bamboo-stalk { filter: blur(3.5px); }
.bg-nature.depth-deep #bamboo-mid .bamboo-stalk { filter: blur(4.5px); }
@keyframes bambooSwayHalf {
0%, 100% { transform: rotate(0); }
50% { transform: rotate(calc(var(--sway, 1.2deg) * 0.5)); }
}
/* (b) Near bamboo layer: sharp stalks with nodes and leaves, gentle sway */ /* (b) Near bamboo layer: sharp stalks with nodes and leaves, gentle sway */
#bamboo-near { #bamboo-near {
position: absolute; position: absolute;
@@ -208,10 +273,14 @@ body {
#bamboo-near .bamboo-stalk { #bamboo-near .bamboo-stalk {
position: absolute; position: absolute;
top: -20%; top: -70%;
height: 140%; height: 240%;
border-radius: 999px; border-radius: 999px;
background: linear-gradient(180deg, oklch(0.4 0.07 145 / 0.55), oklch(0.26 0.05 140 / 0.25)); /* Vertical sheen over the culm gradient: reads as light from the
canopy catching the rounded stalk instead of a flat strip. */
background:
linear-gradient(90deg, transparent 12%, oklch(0.62 0.08 145 / 0.18) 36%, transparent 64%),
linear-gradient(180deg, oklch(0.4 0.07 145 / 0.55), oklch(0.26 0.05 140 / 0.25));
transform-origin: bottom center; transform-origin: bottom center;
animation: bambooSway 10s ease-in-out infinite; animation: bambooSway 10s ease-in-out infinite;
} }
@@ -375,13 +444,31 @@ body {
.canopy-arc-3 { top: -4px; left: 61%; width: 46px; height: 14px; transform: rotate(-3deg); } .canopy-arc-3 { top: -4px; left: 61%; width: 46px; height: 14px; transform: rotate(-3deg); }
.canopy-arc-4 { top: -2px; left: 84%; width: 30px; height: 10px; transform: rotate(7deg); background: oklch(0.33 0.06 145 / 0.4); } .canopy-arc-4 { top: -2px; left: 84%; width: 30px; height: 10px; transform: rotate(7deg); background: oklch(0.33 0.06 145 / 0.4); }
/* (f) Undergrowth: grass blades in the lower corners */ /* (f) Undergrowth: grass blades in the lower corners. Each blade carries a
shorter companion blade (::before) so the corners read as small tufts. */
.bg-grass { .bg-grass {
position: absolute; position: absolute;
bottom: -4px; bottom: -4px;
transform-origin: bottom; transform-origin: bottom;
} }
.bg-grass::before {
content: '';
position: absolute;
bottom: 0;
width: 72%;
height: 55%;
border-radius: inherit;
background: inherit;
opacity: 0.7;
transform-origin: bottom;
}
.bg-grass-1::before { left: -5px; transform: rotate(-13deg); }
.bg-grass-2::before { left: 4px; transform: rotate(11deg); }
.bg-grass-3::before { right: -5px; transform: rotate(12deg); }
.bg-grass-4::before { right: 4px; transform: rotate(-9deg); }
.bg-grass-1 { left: 1.5%; width: 6px; height: 52px; border-radius: 100% 0 0 0; background: oklch(0.38 0.07 145 / 0.4); transform: rotate(-9deg); } .bg-grass-1 { left: 1.5%; width: 6px; height: 52px; border-radius: 100% 0 0 0; background: oklch(0.38 0.07 145 / 0.4); transform: rotate(-9deg); }
.bg-grass-2 { left: 3.2%; width: 5px; height: 38px; border-radius: 100% 0 0 0; background: oklch(0.35 0.07 145 / 0.3); transform: rotate(-12deg); } .bg-grass-2 { left: 3.2%; width: 5px; height: 38px; border-radius: 100% 0 0 0; background: oklch(0.35 0.07 145 / 0.3); transform: rotate(-12deg); }
.bg-grass-3 { right: 2%; width: 6px; height: 56px; border-radius: 0 100% 0 0; background: oklch(0.4 0.07 145 / 0.4); transform: rotate(8deg); } .bg-grass-3 { right: 2%; width: 6px; height: 56px; border-radius: 0 100% 0 0; background: oklch(0.4 0.07 145 / 0.4); transform: rotate(8deg); }
@@ -508,6 +595,83 @@ body {
100% { translate: -14px -18px; } 100% { translate: -14px -18px; }
} }
/* Day/night: fireflies belong to the dark theme. By daylight the same six
spots are visited by small butterflies instead — the wrapper (and with it
the cursor-shy push and the section greeting) is shared, only the inner
dot is redressed. Only animation-name is swapped so the per-firefly
duration/delay variety above keeps applying to the butterflies. */
html.theme-light .firefly {
position: relative;
width: 2px;
height: 9px;
border-radius: 1px;
background: oklch(0.35 0.05 140 / 0.8);
box-shadow: none;
opacity: 0.85;
animation-name: butterflyBob, fireflyWander;
}
html.theme-light .firefly::before,
html.theme-light .firefly::after {
content: '';
position: absolute;
top: -2px;
width: 7px;
height: 10px;
background: oklch(0.62 0.14 45 / 0.7);
animation: wingFlap 0.5s ease-in-out infinite alternate;
}
html.theme-light .firefly::before {
left: -6px;
border-radius: 90% 10% 60% 40%;
transform-origin: 100% 55%;
}
html.theme-light .firefly::after {
right: -6px;
border-radius: 10% 90% 40% 60%;
transform-origin: 0% 55%;
animation-name: wingFlapR;
}
/* Every other butterfly wears the sage of the canopy and flaps lazier */
html.theme-light .fw-2 .firefly::before,
html.theme-light .fw-2 .firefly::after,
html.theme-light .fw-4 .firefly::before,
html.theme-light .fw-4 .firefly::after,
html.theme-light .fw-6 .firefly::before,
html.theme-light .fw-6 .firefly::after {
background: oklch(0.52 0.12 150 / 0.55);
animation-duration: 0.62s;
}
@keyframes butterflyBob {
0%, 100% { transform: translateY(0) rotate(-4deg); }
50% { transform: translateY(-8px) rotate(5deg); }
}
@keyframes wingFlap {
from { transform: scaleX(1) rotate(-6deg); }
to { transform: scaleX(0.3) rotate(8deg); }
}
@keyframes wingFlapR {
from { transform: scaleX(1) rotate(6deg); }
to { transform: scaleX(0.3) rotate(-8deg); }
}
/* By day the dusk layer must not dim the butterflies with scroll depth
(the JS crossfade keeps writing inline opacity for the dark theme), and
the warm horizon glow steps back so it reads as haze, not dusk. */
html.theme-light .bg-depth-dusk {
opacity: 1 !important;
}
html.theme-light .bg-horizon {
background: radial-gradient(ellipse 60% 100% at 50% 100%, oklch(0.8 0.08 60 / 0.1), transparent 70%);
}
/* (j) Wandering light sweep */ /* (j) Wandering light sweep */
.bg-light-sweep { .bg-light-sweep {
position: absolute; position: absolute;
@@ -525,6 +689,20 @@ body {
100% { transform: translate(20%, 20%) rotate(8deg); } 100% { transform: translate(20%, 20%) rotate(8deg); }
} }
/* (j2) Corner vignette: pulls the eye toward the content and grounds the
scene — the forest darkens gently toward the edges like real depth. */
.bg-nature::after {
content: '';
position: absolute;
inset: 0;
pointer-events: none;
background: radial-gradient(ellipse 120% 90% at 50% 38%, transparent 55%, oklch(0.10 0.025 142 / 0.4) 100%);
}
html.theme-light .bg-nature::after {
background: radial-gradient(ellipse 120% 90% at 50% 38%, transparent 62%, oklch(0.78 0.03 120 / 0.3) 100%);
}
/* (k) Film grain (SVG feTurbulence, no external assets) */ /* (k) Film grain (SVG feTurbulence, no external assets) */
.bg-grain { .bg-grain {
position: absolute; position: absolute;
@@ -543,6 +721,7 @@ body {
.fall-leaf-8, .fall-leaf-8,
.bg-godrays, .bg-godrays,
.bg-light-sweep, .bg-light-sweep,
#bamboo-mid,
#bamboo-far .far-4, #bamboo-far .far-4,
#bamboo-far .far-5, #bamboo-far .far-5,
#bamboo-near .near-3 { #bamboo-near .near-3 {
@@ -550,7 +729,67 @@ body {
} }
} }
/* (l) Click confetti: a small handful of eucalyptus leaves bursts from
wherever the visitor presses (spawned by app.js). Built like the falling
leaves above — the outer span owns the flight path (fast burst that
brakes at its peak, then sinks), the inner leaf rocks around its stem
the whole way down. Deliberately subtle: few, tiny, short-lived. */
.click-leaf {
position: fixed;
z-index: 3000;
margin: -6px 0 0 -4px;
pointer-events: none;
opacity: 0;
animation: leafBurst var(--leaf-dur, 1.1s) forwards;
will-change: transform, opacity;
}
.click-leaf i {
display: block;
width: 8px;
height: 12px;
border-radius: 0 70% 0 70%;
background: linear-gradient(135deg, oklch(0.62 0.12 150 / 0.95), oklch(0.45 0.09 145 / 0.85));
transform: rotate(var(--leaf-rot, 0deg));
animation: leafRock var(--flutter-dur, 0.6s) ease-in-out infinite alternate;
}
.click-leaf-sage i {
background: linear-gradient(135deg, oklch(0.72 0.09 140 / 0.9), oklch(0.55 0.08 145 / 0.8));
}
.click-leaf-amber i {
background: linear-gradient(135deg, oklch(0.68 0.14 55 / 0.95), oklch(0.55 0.13 40 / 0.85));
}
/* Two-phase flight: --leaf-x/--leaf-y is the burst PEAK; from there each
leaf loses momentum and sinks past it while fading. Per-segment timing
functions give the fast pop and the gentle fall. */
@keyframes leafBurst {
0% {
opacity: 0;
transform: translate(0, 0) scale(var(--leaf-scale, 1));
animation-timing-function: cubic-bezier(0.16, 0.84, 0.44, 1);
}
10% { opacity: 1; }
45% {
opacity: 1;
transform: translate(var(--leaf-x, 0), var(--leaf-y, -20px)) scale(var(--leaf-scale, 1));
animation-timing-function: cubic-bezier(0.55, 0.06, 0.68, 0.19);
}
100% {
opacity: 0;
transform: translate(calc(var(--leaf-x, 0) * 1.4), calc(var(--leaf-y, -20px) + 44px)) scale(calc(var(--leaf-scale, 1) * 0.92));
}
}
@keyframes leafRock {
from { transform: rotate(calc(var(--leaf-rot, 0deg) - 24deg)); }
to { transform: rotate(calc(var(--leaf-rot, 0deg) + 24deg)); }
}
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.click-leaf { display: none; }
.bg-nature *, .bg-nature *,
.bg-nature *::before, .bg-nature *::before,
.bg-nature *::after { .bg-nature *::after {
@@ -771,6 +1010,7 @@ html.theme-light .bg-nature {
html.theme-light .bg-canopy, html.theme-light .bg-canopy,
html.theme-light .canopy-arc, html.theme-light .canopy-arc,
html.theme-light #bamboo-far .bamboo-stalk, html.theme-light #bamboo-far .bamboo-stalk,
html.theme-light #bamboo-mid .bamboo-stalk,
html.theme-light #bamboo-near .bamboo-stalk, html.theme-light #bamboo-near .bamboo-stalk,
html.theme-light .bg-grass { html.theme-light .bg-grass {
opacity: 0.45; opacity: 0.45;
@@ -1158,14 +1398,16 @@ html.theme-light .koala-speech-bubble::after {
font-size: 0.95rem; font-size: 0.95rem;
} }
.btn-primary { .btn-primary {
background: var(--accent-green); background: linear-gradient(135deg, oklch(0.48 0.13 150), oklch(0.39 0.10 145));
color: var(--text-on-green); color: #fff;
box-shadow: 0 10px 15px -3px var(--accent-glow); border: 1px solid oklch(0.72 0.11 95 / 0.24);
box-shadow: 0 12px 24px -8px oklch(0.38 0.11 148 / 0.65), 0 0 0 1px oklch(0.80 0.08 90 / 0.06) inset;
} }
.btn-primary:hover { .btn-primary:hover {
background: linear-gradient(135deg, oklch(0.54 0.14 150), oklch(0.44 0.11 145));
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 20px 25px -5px var(--accent-glow); box-shadow: 0 18px 32px -8px oklch(0.38 0.11 148 / 0.75), 0 0 18px oklch(0.68 0.13 150 / 0.18);
} }
.btn-firefox { .btn-firefox {
@@ -1615,6 +1857,19 @@ html.theme-light .koala-speech-bubble::after {
put white text on a colour get a darker shade (these beat inline styles). */ put white text on a colour get a darker shade (these beat inline styles). */
.hero-demo-scene { .hero-demo-scene {
--accent: oklch(0.80 0.10 150); --accent: oklch(0.80 0.10 150);
--demo-sky-top: oklch(0.22 0.055 152);
--demo-sky-mid: oklch(0.145 0.035 150);
--demo-sky-bottom: oklch(0.075 0.018 145);
--demo-glow: oklch(0.76 0.13 150 / 0.34);
--demo-stars: oklch(0.94 0.025 95);
--demo-moon: oklch(0.97 0.025 92);
--demo-mountain: oklch(0.18 0.055 148 / 0.9);
--demo-midground: oklch(0.22 0.065 145 / 0.88);
--demo-foreground: oklch(0.065 0.025 145);
--demo-card: oklch(0.18 0.032 142);
--demo-toolbar: oklch(0.12 0.025 142);
--demo-control-fade: oklch(0.055 0.015 145 / 0.94);
--demo-track: oklch(0.92 0.02 95 / 0.2);
} }
.hero-demo-scene .mock-tab.active, .hero-demo-scene .mock-tab.active,
.hero-demo-scene .mock-btn-force, .hero-demo-scene .mock-btn-force,
@@ -1636,17 +1891,33 @@ html.theme-light .koala-speech-bubble::after {
html.theme-light .hero-demo-scene { html.theme-light .hero-demo-scene {
--accent: oklch(0.60 0.12 150); --accent: oklch(0.60 0.12 150);
--demo-sky-top: oklch(0.86 0.085 78);
--demo-sky-mid: oklch(0.76 0.095 112);
--demo-sky-bottom: oklch(0.54 0.10 150);
--demo-glow: oklch(0.92 0.13 72 / 0.44);
--demo-stars: oklch(0.98 0.025 90);
--demo-moon: oklch(0.99 0.025 88);
--demo-mountain: oklch(0.48 0.095 150 / 0.82);
--demo-midground: oklch(0.40 0.10 148 / 0.8);
--demo-foreground: oklch(0.24 0.075 145);
--demo-card: oklch(0.985 0.008 95);
--demo-toolbar: oklch(0.955 0.014 100);
--demo-control-fade: oklch(0.16 0.035 145 / 0.88);
--demo-track: oklch(0.98 0.01 95 / 0.34);
} }
html.theme-light .hero-demo-scene .demo-tab-card { html.theme-light .hero-demo-scene .demo-tab-card {
background: #ffffff; background: var(--demo-card);
border-color: oklch(0.20 0.025 140 / 0.1); border-color: oklch(0.20 0.025 140 / 0.1);
box-shadow: 0 18px 38px oklch(0.20 0.025 140 / 0.14); box-shadow:
0 18px 38px oklch(0.20 0.025 140 / 0.16),
0 0 28px oklch(0.82 0.10 85 / 0.14),
inset 0 1px rgba(255, 255, 255, 0.9);
} }
html.theme-light .hero-demo-scene .demo-tab-titlebar { html.theme-light .hero-demo-scene .demo-tab-titlebar {
background: oklch(0.96 0.01 90); background: var(--demo-toolbar);
border-bottom-color: oklch(0.88 0.015 90); border-bottom-color: oklch(0.88 0.015 90);
color: oklch(0.45 0.02 110); color: oklch(0.45 0.02 110);
} }
@@ -2941,6 +3212,61 @@ footer a:hover {
transform: translateY(0); transform: translateY(0);
} }
html.theme-light .hero-demo-scene .demo-user-a,
html.theme-light .hero-demo-scene .demo-user-b {
color: oklch(0.36 0.105 150);
background: oklch(0.60 0.12 150 / 0.13);
}
html.theme-light .hero-demo-scene .extension-mockup {
--bg: oklch(0.935 0.018 110) !important;
--card: oklch(0.995 0.006 100) !important;
--accent: oklch(0.56 0.13 150) !important;
--accent-glow: oklch(0.56 0.13 150 / 0.22) !important;
--text: oklch(0.21 0.03 140) !important;
--text-muted: oklch(0.43 0.03 135) !important;
--success: oklch(0.56 0.13 150) !important;
--error: oklch(0.57 0.17 25) !important;
--glass-border: oklch(0.28 0.035 140 / 0.10) !important;
background: oklch(0.935 0.018 110) !important;
border-color: oklch(0.28 0.035 140 / 0.12);
box-shadow:
0 20px 42px oklch(0.20 0.025 140 / 0.16),
0 0 26px oklch(0.82 0.10 85 / 0.12);
}
html.theme-light .hero-demo-scene .mock-tabs,
html.theme-light .hero-demo-scene .mock-card,
html.theme-light .hero-demo-scene .mock-input {
background: oklch(0.995 0.006 100);
border-color: oklch(0.28 0.035 140 / 0.14);
color: oklch(0.21 0.03 140);
}
html.theme-light .hero-demo-scene .mock-peer-item {
background: oklch(0.28 0.035 140 / 0.035);
border-color: oklch(0.28 0.035 140 / 0.09);
}
html.theme-light .hero-demo-scene .mock-joined-room,
html.theme-light .hero-demo-scene .mock-status-pill {
background: oklch(0.56 0.13 150 / 0.10);
border-color: oklch(0.56 0.13 150 / 0.24);
}
html.theme-light .hero-demo-scene .mock-close-btn:hover {
color: oklch(0.21 0.03 140);
background: oklch(0.28 0.035 140 / 0.08);
}
/* A direct hash destination is content, not an entrance animation. Keep it
readable from the first paint even before app.js has initialized. */
section:target [data-reveal],
section:target[data-reveal] {
opacity: 1;
transform: translateY(0);
}
/* --- Legal Pages --- */ /* --- Legal Pages --- */
.legal-content { .legal-content {
@@ -3685,9 +4011,9 @@ footer {
} }
.cross { .cross {
color: oklch(0.55 0.15 25); color: oklch(0.68 0.17 25);
font-weight: 500; font-weight: 600;
opacity: 0.8; opacity: 1;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
@@ -3838,7 +4164,7 @@ footer {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
vertical-align: super; vertical-align: super;
opacity: 0.6; opacity: 0.82;
transition: opacity 0.2s ease, color 0.2s ease, transform 0.2s ease; transition: opacity 0.2s ease, color 0.2s ease, transform 0.2s ease;
} }
@@ -4396,10 +4722,10 @@ header[id] {
position: absolute; position: absolute;
width: 310px; width: 310px;
width: 70cqw; width: 70cqw;
background: oklch(0.21 0.025 138); background: var(--demo-card);
border: 1px solid rgba(255, 255, 255, 0.09); border: 1px solid rgba(255, 255, 255, 0.09);
border-radius: 14px; border-radius: 14px;
box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45); box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45), inset 0 1px rgba(255, 255, 255, 0.05);
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
@@ -4471,7 +4797,7 @@ header[id] {
align-items: center; align-items: center;
gap: 8px; gap: 8px;
padding: 6px 8px 6px 10px; padding: 6px 8px 6px 10px;
background: oklch(0.15 0.02 138); background: var(--demo-toolbar);
border-bottom: 1px solid rgba(255, 255, 255, 0.06); border-bottom: 1px solid rgba(255, 255, 255, 0.06);
font-size: 10.5px; font-size: 10.5px;
color: var(--text-muted); color: var(--text-muted);
@@ -4596,18 +4922,35 @@ header[id] {
.demo-video { .demo-video {
position: relative; position: relative;
aspect-ratio: 16 / 9; aspect-ratio: 16 / 9;
background: #020617; background: var(--demo-sky-bottom);
overflow: hidden; overflow: hidden;
} }
.demo-video::after {
content: '';
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
box-shadow:
inset 0 0 0 1px rgba(255, 255, 255, 0.05),
inset 0 -24px 36px oklch(0.04 0.015 145 / 0.2);
}
html.theme-light .hero-demo-scene .demo-video::after {
box-shadow:
inset 0 0 0 1px oklch(0.30 0.04 145 / 0.13),
inset 0 18px 32px oklch(0.98 0.03 90 / 0.1),
inset 0 -22px 34px oklch(0.18 0.05 145 / 0.16);
}
.demo-video-art { .demo-video-art {
position: absolute; position: absolute;
inset: 0; inset: 0;
background: background:
radial-gradient(circle at 78% 22%, oklch(0.96 0.01 90 / 0.28) 0 4%, transparent 5.5%), radial-gradient(30% 42% at 76% 29%, var(--demo-glow), transparent 64%),
radial-gradient(120% 90% at 70% 115%, oklch(0.26 0.05 140) 0%, transparent 60%), radial-gradient(90% 85% at 18% 5%, var(--demo-glow), transparent 66%),
radial-gradient(100% 80% at 12% 112%, oklch(0.20 0.04 145) 0%, transparent 55%), linear-gradient(160deg, var(--demo-sky-top) 0%, var(--demo-sky-mid) 52%, var(--demo-sky-bottom) 100%);
linear-gradient(160deg, oklch(0.27 0.035 130) 0%, oklch(0.15 0.02 138) 55%, #020617 100%);
transform-origin: 60% 40%; transform-origin: 60% 40%;
animation: animation:
demoKenBurns 9s ease-in-out infinite alternate, demoKenBurns 9s ease-in-out infinite alternate,
@@ -4624,7 +4967,7 @@ header[id] {
width: 70%; width: 70%;
aspect-ratio: 1; aspect-ratio: 1;
border-radius: 50%; border-radius: 50%;
background: radial-gradient(circle, oklch(0.75 0.13 150 / 0.32), transparent 65%); background: radial-gradient(circle, var(--demo-glow), transparent 65%);
animation: demoGlowDrift 7s ease-in-out infinite alternate; animation: demoGlowDrift 7s ease-in-out infinite alternate;
animation-play-state: paused; animation-play-state: paused;
} }
@@ -4718,7 +5061,7 @@ header[id] {
align-items: center; align-items: center;
gap: 8px; gap: 8px;
padding: 14px 10px 8px; padding: 14px 10px 8px;
background: linear-gradient(transparent, oklch(0.10 0.01 135 / 0.85)); background: linear-gradient(transparent, var(--demo-control-fade));
font-size: 9.5px; font-size: 9.5px;
color: oklch(0.88 0.015 90); color: oklch(0.88 0.015 90);
z-index: 1; z-index: 1;
@@ -4737,7 +5080,7 @@ header[id] {
flex: 1; flex: 1;
height: 5px; height: 5px;
border-radius: 99px; border-radius: 99px;
background: rgba(255, 255, 255, 0.18); background: var(--demo-track);
cursor: pointer; cursor: pointer;
} }
@@ -4925,6 +5268,15 @@ header[id] {
height: 100%; height: 100%;
} }
.hero-demo-scene .film-stars { fill: var(--demo-stars) !important; }
.hero-demo-scene .film-moon {
fill: var(--demo-moon) !important;
filter: drop-shadow(0 0 6px var(--demo-moon));
}
.hero-demo-scene .film-mtn-scroll { fill: var(--demo-mountain) !important; }
.hero-demo-scene .film-mid-scroll > * { fill: var(--demo-midground) !important; }
.hero-demo-scene .film-fore-scroll > * { fill: var(--demo-foreground) !important; }
/* Stars: staggered twinkle so the sky feels alive */ /* Stars: staggered twinkle so the sky feels alive */
.film-star { .film-star {
transform-box: fill-box; transform-box: fill-box;
+6
View File
@@ -235,6 +235,12 @@
<span class="bamboo-stalk far-4"></span> <span class="bamboo-stalk far-4"></span>
<span class="bamboo-stalk far-5"></span> <span class="bamboo-stalk far-5"></span>
</div> </div>
<div id="bamboo-mid">
<span class="bamboo-stalk mid-1"></span>
<span class="bamboo-stalk mid-2"></span>
<span class="bamboo-stalk mid-3"></span>
<span class="bamboo-stalk mid-4"></span>
</div>
<div id="bamboo-near"> <div id="bamboo-near">
<div class="bamboo-stalk near-1"> <div class="bamboo-stalk near-1">
<i class="bamboo-node node-1"></i> <i class="bamboo-node node-1"></i>