File size: 4,245 Bytes
b7f5767 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | // Theme toggle with persistence
(function initTheme() {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const saved = localStorage.getItem('theme'); // 'light' | 'dark'
const root = document.documentElement;
const setTheme = (mode) => {
if (mode === 'dark') {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
localStorage.setItem('theme', mode);
// Update icon
const iconEl = document.querySelector('#themeToggle i');
if (iconEl) {
iconEl.setAttribute('data-feather', mode === 'dark' ? 'sun' : 'moon');
if (window.feather) feather.replace();
}
};
setTheme(saved || (prefersDark ? 'dark' : 'light'));
})();
document.getElementById('themeToggle')?.addEventListener('click', () => {
const isDark = document.documentElement.classList.contains('dark');
const next = isDark ? 'light' : 'dark';
if (next === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
localStorage.setItem('theme', next);
const iconEl = document.querySelector('#themeToggle i');
if (iconEl) {
iconEl.setAttribute('data-feather', next === 'dark' ? 'sun' : 'moon');
if (window.feather) feather.replace();
}
});
// Year in footer
document.getElementById('year').textContent = new Date().getFullYear();
// Copy buttons
document.querySelectorAll('button[data-copy]').forEach((btn) => {
btn.addEventListener('click', async () => {
const sel = btn.getAttribute('data-copy');
const codeEl = document.querySelector(sel);
const text = codeEl?.innerText || '';
try {
await navigator.clipboard.writeText(text);
const label = btn.querySelector('.copy-label');
if (label) label.textContent = 'Copied!';
setTimeout(() => {
if (label) label.textContent = 'Copy';
}, 1500);
} catch (e) {
console.error('Copy failed', e);
}
});
});
// Signup form handlers (both CTA sections)
function handleSignup(formId, messageId) {
const form = document.getElementById(formId);
const msg = document.getElementById(messageId);
if (!form) return;
form.addEventListener('submit', async (e) => {
e.preventDefault();
const email = form.querySelector('input[type="email"]')?.value?.trim();
const password = form.querySelector('input[type="password"]')?.value;
if (!email) return;
if (formId === 'signupModalForm' && password && password.length < 8) {
if (msg) msg.textContent = 'Password must be at least 8 characters.';
return;
}
// Simulate async signup
if (msg) msg.textContent = 'Creating your workspace...';
await new Promise((r) => setTimeout(r, 900));
if (msg) msg.textContent = 'Success! Check your email to verify your account.';
// Close modal if applicable
if (formId === 'signupModalForm') closeSignupModal();
// Reset form
form.reset();
});
}
handleSignup('signupForm', 'formMessage');
handleSignup('signupModalForm', 'modalMessage');
// Signup modal
const openSignupButtons = ['openSignupTop', 'openSignupHero', 'openSignupPricing']
.map((id) => document.getElementById(id))
.filter(Boolean);
const modal = document.getElementById('signupModal');
const closeBtn = document.getElementById('closeSignup');
function openSignupModal() {
modal?.classList.remove('hidden');
modal?.classList.add('flex');
document.getElementById('modalEmail')?.focus();
}
function closeSignupModal() {
modal?.classList.add('hidden');
modal?.classList.remove('flex');
}
openSignupButtons.forEach((btn) => btn.addEventListener('click', openSignupModal));
closeBtn?.addEventListener('click', closeSignupModal);
modal?.addEventListener('click', (e) => {
if (e.target === modal) closeSignupModal();
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeSignupModal();
});
// Pause video when modal opens/closes
const heroVideo = document.getElementById('heroVideo');
const observer = new MutationObserver(() => {
if (modal?.classList.contains('hidden') === false && !heroVideo?.paused) {
heroVideo?.pause();
}
});
observer.observe(modal, { attributes: true, attributeFilter: ['class'] }); |