const introScreen = document.querySelector('#introScreen'); const holdScreen = document.querySelector('#holdScreen'); const completeScreen = document.querySelector('#completeScreen'); const openButton = document.querySelector('#openButton'); const replayButton = document.querySelector('#replayButton'); const sendBackButton = document.querySelector('#sendBackButton'); const sendBackNote = document.querySelector('#sendBackNote'); const letterButton = document.querySelector('#letterButton'); const letterPanel = document.querySelector('#letterPanel'); const letterClose = document.querySelector('#letterClose'); const heartHold = document.querySelector('#heartHold'); const holdMeterFill = document.querySelector('#holdMeterFill'); const holdStatusText = document.querySelector('#holdStatusText'); const holdInstruction = document.querySelector('#holdInstruction'); const topbarNote = document.querySelector('#topbarNote'); const HOLD_DURATION = 3200; let holdStartedAt = 0; let holdFrame = null; let isHolding = false; let hasCompleted = false; const holdLines = [ 'I’m right here', 'A little closer…', 'That’s it, Pookie', 'You are so, so loved', 'I’ve got you' ]; function showScreen(screen) { [introScreen, holdScreen, completeScreen].forEach((item) => { const active = item === screen; item.classList.toggle('is-active', active); item.setAttribute('aria-hidden', String(!active)); }); } function resetHold() { cancelAnimationFrame(holdFrame); isHolding = false; holdStartedAt = 0; heartHold.classList.remove('is-holding'); heartHold.style.setProperty('--progress', '0%'); holdMeterFill.style.width = '0%'; holdStatusText.textContent = 'I’m right here'; holdInstruction.textContent = 'Press and hold the heart to feel how much I love you.'; } function beginExperience() { resetHold(); showScreen(holdScreen); topbarNote.textContent = 'hold close'; window.setTimeout(() => heartHold.focus(), 60); } function updateHold(now) { if (!isHolding) return; const elapsed = now - holdStartedAt; const progress = Math.min(elapsed / HOLD_DURATION, 1); const percent = `${Math.round(progress * 100)}%`; heartHold.style.setProperty('--progress', percent); holdMeterFill.style.width = percent; const lineIndex = Math.min(Math.floor(progress * holdLines.length), holdLines.length - 1); holdStatusText.textContent = holdLines[lineIndex]; if (progress >= 1) { completeHug(); return; } holdFrame = requestAnimationFrame(updateHold); } function startHolding(event) { if (hasCompleted || isHolding) return; if (event?.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return; if (event?.cancelable) event.preventDefault(); isHolding = true; holdStartedAt = performance.now(); heartHold.classList.add('is-holding'); holdInstruction.textContent = 'Keep holding… let the warmth find you.'; holdFrame = requestAnimationFrame(updateHold); } function stopHolding(event) { if (event?.type === 'keyup' && event.key !== ' ' && event.key !== 'Enter') return; if (event?.cancelable) event.preventDefault(); if (!isHolding || hasCompleted) return; cancelAnimationFrame(holdFrame); isHolding = false; heartHold.classList.remove('is-holding'); holdStatusText.textContent = 'Almost… stay with me'; holdInstruction.textContent = 'You can hold a little longer for the full hug.'; } function completeHug() { cancelAnimationFrame(holdFrame); isHolding = false; hasCompleted = true; heartHold.classList.remove('is-holding'); heartHold.style.setProperty('--progress', '100%'); holdMeterFill.style.width = '100%'; holdStatusText.textContent = 'Hug complete'; topbarNote.textContent = 'made of warmth'; window.setTimeout(() => { showScreen(completeScreen); window.setTimeout(() => replayButton.focus(), 80); }, 420); } function replayHug() { hasCompleted = false; sendBackNote.textContent = ''; letterPanel.hidden = true; beginExperience(); } function openLetter() { letterPanel.hidden = false; letterButton.setAttribute('aria-expanded', 'true'); window.setTimeout(() => letterClose.focus(), 60); } function closeLetter() { letterPanel.hidden = true; letterButton.setAttribute('aria-expanded', 'false'); letterButton.focus(); } function sendHugBack() { sendBackNote.textContent = 'I felt that, Pookie. Keeping it close.'; sendBackButton.innerHTML = ' Hug sent'; window.setTimeout(() => { sendBackButton.innerHTML = ' Send another hug back'; }, 2600); } openButton.addEventListener('click', beginExperience); replayButton.addEventListener('click', replayHug); sendBackButton.addEventListener('click', sendHugBack); letterButton.addEventListener('click', openLetter); letterClose.addEventListener('click', closeLetter); heartHold.addEventListener('pointerdown', startHolding); heartHold.addEventListener('pointerup', stopHolding); heartHold.addEventListener('pointercancel', stopHolding); heartHold.addEventListener('pointerleave', stopHolding); heartHold.addEventListener('keydown', startHolding); heartHold.addEventListener('keyup', stopHolding); heartHold.addEventListener('blur', stopHolding); window.addEventListener('contextmenu', (event) => { if (event.target === heartHold) event.preventDefault(); });