/* drag area relative container */ .drag-zone position: relative; width: 100%; height: 68px;
.shake-warning animation: gentleShake 0.25s ease-in-out 0s 1;
.sub color: #9aa9cf; font-size: 0.85rem; margin-top: 0.3rem; font-weight: 450; text-align: center;
.title font-size: 1.9rem; font-weight: 600; letter-spacing: -0.3px; background: linear-gradient(135deg, #ffffff, #c0d0ff); background-clip: text; -webkit-background-clip: text; color: transparent; margin-top: 0.25rem;
body min-height: 100vh; background: radial-gradient(circle at 20% 30%, #0b1120, #03050b); display: flex; align-items: center; justify-content: center; font-family: 'Segoe UI', 'SF Pro Text', 'Segoe UI Variable', system-ui, -apple-system, 'BlinkMacSystemFont', 'Roboto', sans-serif; padding: 1.5rem;
function onPointerMove(e) if (!isDragging) return; if (shutdownTriggered) isDragging = false; return; e.preventDefault(); let clientX = e.clientX; if (e.touches) clientX = e.touches[0].clientX; e.preventDefault(); // get current track boundaries relative to dragZone if (!trackContainer) return; const trackBounds = trackContainer.getBoundingClientRect(); const thumbWidth = thumb.offsetWidth; // compute new left position: pointer position - start offset let newThumbLeft = clientX - startX; // clamp within track container bounds (left bound and right bound) const minLeft = trackBounds.left; const maxLeft = trackBounds.right - thumbWidth; let clampedLeft = Math.min(maxLeft, Math.max(minLeft, newThumbLeft)); // compute translateX = clampedLeft - originalLeft (original thumb position relative to track) // easier: get current transform? we compute relative offset from initial position. // but we want offset relative to left start = 0px. const trackRectCached = trackContainer.getBoundingClientRect(); const offsetFromTrackStart = clampedLeft - trackRectCached.left; let translateValue = Math.min(maxOffset, Math.max(0, offsetFromTrackStart)); // apply to thumb if(thumb) thumb.style.transform = `translateX($translateValuepx)`; currentTranslateX = translateValue; // update fill width based on progress updateFillAndLabel(translateValue); // if fully slid, performShutdown will be called inside setThumbOffset logic ( but we call manually also ) if (!shutdownTriggered && maxOffset > 0 && translateValue >= maxOffset - 0.2) // ensure full engagement if(translateValue >= maxOffset - 0.01) performShutdown(); else // snap to max if close enough? if(translateValue > maxOffset - 1 && translateValue < maxOffset) setThumbOffset(maxOffset, true);
.power-icon font-size: 4.2rem; filter: drop-shadow(0 4px 8px rgba(0,0,0,0.3)); margin-bottom: 0.75rem;
// move thumb to a specific offset (clamped 0..maxOffset) function setThumbOffset(offset, updateFill=true) let clamped = Math.min(maxOffset, Math.max(0, offset)); if (thumb) thumb.style.transform = `translateX($clampedpx)`; currentTranslateX = clamped; if (updateFill) updateFillAndLabel(clamped); // check if reached shutdown threshold (>= maxOffset) if (!shutdownTriggered && maxOffset > 0 && clamped >= maxOffset - 0.5) // fully slid: trigger shutdown! performShutdown();