Spaces:
Running
Running
Ctrl+K
- 1.52 kB initial commit
- 209 Bytes <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Zivvy - School Profile</title> <style> :root { --zivvy-black: #000000; --zivvy-white: #ffffff; --zivvy-purple: #6b47eb; --zivvy-dark-purple: #510d54; --zivvy-darkest-purple: #302326; --zivvy-yellow: #f9c80e; --zivvy-blue: #256eff; --zivvy-light-blue: #cddeff; --zivvy-light-yellow: #efe3ac; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, var(--zivvy-darkest-purple) 0%, var(--zivvy-dark-purple) 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; overflow-x: hidden; position: relative; } /* Animated Background */ .bg-shapes { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; } .shape { position: absolute; background: var(--zivvy-purple); opacity: 0.1; animation: float 20s infinite ease-in-out; } .shape:nth-child(1) { width: 300px; height: 300px; border-radius: 50%; top: -150px; right: -150px; animation-duration: 25s; } .shape:nth-child(2) { width: 200px; height: 200px; border-radius: 50%; bottom: -100px; left: -100px; animation-duration: 30s; animation-delay: 5s; } @keyframes float { 0%, 100% { transform: translate(0, 0) rotate(0deg); } 33% { transform: translate(30px, -30px) rotate(120deg); } 66% { transform: translate(-20px, 20px) rotate(240deg); } } /* Main Container */ .container { position: relative; z-index: 1; width: 100%; max-width: 600px; padding: 20px; } /* Progress Indicator */ .progress-container { text-align: center; margin-bottom: 40px; animation: slideDown 0.6s ease-out; } .progress-title { color: var(--zivvy-light-yellow); font-size: 14px; margin-bottom: 15px; opacity: 0.9; } .progress-coins { display: flex; justify-content: center; gap: 15px; } .coin { width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(135deg, var(--zivvy-yellow) 0%, #ffd23f 100%); display: flex; align-items: center; justify-content: center; font-weight: bold; color: var(--zivvy-darkest-purple); transition: all 0.3s ease; position: relative; box-shadow: 0 4px 15px rgba(249, 200, 14, 0.3); } .coin.active { transform: scale(1.2); box-shadow: 0 6px 25px rgba(249, 200, 14, 0.5); animation: pulse 2s infinite; } .coin.completed { background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%); box-shadow: 0 4px 15px rgba(34, 197, 94, 0.3); } @keyframes pulse { 0%, 100% { transform: scale(1.2); } 50% { transform: scale(1.3); } } @keyframes slideDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } /* Profile Card */ .profile-card { background: rgba(255, 255, 255, 0.98); border-radius: 20px; padding: 40px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); animation: slideUp 0.6s ease-out; } @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .card-header { text-align: center; margin-bottom: 40px; } .card-title { font-size: 28px; color: var(--zivvy-darkest-purple); margin-bottom: 10px; font-weight: 700; } .card-subtitle { color: #666; font-size: 16px; } /* Dial Controls Container */ .dials-container { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; margin-bottom: 40px; } /* Circular Dial */ .dial-group { text-align: center; } .dial-label { font-size: 16px; color: var(--zivvy-darkest-purple); font-weight: 600; margin-bottom: 20px; } .dial-wrapper { position: relative; width: 200px; height: 200px; margin: 0 auto 20px; } .dial-svg { width: 100%; height: 100%; transform: rotate(-90deg); } .dial-background { fill: none; stroke: #e0e0e0; stroke-width: 15; } .dial-progress { fill: none; stroke: url(#gradient); stroke-width: 15; stroke-linecap: round; transition: stroke-dashoffset 0.4s ease; } .dial-handle { position: absolute; width: 30px; height: 30px; background: white; border: 3px solid var(--zivvy-purple); border-radius: 50%; top: 50%; left: 50%; transform-origin: center; cursor: grab; box-shadow: 0 4px 15px rgba(107, 71, 235, 0.3); transition: all 0.3s ease; } .dial-handle:active { cursor: grabbing; transform: scale(1.2); box-shadow: 0 6px 20px rgba(107, 71, 235, 0.5); } .dial-value { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 32px; font-weight: 700; color: var(--zivvy-purple); pointer-events: none; } .dial-range { font-size: 14px; color: #666; display: flex; justify-content: space-between; padding: 0 20px; } /* Submit Button */ .submit-btn { width: 100%; padding: 18px; font-size: 18px; font-weight: 700; color: white; background: linear-gradient(135deg, var(--zivvy-purple) 0%, var(--zivvy-blue) 100%); border: none; border-radius: 10px; cursor: pointer; position: relative; overflow: hidden; transition: all 0.3s ease; text-transform: uppercase; letter-spacing: 1px; opacity: 0.7; pointer-events: none; } .submit-btn.enabled { opacity: 1; pointer-events: auto; } .submit-btn::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); animation: shimmerBtn 3s infinite; } @keyframes shimmerBtn { 0% { left: -100%; } 100% { left: 100%; } } .submit-btn.enabled:hover { transform: translateY(-2px); box-shadow: 0 10px 30px rgba(107, 71, 235, 0.3); } .submit-btn.enabled:active { transform: scale(0.98); } /* Celebration Elements */ .celebration { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1000; } .confetti { position: absolute; width: 10px; height: 10px; background: var(--zivvy-yellow); opacity: 0; } @keyframes confettiFall { 0% { opacity: 1; transform: translateY(-100vh) rotate(0deg); } 100% { opacity: 0; transform: translateY(100vh) rotate(720deg); } } /* Milestone Toast */ .milestone-toast { position: fixed; top: 20px; right: 20px; background: white; padding: 20px 30px; border-radius: 10px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); display: flex; align-items: center; gap: 15px; opacity: 0; transform: translateX(100%); transition: all 0.5s ease; z-index: 1001; } .milestone-toast.show { opacity: 1; transform: translateX(0); } .milestone-icon { width: 40px; height: 40px; background: linear-gradient(135deg, var(--zivvy-yellow) 0%, #ffd23f 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 20px; animation: bounce 0.5s ease; } @keyframes bounce { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.2); } } .milestone-text { color: var(--zivvy-darkest-purple); } .milestone-title { font-weight: 700; margin-bottom: 2px; } .milestone-subtitle { font-size: 14px; color: #666; } /* Mobile Responsive */ @media (max-width: 600px) { .dials-container { grid-template-columns: 1fr; gap: 30px; } .dial-wrapper { width: 180px; height: 180px; } .profile-card { padding: 30px 20px; } .card-title { font-size: 24px; } .milestone-toast { right: 10px; left: 10px; transform: translateY(-100%); } .milestone-toast.show { transform: translateY(0); } } </style> </head> <body> <!-- Animated Background --> <div class="bg-shapes"> <div class="shape"></div> <div class="shape"></div> </div> <!-- Main Container --> <div class="container"> <!-- Progress Indicator --> <div class="progress-container"> <div class="progress-title">Your Journey to Hidden Revenue</div> <div class="progress-coins"> <div class="coin completed">1</div> <div class="coin completed">2</div> <div class="coin active">3</div> <div class="coin">4</div> <div class="coin">5</div> <div class="coin">6</div> </div> </div> <!-- Profile Card --> <div class="profile-card"> <div class="card-header"> <h1 class="card-title">Tell Us About Your School</h1> <p class="card-subtitle">This helps us calculate your hidden revenue potential</p> </div> <!-- Dials Container --> <div class="dials-container"> <!-- Students Dial --> <div class="dial-group"> <div class="dial-label">Active Students</div> <div class="dial-wrapper"> <svg class="dial-svg" viewBox="0 0 200 200"> <defs> <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" style="stop-color:#6b47eb" /> <stop offset="100%" style="stop-color:#256eff" /> </linearGradient> </defs> <circle cx="100" cy="100" r="85" class="dial-background"></circle> <circle cx="100" cy="100" r="85" class="dial-progress" stroke-dasharray="534.07" stroke-dashoffset="534.07" id="studentsDial"></circle> </svg> <div class="dial-handle" id="studentsHandle"></div> <div class="dial-value" id="studentsValue">0</div> </div> <div class="dial-range"> <span>0</span> <span>500</span> </div> </div> <!-- Tuition Dial --> <div class="dial-group"> <div class="dial-label">Average Monthly Tuition</div> <div class="dial-wrapper"> <svg class="dial-svg" viewBox="0 0 200 200"> <circle cx="100" cy="100" r="85" class="dial-background"></circle> <circle cx="100" cy="100" r="85" class="dial-progress" stroke-dasharray="534.07" stroke-dashoffset="534.07" id="tuitionDial"></circle> </svg> <div class="dial-handle" id="tuitionHandle"></div> <div class="dial-value" id="tuitionValue">$50</div> </div> <div class="dial-range"> <span>$50</span> <span>$1,000</span> </div> </div> </div> <button class="submit-btn" id="submitBtn"> Calculate Hidden Revenue </button> </div> </div> <!-- Celebration Container --> <div class="celebration" id="celebration"></div> <!-- Milestone Toast --> <div class="milestone-toast" id="milestoneToast"> <div class="milestone-icon">๐</div> <div class="milestone-text"> <div class="milestone-title">Great School Size!</div> <div class="milestone-subtitle">Perfect for maximizing revenue recovery</div> </div> </div> <script> // Dial functionality class Dial { constructor(dialId, handleId, valueId, min, max, prefix, suffix) { this.dial = document.getElementById(dialId); this.handle = document.getElementById(handleId); this.valueDisplay = document.getElementById(valueId); this.min = min; this.max = max; this.prefix = prefix || ''; this.suffix = suffix || ''; this.value = min; this.angle = 0; this.isDragging = false; this.circumference = 2 * Math.PI * 85; this.dial.style.strokeDasharray = this.circumference; this.init(); } init() { this.handle.addEventListener('mousedown', this.startDrag.bind(this)); document.addEventListener('mousemove', this.drag.bind(this)); document.addEventListener('mouseup', this.stopDrag.bind(this)); // Touch events this.handle.addEventListener('touchstart', this.startDrag.bind(this)); document.addEventListener('touchmove', this.drag.bind(this)); document.addEventListener('touchend', this.stopDrag.bind(this)); // Set initial position this.updateValue(this.min); } startDrag(e) { e.preventDefault(); this.isDragging = true; this.handle.style.cursor = 'grabbing'; } drag(e) { if (!this.isDragging) return; e.preventDefault(); const rect = this.handle.parentElement.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; let clientX, clientY; if (e.touches) { clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; } else { clientX = e.clientX; clientY = e.clientY; } const angle = Math.atan2(clientY - centerY, clientX - centerX); let degrees = angle * (180 / Math.PI) + 90; if (degrees < 0) degrees += 360; if (degrees > 270) degrees = 270; this.angle = degrees; const percentage = degrees / 270; const value = this.min + (this.max - this.min) * percentage; this.updateValue(value); } stopDrag() { this.isDragging = false; this.handle.style.cursor = 'grab'; } updateValue(value) { this.value = Math.round(value); const percentage = (this.value - this.min) / (this.max - this.min); this.angle = percentage * 270; // Update dial progress const offset = this.circumference - (percentage * this.circumference * 0.75); this.dial.style.strokeDashoffset = offset; // Update handle position const radian = (this.angle - 90) * (Math.PI / 180); const x = 100 + 85 * Math.cos(radian); const y = 100 + 85 * Math.sin(radian); this.handle.style.left = x + 'px'; this.handle.style.top = y + 'px'; this.handle.style.transform = 'translate(-50%, -50%)'; // Update value display this.valueDisplay.textContent = this.prefix + this.value.toLocaleString() + this.suffix; // Check if both dials have values checkFormCompletion(); } } // Declare variables in proper scope let studentsDial; let tuitionDial; // Form completion check function checkFormCompletion() { const submitBtn = document.getElementById('submitBtn'); if (studentsDial && tuitionDial && studentsDial.value > 0 && tuitionDial.value > 50) { submitBtn.classList.add('enabled'); } else { submitBtn.classList.remove('enabled'); } } // Milestones const milestones = { students: [ { value: 100, title: 'Growing School!', subtitle: 'Great foundation for growth' }, { value: 200, title: 'Established School!', subtitle: 'Perfect for revenue optimization' }, { value: 300, title: 'Large School!', subtitle: 'Massive revenue potential' }, { value: 400, title: 'Elite School!', subtitle: 'Top-tier opportunity awaits' } ], tuition: [ { value: 150, title: 'Smart Pricing!', subtitle: 'Room for optimization' }, { value: 300, title: 'Premium Value!', subtitle: 'Students value your training' }, { value: 500, title: 'High-Value Program!', subtitle: 'Excellent revenue per student' }, { value: 750, title: 'Elite Pricing!', subtitle: 'Maximum revenue potential' } ] }; let lastStudentMilestone = 0; let lastTuitionMilestone = 0; function checkMilestone(value, milestoneList, lastMilestone, type) { for (let milestone of milestoneList) { if (value >= milestone.value && lastMilestone < milestone.value) { showMilestone(milestone.title, milestone.subtitle); triggerCelebration(); return milestone.value; } } return lastMilestone; } function showMilestone(title, subtitle) { const toast = document.getElementById('milestoneToast'); toast.querySelector('.milestone-title').textContent = title; toast.querySelector('.milestone-subtitle').textContent = subtitle; toast.classList.add('show'); setTimeout(() => { toast.classList.remove('show'); }, 3000); } function triggerCelebration() { const celebration = document.getElementById('celebration'); // Create confetti for (let i = 0; i < 50; i++) { const confetti = document.createElement('div'); confetti.className = 'confetti'; confetti.style.left = Math.random() * 100 + '%'; confetti.style.backgroundColor = Math.random() > 0.5 ? 'var(--zivvy-yellow)' : 'var(--zivvy-purple)'; confetti.style.animationDelay = Math.random() * 0.5 + 's'; confetti.style.animationDuration = (Math.random() * 1 + 1) + 's'; confetti.style.animation = 'confettiFall ' + confetti.style.animationDuration + ' ease-out'; celebration.appendChild(confetti); setTimeout(() => { confetti.remove(); }, 2000); } } // Initialize dials studentsDial = new Dial('studentsDial', 'studentsHandle', 'studentsValue', 0, 500, '', ''); tuitionDial = new Dial('tuitionDial', 'tuitionHandle', 'tuitionValue', 50, 1000, '$', ''); // Override dial update to check milestones const originalStudentUpdate = studentsDial.updateValue.bind(studentsDial); studentsDial.updateValue = function(value) { originalStudentUpdate(value); lastStudentMilestone = checkMilestone(this.value, milestones.students, lastStudentMilestone, 'students'); }; const originalTuitionUpdate = tuitionDial.updateValue.bind(tuitionDial); tuitionDial.updateValue = function(value) { originalTuitionUpdate(value); lastTuitionMilestone = checkMilestone(this.value, milestones.tuition, lastTuitionMilestone, 'tuition'); }; // Form submission document.getElementById('submitBtn').addEventListener('click', function() { if (!this.classList.contains('enabled')) return; // Trigger big celebration triggerCelebration(); // In real implementation, save data and redirect to page 4 console.log('School Profile:', { students: studentsDial.value, tuition: tuitionDial.value, potentialRevenue: studentsDial.value * tuitionDial.value * 12 }); const annualRevenue = (studentsDial.value * tuitionDial.value * 12).toLocaleString(); setTimeout(() => { alert('School profile saved! Potential annual revenue: $' + annualRevenue + '\n\n(In production, this would redirect to Opportunity Categories page)'); }, 1000); }); </script> </body> </html> - Initial Deployment
- 13.5 kB <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Zivvy - School Profile</title> <style> :root { --zivvy-black: #000000; --zivvy-white: #ffffff; --zivvy-purple: #6b47eb; --zivvy-dark-purple: #510d54; --zivvy-darkest-purple: #302326; --zivvy-yellow: #f9c80e; --zivvy-blue: #256eff; --zivvy-light-blue: #cddeff; --zivvy-light-yellow: #efe3ac; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, var(--zivvy-darkest-purple) 0%, var(--zivvy-dark-purple) 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; overflow-x: hidden; position: relative; } /* Animated Background */ .bg-shapes { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; } .shape { position: absolute; background: var(--zivvy-purple); opacity: 0.1; animation: float 20s infinite ease-in-out; } .shape:nth-child(1) { width: 300px; height: 300px; border-radius: 50%; top: -150px; right: -150px; animation-duration: 25s; } .shape:nth-child(2) { width: 200px; height: 200px; border-radius: 50%; bottom: -100px; left: -100px; animation-duration: 30s; animation-delay: 5s; } @keyframes float { 0%, 100% { transform: translate(0, 0) rotate(0deg); } 33% { transform: translate(30px, -30px) rotate(120deg); } 66% { transform: translate(-20px, 20px) rotate(240deg); } } /* Main Container */ .container { position: relative; z-index: 1; width: 100%; max-width: 600px; padding: 20px; } /* Progress Indicator */ .progress-container { text-align: center; margin-bottom: 40px; animation: slideDown 0.6s ease-out; } .progress-title { color: var(--zivvy-light-yellow); font-size: 14px; margin-bottom: 15px; opacity: 0.9; } .progress-coins { display: flex; justify-content: center; gap: 15px; } .coin { width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(135deg, var(--zivvy-yellow) 0%, #ffd23f 100%); display: flex; align-items: center; justify-content: center; font-weight: bold; color: var(--zivvy-darkest-purple); transition: all 0.3s ease; position: relative; box-shadow: 0 4px 15px rgba(249, 200, 14, 0.3); } .coin.active { transform: scale(1.2); box-shadow: 0 6px 25px rgba(249, 200, 14, 0.5); animation: pulse 2s infinite; } .coin.completed { background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%); box-shadow: 0 4px 15px rgba(34, 197, 94, 0.3); } @keyframes pulse { 0%, 100% { transform: scale(1.2); } 50% { transform: scale(1.3); } } @keyframes slideDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } /* Profile Card */ .profile-card { background: rgba(255, 255, 255, 0.98); border-radius: 20px; padding: 40px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); animation: slideUp 0.6s ease-out; } @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .card-header { text-align: center; margin-bottom: 40px; } .card-title { font-size: 28px; color: var(--zivvy-darkest-purple); margin-bottom: 10px; font-weight: 700; } .card-subtitle { color: #666; font-size: 16px; } /* Dial Controls Container */ .dials-container { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; margin-bottom: 40px; } /* Circular Dial */ .dial-group { text-align: center; } .dial-label { font-size: 16px; color: var(--zivvy-darkest-purple); font-weight: 600; margin-bottom: 20px; } .dial-wrapper { position: relative; width: 200px; height: 200px; margin: 0 auto 20px; } .dial-svg { width: 100%; height: 100%; transform: rotate(-90deg); } .dial-background { fill: none; stroke: #e0e0e0; stroke-width: 15; } .dial-progress { fill: none; stroke: url(#gradient); stroke-width: 15; stroke-linecap: round; transition: stroke-dashoffset 0.4s ease; } .dial-handle { position: absolute; width: 30px; height: 30px; background: white; border: 3px solid var(--zivvy-purple); border-radius: 50%; top: 50%; left: 50%; transform-origin: center; cursor: grab; box-shadow: 0 4px 15px rgba(107, 71, 235, 0.3); transition: all 0.3s ease; } .dial-handle:active { cursor: grabbing; transform: scale(1.2); box-shadow: 0 6px 20px rgba(107, 71, 235, 0.5); } .dial-value { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 32px; font-weight: 700; color: var(--zivvy-purple); pointer-events: none; } .dial-range { font-size: 14px; color: #666; display: flex; justify-content: space-between; padding: 0 20px; } /* Submit Button */ .submit-btn { width: 100%; padding: 18px; font-size: 18px; font-weight: 700; color: white; background: linear-gradient(135deg, var(--zivvy-purple) 0%, var(--zivvy-blue) 100%); border: none; border-radius: 10px; cursor: pointer; position: relative; overflow: hidden; transition: all 0.3s ease; text-transform: uppercase; letter-spacing: 1px; opacity: 0.7; pointer-events: none; } .submit-btn.enabled { opacity: 1; pointer-events: auto; } .submit-btn::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); animation: shimmerBtn 3s infinite; } @keyframes shimmerBtn { 0% { left: -100%; } 100% { left: 100%; } } .submit-btn.enabled:hover { transform: translateY(-2px); box-shadow: 0 10px 30px rgba(107, 71, 235, 0.3); } .submit-btn.enabled:active { transform: scale(0.98); } /* Celebration Elements */ .celebration { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1000; } .confetti { position: absolute; width: 10px; height: 10px; background: var(--zivvy-yellow); opacity: 0; } @keyframes confettiFall { 0% { opacity: 1; transform: translateY(-100vh) rotate(0deg); } 100% { opacity: 0; transform: translateY(100vh) rotate(720deg); } } /* Milestone Toast */ .milestone-toast { position: fixed; top: 20px; right: 20px; background: white; padding: 20px 30px; border-radius: 10px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); display: flex; align-items: center; gap: 15px; opacity: 0; transform: translateX(100%); transition: all 0.5s ease; z-index: 1001; } .milestone-toast.show { opacity: 1; transform: translateX(0); } .milestone-icon { width: 40px; height: 40px; background: linear-gradient(135deg, var(--zivvy-yellow) 0%, #ffd23f 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 20px; animation: bounce 0.5s ease; } @keyframes bounce { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.2); } } .milestone-text { color: var(--zivvy-darkest-purple); } .milestone-title { font-weight: 700; margin-bottom: 2px; } .milestone-subtitle { font-size: 14px; color: #666; } /* Mobile Responsive */ @media (max-width: 600px) { .dials-container { grid-template-columns: 1fr; gap: 30px; } .dial-wrapper { width: 180px; height: 180px; } .profile-card { padding: 30px 20px; } .card-title { font-size: 24px; } .milestone-toast { right: 10px; left: 10px; transform: translateY(-100%); } .milestone-toast.show { transform: translateY(0); } } </style> </head> <body> <!-- Animated Background --> <div class="bg-shapes"> <div class="shape"></div> <div class="shape"></div> </div> <!-- Main Container --> <div class="container"> <!-- Progress Indicator --> <div class="progress-container"> <div class="progress-title">Your Journey to Hidden Revenue</div> <div class="progress-coins"> <div class="coin completed">1</div> <div class="coin completed">2</div> <div class="coin active">3</div> <div class="coin">4</div> <div class="coin">5</div> <div class="coin">6</div> </div> </div> <!-- Profile Card --> <div class="profile-card"> <div class="card-header"> <h1 class="card-title">Tell Us About Your School</h1> <p class="card-subtitle">This helps us calculate your hidden revenue potential</p> </div> <!-- Dials Container --> <div class="dials-container"> <!-- Students Dial --> <div class="dial-group"> <div class="dial-label">Active Students</div> <div class="dial-wrapper"> <svg class="dial-svg" viewBox="0 0 200 200"> <defs> <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" style="stop-color:#6b47eb" /> <stop offset="100%" style="stop-color:#256eff" /> </linearGradient> </defs> <circle cx="100" cy="100" r="85" class="dial-background"></circle> <circle cx="100" cy="100" r="85" class="dial-progress" stroke-dasharray="534.07" stroke-dashoffset="534.07" id="studentsDial"></circle> </svg> <div class="dial-handle" id="studentsHandle"></div> <div class="dial-value" id="studentsValue">0</div> </div> <div class="dial-range"> <span>0</span> <span>500</span> </div> </div> <!-- Tuition Dial --> <div class="dial-group"> <div class="dial-label">Average Monthly Tuition</div> <div class="dial-wrapper"> <svg class="dial-svg" viewBox="0 0 200 200"> <circle cx="100" cy="100" r="85" class="dial-background"></circle> <circle cx="100" cy="100" r="85" class="dial-progress" stroke-dasharray="534.07" stroke-dashoffset="534.07" id="tuitionDial"></circle> </svg> <div class="dial-handle" id="tuitionHandle"></div> <div class="dial-value" id="tuitionValue">$50</div> </div> <div class="dial-range"> <span>$50</span> <span>$1,000</span> </div> </div> </div> <button class="submit-btn" id="submitBtn"> Calculate Hidden Revenue </button> </div> </div> <!-- Celebration Container --> <div class="celebration" id="celebration"></div> <!-- Milestone Toast --> <div class="milestone-toast" id="milestoneToast"> <div class="milestone-icon">๐</div> <div class="milestone-text"> <div class="milestone-title">Great School Size!</div> <div class="milestone-subtitle">Perfect for maximizing revenue recovery</div> </div> </div> <script> // Dial functionality class Dial { constructor(dialId, handleId, valueId, min, max, prefix, suffix) { this.dial = document.getElementById(dialId); this.handle = document.getElementById(handleId); this.valueDisplay = document.getElementById(valueId); this.min = min; this.max = max; this.prefix = prefix || ''; this.suffix = suffix || ''; this.value = min; this.angle = 0; this.isDragging = false; this.circumference = 2 * Math.PI * 85; this.dial.style.strokeDasharray = this.circumference; this.init(); } init() { this.handle.addEventListener('mousedown', this.startDrag.bind(this)); document.addEventListener('mousemove', this.drag.bind(this)); document.addEventListener('mouseup', this.stopDrag.bind(this)); // Touch events this.handle.addEventListener('touchstart', this.startDrag.bind(this)); document.addEventListener('touchmove', this.drag.bind(this)); document.addEventListener('touchend', this.stopDrag.bind(this)); // Set initial position this.updateValue(this.min); } startDrag(e) { e.preventDefault(); this.isDragging = true; this.handle.style.cursor = 'grabbing'; } drag(e) { if (!this.isDragging) return; e.preventDefault(); const rect = this.handle.parentElement.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; let clientX, clientY; if (e.touches) { clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; } else { clientX = e.clientX; clientY = e.clientY; } const angle = Math.atan2(clientY - centerY, clientX - centerX); let degrees = angle * (180 / Math.PI) + 90; if (degrees < 0) degrees += 360; if (degrees > 270) degrees = 270; this.angle = degrees; const percentage = degrees / 270; const value = this.min + (this.max - this.min) * percentage; this.updateValue(value); } stopDrag() { this.isDragging = false; this.handle.style.cursor = 'grab'; } updateValue(value) { this.value = Math.round(value); const percentage = (this.value - this.min) / (this.max - this.min); this.angle = percentage * 270; // Update dial progress const offset = this.circumference - (percentage * this.circumference * 0.75); this.dial.style.strokeDashoffset = offset; // Update handle position const radian = (this.angle - 90) * (Math.PI / 180); const x = 100 + 85 * Math.cos(radian); const y = 100 + 85 * Math.sin(radian); this.handle.style.left = x + 'px'; this.handle.style.top = y + 'px'; this.handle.style.transform = 'translate(-50%, -50%)'; // Update value display this.valueDisplay.textContent = this.prefix + this.value.toLocaleString() + this.suffix; // Check if both dials have values checkFormCompletion(); } } // Declare variables in proper scope let studentsDial; let tuitionDial; // Form completion check function checkFormCompletion() { const submitBtn = document.getElementById('submitBtn'); if (studentsDial && tuitionDial && studentsDial.value > 0 && tuitionDial.value > 50) { submitBtn.classList.add('enabled'); } else { submitBtn.classList.remove('enabled'); } } // Milestones const milestones = { students: [ { value: 100, title: 'Growing School!', subtitle: 'Great foundation for growth' }, { value: 200, title: 'Established School!', subtitle: 'Perfect for revenue optimization' }, { value: 300, title: 'Large School!', subtitle: 'Massive revenue potential' }, { value: 400, title: 'Elite School!', subtitle: 'Top-tier opportunity awaits' } ], tuition: [ { value: 150, title: 'Smart Pricing!', subtitle: 'Room for optimization' }, { value: 300, title: 'Premium Value!', subtitle: 'Students value your training' }, { value: 500, title: 'High-Value Program!', subtitle: 'Excellent revenue per student' }, { value: 750, title: 'Elite Pricing!', subtitle: 'Maximum revenue potential' } ] }; let lastStudentMilestone = 0; let lastTuitionMilestone = 0; function checkMilestone(value, milestoneList, lastMilestone, type) { for (let milestone of milestoneList) { if (value >= milestone.value && lastMilestone < milestone.value) { showMilestone(milestone.title, milestone.subtitle); triggerCelebration(); return milestone.value; } } return lastMilestone; } function showMilestone(title, subtitle) { const toast = document.getElementById('milestoneToast'); toast.querySelector('.milestone-title').textContent = title; toast.querySelector('.milestone-subtitle').textContent = subtitle; toast.classList.add('show'); setTimeout(() => { toast.classList.remove('show'); }, 3000); } function triggerCelebration() { const celebration = document.getElementById('celebration'); // Create confetti for (let i = 0; i < 50; i++) { const confetti = document.createElement('div'); confetti.className = 'confetti'; confetti.style.left = Math.random() * 100 + '%'; confetti.style.backgroundColor = Math.random() > 0.5 ? 'var(--zivvy-yellow)' : 'var(--zivvy-purple)'; confetti.style.animationDelay = Math.random() * 0.5 + 's'; confetti.style.animationDuration = (Math.random() * 1 + 1) + 's'; confetti.style.animation = 'confettiFall ' + confetti.style.animationDuration + ' ease-out'; celebration.appendChild(confetti); setTimeout(() => { confetti.remove(); }, 2000); } } // Initialize dials studentsDial = new Dial('studentsDial', 'studentsHandle', 'studentsValue', 0, 500, '', ''); tuitionDial = new Dial('tuitionDial', 'tuitionHandle', 'tuitionValue', 50, 1000, '$', ''); // Override dial update to check milestones const originalStudentUpdate = studentsDial.updateValue.bind(studentsDial); studentsDial.updateValue = function(value) { originalStudentUpdate(value); lastStudentMilestone = checkMilestone(this.value, milestones.students, lastStudentMilestone, 'students'); }; const originalTuitionUpdate = tuitionDial.updateValue.bind(tuitionDial); tuitionDial.updateValue = function(value) { originalTuitionUpdate(value); lastTuitionMilestone = checkMilestone(this.value, milestones.tuition, lastTuitionMilestone, 'tuition'); }; // Form submission document.getElementById('submitBtn').addEventListener('click', function() { if (!this.classList.contains('enabled')) return; // Trigger big celebration triggerCelebration(); // In real implementation, save data and redirect to page 4 console.log('School Profile:', { students: studentsDial.value, tuition: tuitionDial.value, potentialRevenue: studentsDial.value * tuitionDial.value * 12 }); const annualRevenue = (studentsDial.value * tuitionDial.value * 12).toLocaleString(); setTimeout(() => { alert('School profile saved! Potential annual revenue: $' + annualRevenue + '\n\n(In production, this would redirect to Opportunity Categories page)'); }, 1000); }); </script> </body> </html> - Initial Deployment
- 388 Bytes initial commit