.timer-page { display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding-top: 80rpx; padding-bottom: calc(170rpx + env(safe-area-inset-bottom)); min-height: 100vh; box-sizing: border-box; } /* ---- ring wrapper ---- */ .ring-wrapper { position: relative; display: flex; align-items: center; justify-content: center; } /* breathing guide rings — three concentric layers */ .breathe-ring { position: absolute; border-radius: 50%; border: 3rpx solid rgba(var(--primary-rgb), 0.15); animation: breathePulse 3.5s ease-in-out infinite; pointer-events: none; z-index: 0; } .breathe-ring-1 { width: 480rpx; height: 480rpx; animation-delay: 0s; } .breathe-ring-2 { width: 400rpx; height: 400rpx; animation-delay: 0.5s; border-color: rgba(var(--primary-rgb), 0.25); border-width: 4rpx; } .breathe-ring-3 { width: 320rpx; height: 320rpx; animation-delay: 1s; border-color: rgba(var(--primary-rgb), 0.35); border-width: 5rpx; } .breathe-ring.fast { animation: breathePulseFast 1.4s ease-in-out infinite; } .breathe-ring.fast.breathe-ring-1 { animation-delay: 0s; border-color: rgba(var(--primary-rgb), 0.3); } .breathe-ring.fast.breathe-ring-2 { animation-delay: 0.25s; border-color: rgba(var(--primary-rgb), 0.45); } .breathe-ring.fast.breathe-ring-3 { animation-delay: 0.5s; border-color: rgba(var(--primary-rgb), 0.6); } /* celebration burst */ .celebrate-burst { position: absolute; width: 520rpx; height: 520rpx; pointer-events: none; z-index: 2; } .spark { position: absolute; font-size: 36rpx; animation: sparkBurst 1s ease-out forwards; } .s1 { top: -10rpx; left: 50%; margin-left: -18rpx; animation-delay: 0s; } .s2 { top: 30rpx; right: -10rpx; animation-delay: 0.1s; } .s3 { bottom: -10rpx; right: 50rpx; animation-delay: 0.2s; } .s4 { bottom: 20rpx; left: 30rpx; animation-delay: 0.15s; } .s5 { top: 50%; right: -20rpx; animation-delay: 0.25s; } .s6 { top: -20rpx; left: 40rpx; animation-delay: 0.05s; } @keyframes sparkBurst { 0% { opacity: 0; transform: translate(0, 0) scale(0.3); } 40% { opacity: 1; transform: translate(0, -30rpx) scale(1.3); } 100% { opacity: 0; transform: translate(0, -80rpx) scale(0.4); } } /* overtime badge */ .overtime-badge { position: absolute; bottom: 10rpx; left: 50%; transform: translateX(-50%); background: rgba(76, 175, 80, 0.12); border: 2rpx solid rgba(76, 175, 80, 0.3); color: var(--success); padding: 8rpx 24rpx; border-radius: 24rpx; font-size: 28rpx; font-weight: 600; display: flex; align-items: center; gap: 6rpx; animation: popIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); } .overtime-icon { width: 28rpx; height: 28rpx; } /* ---- hint section ---- */ .hint-section { margin-top: 36rpx; text-align: center; } .hint-row { display: inline-flex; align-items: center; justify-content: center; gap: 10rpx; } .hint-icon { width: 32rpx; height: 32rpx; } .running-pulse { animation: float 1.5s ease-in-out infinite; } .completed-bounce { animation: bounceSoft 0.6s ease infinite; } .hint-text { font-size: 28rpx; color: var(--text-secondary); line-height: 1.3; } .hint-text.running { color: var(--primary); font-weight: 600; } .hint-text.paused { color: var(--primary-light); } .hint-text.completed { color: var(--success); font-weight: 600; } .completed-hint { animation: popIn 0.5s ease; } /* ---- controls ---- */ .controls { margin-top: 60rpx; width: 100%; display: flex; justify-content: center; } .ctrl-row { display: flex; justify-content: center; align-items: center; } /* ---- full-screen confetti ---- */ .confetti { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 9999; overflow: hidden; } .confetti-piece { position: absolute; top: -20rpx; border-radius: 4rpx; animation: confettiFall 2.4s ease-out forwards; } /* Higher celebration levels: faster / longer fall */ .confetti-piece--milestone, .confetti-piece--first, .confetti-piece--record { animation-duration: 3.2s; } .confetti-piece--first { animation-duration: 4s; } @keyframes confettiFall { 0% { opacity: 1; transform: translateY(0) rotate(0deg); } 100% { opacity: 0.6; transform: translateY(100vh) rotate(720deg); } } /* ---- achievement banner ---- */ .achievement-banner { position: fixed; top: 160rpx; left: 50%; transform: translateX(-50%); background: linear-gradient(135deg, var(--primary), var(--primary-light)); color: #FFFFFF; font-size: 32rpx; font-weight: 700; padding: 24rpx 48rpx; border-radius: 48rpx; box-shadow: 0 8rpx 24rpx rgba(var(--primary-rgb), 0.4); z-index: 10000; animation: achievementIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both, achievementOut 0.4s ease-in 3.5s both; white-space: nowrap; } .achievement-text { display: block; line-height: 1; } @keyframes achievementIn { 0% { opacity: 0; transform: translateX(-50%) scale(0.5); } 100% { opacity: 1; transform: translateX(-50%) scale(1); } } @keyframes achievementOut { 0% { opacity: 1; transform: translateX(-50%) scale(1); } 100% { opacity: 0; transform: translateX(-50%) scale(0.8); } }