const modeToggle = document.getElementById('modeToggle'); const modeLabel = document.getElementById('modeLabel'); const workItems = document.querySelectorAll('.work-item'); const workIndex = document.getElementById('workIndex'); const workTitle = document.getElementById('workTitle'); const workCopy = document.getElementById('workCopy'); const stageCard = document.querySelector('.stage-card'); const revealBlocks = document.querySelectorAll('.reveal'); const toneChips = document.querySelectorAll('.tone-chip'); const swatches = document.querySelectorAll('.swatch'); const noteItems = document.querySelectorAll('.note-item'); const densityRange = document.getElementById('densityRange'); const densityValue = document.getElementById('densityValue'); const previewCard = document.getElementById('previewCard'); const previewTone = document.getElementById('previewTone'); const previewDensity = document.getElementById('previewDensity'); const previewTitle = document.getElementById('previewTitle'); const previewText = document.getElementById('previewText'); const previewAccentLabel = document.getElementById('previewAccentLabel'); const previewNote = document.getElementById('previewNote'); const storedMode = window.localStorage.getItem('spectra-mode'); if (storedMode === 'light') { document.body.classList.add('light-mode'); } const toneMap = { quiet: { label: 'Quiet tone', title: 'A layout that leaves more breathing room.', text: '较安静的版式语气会减少冲突感,让留白、边框与小字说明承担更多秩序工作。', }, balanced: { label: 'Balanced tone', title: 'A layout that knows where to stop.', text: '平衡模式维持标题、说明与边框之间的中段张力,更适合作为正式落地页的默认状态。', }, dramatic: { label: 'Dramatic tone', title: 'A layout with sharper contrast and pacing.', text: '更强对比的语气不会增加花哨效果,而是把标题权重、信息落差和段落收束做得更明显。', }, }; const densityMap = { '1': 'Airy', '2': 'Balanced', '3': 'Dense', }; const accentNames = { '#7f2f2f': 'Accent / Oxblood', '#3f5a73': 'Accent / Slate Blue', '#6c6250': 'Accent / Warm Taupe', }; function applyMode() { const isLight = document.body.classList.contains('light-mode'); modeToggle.textContent = isLight ? 'Dark Mode' : 'Light Mode'; modeLabel.textContent = isLight ? 'Editorial Light' : 'Editorial Dark'; window.localStorage.setItem('spectra-mode', isLight ? 'light' : 'dark'); } function updateWorkStage(item) { workItems.forEach((button) => { button.classList.remove('is-active'); button.setAttribute('aria-selected', 'false'); }); item.classList.add('is-active'); item.setAttribute('aria-selected', 'true'); stageCard?.classList.remove('is-refreshing'); requestAnimationFrame(() => { stageCard?.classList.add('is-refreshing'); window.setTimeout(() => { stageCard?.classList.remove('is-refreshing'); }, 340); }); workIndex.textContent = item.dataset.index || ''; workTitle.textContent = item.dataset.title || ''; workCopy.textContent = item.dataset.copy || ''; } function updatePreviewTone(tone) { toneChips.forEach((chip) => chip.classList.toggle('is-active', chip.dataset.tone === tone)); previewCard.dataset.tone = tone; previewTone.textContent = toneMap[tone].label; previewTitle.textContent = toneMap[tone].title; previewText.textContent = toneMap[tone].text; } function updatePreviewDensity(level) { previewCard.dataset.density = level; previewCard.style.padding = level === '1' ? '20px' : level === '3' ? '30px' : '24px'; densityValue.textContent = densityMap[level]; previewDensity.textContent = `Density 0${level}`; } function updatePreviewAccent(accent) { swatches.forEach((swatch) => swatch.classList.toggle('is-active', swatch.dataset.accent === accent)); document.documentElement.style.setProperty('--accent', accent); document.documentElement.style.setProperty('--accent-soft', `${accent}22`); previewAccentLabel.textContent = accentNames[accent] || 'Accent / Custom'; } function updatePreviewNote(note) { noteItems.forEach((item) => item.classList.toggle('is-active', item.dataset.note === note)); previewNote.textContent = note; } modeToggle?.addEventListener('click', () => { document.body.classList.toggle('light-mode'); applyMode(); }); workItems.forEach((item) => { item.addEventListener('click', () => updateWorkStage(item)); }); toneChips.forEach((chip) => { chip.addEventListener('click', () => updatePreviewTone(chip.dataset.tone || 'balanced')); }); swatches.forEach((swatch) => { swatch.addEventListener('click', () => updatePreviewAccent(swatch.dataset.accent || '#7f2f2f')); }); noteItems.forEach((item) => { item.addEventListener('click', () => updatePreviewNote(item.dataset.note || '')); }); densityRange?.addEventListener('input', (event) => { updatePreviewDensity(event.target.value); }); const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add('is-visible'); observer.unobserve(entry.target); } }); }, { threshold: 0.18 }, ); revealBlocks.forEach((block) => observer.observe(block)); if (workItems[0]) { updateWorkStage(workItems[0]); } updatePreviewTone('balanced'); updatePreviewDensity(densityRange?.value || '2'); updatePreviewAccent('#7f2f2f'); updatePreviewNote('封面对齐决定第一印象。'); applyMode();