| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>SmartFit: AI Workout Generator</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| @keyframes pulse { |
| 0% { transform: scale(1); } |
| 50% { transform: scale(1.05); } |
| 100% { transform: scale(1); } |
| } |
| .pulse-animation { |
| animation: pulse 2s infinite; |
| } |
| .exercise-card { |
| transition: all 0.3s ease; |
| } |
| .exercise-card:hover { |
| transform: translateY(-5px); |
| box-shadow: 0 10px 25px rgba(79, 70, 229, 0.15); |
| } |
| .fade-in { |
| animation: fadeIn 0.5s ease-in forwards; |
| opacity: 0; |
| } |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(20px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| .progress-ring__circle { |
| transition: stroke-dashoffset 0.75s ease-in-out; |
| transform: rotate(-90deg); |
| transform-origin: 50% 50%; |
| } |
| .input-slider::-webkit-slider-thumb { |
| -webkit-appearance: none; |
| appearance: none; |
| width: 20px; |
| height: 20px; |
| border-radius: 50%; |
| background: #4f46e5; |
| cursor: pointer; |
| } |
| .input-slider::-moz-range-thumb { |
| width: 20px; |
| height: 20px; |
| border-radius: 50%; |
| background: #4f46e5; |
| cursor: pointer; |
| } |
| </style> |
| </head> |
| <body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen"> |
| <div class="container mx-auto px-4 py-8"> |
| <header class="text-center mb-12"> |
| <h1 class="text-4xl md:text-5xl font-extrabold text-transparent bg-clip-text bg-gradient-to-r from-indigo-600 to-purple-600 mb-2">SmartFit</h1> |
| <p class="text-lg text-gray-600 max-w-2xl mx-auto">Generate personalized workouts tailored to your fitness goals and body metrics</p> |
| </header> |
|
|
| <div class="max-w-5xl mx-auto bg-white rounded-2xl shadow-xl overflow-hidden"> |
| <div class="md:flex"> |
| |
| <div class="md:w-2/5 p-8 bg-gradient-to-br from-indigo-600 to-purple-600 text-white"> |
| <h2 class="text-2xl font-bold mb-6 flex items-center"> |
| <i class="fas fa-cog mr-2"></i> Workout Settings |
| </h2> |
| |
| <div class="space-y-6"> |
| <div> |
| <label class="block text-sm font-medium mb-2">Calories to Burn</label> |
| <div class="relative"> |
| <input type="number" id="calories" min="100" max="1500" step="50" value="400" |
| class="w-full px-4 py-3 rounded-xl bg-indigo-500/50 border border-indigo-400 focus:outline-none focus:ring-2 focus:ring-indigo-300 focus:border-transparent"> |
| <span class="absolute right-4 top-3.5 text-indigo-200">kcal</span> |
| </div> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium mb-2">Duration (minutes)</label> |
| <input type="range" id="duration" min="10" max="90" value="30" step="5" |
| class="w-full h-2 bg-indigo-500/50 rounded-lg appearance-none cursor-pointer input-slider"> |
| <div class="flex justify-between text-xs text-indigo-200 mt-1"> |
| <span>10 min</span> |
| <span id="duration-value">30 min</span> |
| <span>90 min</span> |
| </div> |
| </div> |
| |
| <div class="grid grid-cols-2 gap-4"> |
| <div> |
| <label class="block text-sm font-medium mb-2">Age</label> |
| <div class="relative"> |
| <input type="number" id="age" min="16" max="80" value="30" |
| class="w-full px-4 py-3 rounded-xl bg-indigo-500/50 border border-indigo-400 focus:outline-none focus:ring-2 focus:ring-indigo-300 focus:border-transparent"> |
| <span class="absolute right-4 top-3.5 text-indigo-200">yrs</span> |
| </div> |
| </div> |
| <div> |
| <label class="block text-sm font-medium mb-2">Weight</label> |
| <div class="relative"> |
| <input type="number" id="weight" min="40" max="150" value="70" |
| class="w-full px-4 py-3 rounded-xl bg-indigo-500/50 border border-indigo-400 focus:outline-none focus:ring-2 focus:ring-indigo-300 focus:border-transparent"> |
| <span class="absolute right-4 top-3.5 text-indigo-200">kg</span> |
| </div> |
| </div> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium mb-2">Workout Type</label> |
| <div class="grid grid-cols-3 gap-2"> |
| <button class="type-btn active px-3 py-2 rounded-xl bg-indigo-800 text-white text-sm flex items-center justify-center" data-type="fullbody"> |
| <i class="fas fa-running mr-1"></i> Full Body |
| </button> |
| <button class="type-btn px-3 py-2 rounded-xl bg-indigo-500 hover:bg-indigo-700 text-white text-sm flex items-center justify-center" data-type="strength"> |
| <i class="fas fa-dumbbell mr-1"></i> Strength |
| </button> |
| <button class="type-btn px-3 py-2 rounded-xl bg-indigo-500 hover:bg-indigo-700 text-white text-sm flex items-center justify-center" data-type="cardio"> |
| <i class="fas fa-heartbeat mr-1"></i> Cardio |
| </button> |
| </div> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium mb-2">Intensity Level</label> |
| <div class="flex justify-between"> |
| <span class="text-xs text-indigo-200">Light</span> |
| <span class="text-xs text-indigo-200">Moderate</span> |
| <span class="text-xs text-indigo-200">Intense</span> |
| </div> |
| <input type="range" id="intensity" min="1" max="3" value="2" step="1" |
| class="w-full h-2 bg-indigo-500/50 rounded-lg appearance-none cursor-pointer input-slider mt-1"> |
| </div> |
| |
| <button id="generate-btn" class="w-full py-3 px-4 bg-white text-indigo-700 font-bold rounded-xl hover:bg-indigo-100 transition duration-300 flex items-center justify-center pulse-animation"> |
| <i class="fas fa-bolt mr-2"></i> Generate Workout |
| </button> |
| </div> |
| </div> |
| |
| |
| <div class="md:w-3/5 p-8 bg-white"> |
| <div id="results-placeholder" class="h-full flex flex-col items-center justify-center py-12 text-center"> |
| <div class="w-32 h-32 bg-indigo-100 rounded-full flex items-center justify-center mb-6"> |
| <img src="https://cdn-icons-png.flaticon.com/512/3128/3128857.png" alt="Workout" class="w-20 h-20"> |
| </div> |
| <h3 class="text-xl font-semibold text-gray-700 mb-2">Your Custom Workout Awaits</h3> |
| <p class="text-gray-500 max-w-md">Set your fitness parameters and click the generate button to create your personalized workout plan.</p> |
| </div> |
| |
| <div id="results-container" class="hidden h-full"> |
| <div class="flex justify-between items-center mb-6"> |
| <div> |
| <h2 class="text-2xl font-bold text-gray-800">Your Workout Plan</h2> |
| <div class="flex items-center mt-1"> |
| <span id="workout-type" class="px-2 py-1 rounded-full bg-indigo-100 text-indigo-800 text-xs font-medium mr-2">Full Body</span> |
| <span id="workout-intensity" class="px-2 py-1 rounded-full bg-purple-100 text-purple-800 text-xs font-medium">Moderate</span> |
| </div> |
| </div> |
| <div class="flex space-x-2"> |
| <button id="save-btn" class="px-4 py-1 bg-indigo-100 text-indigo-700 rounded-lg text-sm hover:bg-indigo-200 transition flex items-center"> |
| <i class="fas fa-bookmark mr-1"></i> Save |
| </button> |
| <button id="print-btn" class="px-4 py-1 bg-gray-100 text-gray-700 rounded-lg text-sm hover:bg-gray-200 transition flex items-center"> |
| <i class="fas fa-print mr-1"></i> Print |
| </button> |
| </div> |
| </div> |
| |
| <div class="flex items-center justify-between mb-6 p-5 bg-gradient-to-r from-indigo-50 to-purple-50 rounded-xl border border-indigo-100"> |
| <div> |
| <h3 class="font-medium text-gray-700 flex items-center"> |
| <i class="fas fa-chart-line text-indigo-500 mr-2"></i> Workout Summary |
| </h3> |
| <p class="text-sm text-gray-500" id="workout-summary"></p> |
| </div> |
| <div class="relative w-20 h-20"> |
| <svg class="w-full h-full" viewBox="0 0 36 36"> |
| <path class="text-gray-200" d="M18 2.0845 |
| a 15.9155 15.9155 0 0 1 0 31.831 |
| a 15.9155 15.9155 0 0 1 0 -31.831" fill="none" stroke-width="3" stroke="currentColor"/> |
| <path class="progress-ring__circle text-indigo-600" stroke-dasharray="100, 100" d="M18 2.0845 |
| a 15.9155 15.9155 0 0 1 0 31.831 |
| a 15.9155 15.9155 0 0 1 0 -31.831" fill="none" stroke-width="3" stroke="currentColor"/> |
| <text x="18" y="22.5" class="text-sm font-bold fill-indigo-700" text-anchor="middle" id="calories-progress">0%</text> |
| </svg> |
| </div> |
| </div> |
| |
| <div id="exercises-container" class="space-y-4 max-h-[400px] overflow-y-auto pr-2"> |
| |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="mt-12 text-center text-gray-500 text-sm"> |
| <p>Calorie calculations are estimates and may vary based on individual metabolism and intensity.</p> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| const caloriesInput = document.getElementById('calories'); |
| const durationInput = document.getElementById('duration'); |
| const durationValue = document.getElementById('duration-value'); |
| const ageInput = document.getElementById('age'); |
| const weightInput = document.getElementById('weight'); |
| const typeBtns = document.querySelectorAll('.type-btn'); |
| const intensityInput = document.getElementById('intensity'); |
| const generateBtn = document.getElementById('generate-btn'); |
| const resultsPlaceholder = document.getElementById('results-placeholder'); |
| const resultsContainer = document.getElementById('results-container'); |
| const exercisesContainer = document.getElementById('exercises-container'); |
| const workoutSummary = document.getElementById('workout-summary'); |
| const workoutType = document.getElementById('workout-type'); |
| const workoutIntensity = document.getElementById('workout-intensity'); |
| const caloriesProgress = document.getElementById('calories-progress'); |
| const saveBtn = document.getElementById('save-btn'); |
| const printBtn = document.getElementById('print-btn'); |
| |
| let currentType = 'fullbody'; |
| let currentIntensity = 2; |
| const intensityNames = ['Light', 'Moderate', 'Intense']; |
| |
| |
| durationInput.addEventListener('input', function() { |
| durationValue.textContent = `${this.value} min`; |
| }); |
| |
| |
| intensityInput.addEventListener('input', function() { |
| currentIntensity = parseInt(this.value); |
| }); |
| |
| |
| typeBtns.forEach(btn => { |
| btn.addEventListener('click', function() { |
| typeBtns.forEach(b => b.classList.remove('active', 'bg-indigo-800')); |
| typeBtns.forEach(b => b.classList.add('bg-indigo-500', 'hover:bg-indigo-700')); |
| this.classList.add('active', 'bg-indigo-800'); |
| this.classList.remove('bg-indigo-500', 'hover:bg-indigo-700'); |
| currentType = this.dataset.type; |
| }); |
| }); |
| |
| |
| generateBtn.addEventListener('click', generateWorkout); |
| |
| |
| saveBtn.addEventListener('click', function() { |
| const toast = document.createElement('div'); |
| toast.className = 'fixed bottom-4 right-4 px-4 py-2 bg-green-500 text-white rounded-lg shadow-lg flex items-center'; |
| toast.innerHTML = '<i class="fas fa-check-circle mr-2"></i> Workout saved to your profile!'; |
| document.body.appendChild(toast); |
| setTimeout(() => { |
| toast.classList.add('opacity-0', 'transition-opacity', 'duration-300'); |
| setTimeout(() => toast.remove(), 300); |
| }, 2000); |
| }); |
| |
| |
| printBtn.addEventListener('click', function() { |
| window.print(); |
| }); |
| |
| function generateWorkout() { |
| const calories = parseInt(caloriesInput.value); |
| const duration = parseInt(durationInput.value); |
| const age = parseInt(ageInput.value); |
| const weight = parseInt(weightInput.value); |
| |
| |
| if (isNaN(calories) || isNaN(duration) || isNaN(age) || isNaN(weight)) { |
| showError('Please fill in all fields with valid numbers'); |
| return; |
| } |
| |
| if (age < 16 || age > 80) { |
| showError('Please enter an age between 16 and 80'); |
| return; |
| } |
| |
| if (weight < 40 || weight > 150) { |
| showError('Please enter a weight between 40kg and 150kg'); |
| return; |
| } |
| |
| |
| const exercises = generateExercises(calories, duration, weight, age); |
| |
| |
| displayWorkout(exercises, calories, duration); |
| } |
| |
| function showError(message) { |
| const errorEl = document.createElement('div'); |
| errorEl.className = 'fixed top-4 right-4 px-4 py-2 bg-red-500 text-white rounded-lg shadow-lg flex items-center'; |
| errorEl.innerHTML = `<i class="fas fa-exclamation-circle mr-2"></i> ${message}`; |
| document.body.appendChild(errorEl); |
| setTimeout(() => { |
| errorEl.classList.add('opacity-0', 'transition-opacity', 'duration-300'); |
| setTimeout(() => errorEl.remove(), 300); |
| }, 3000); |
| } |
| |
| function generateExercises(targetCalories, duration, weight, age) { |
| |
| const exerciseDB = { |
| fullbody: [ |
| { name: "Burpees", met: 8.0, sets: 3, reps: 15, rest: 30, desc: "Full-body explosive exercise", video: "https://www.youtube.com/embed/auBLPXO8Fww" }, |
| { name: "Jump Squats", met: 8.0, sets: 3, reps: 12, rest: 45, desc: "Plyometric squat variation", video: "https://www.youtube.com/embed/2R73dAs7QZk" }, |
| { name: "Mountain Climbers", met: 8.0, sets: 3, duration: "30 sec", rest: 30, desc: "Cardio and core exercise", video: "https://www.youtube.com/embed/cnyTQDSE884" }, |
| { name: "Kettlebell Swings", met: 8.0, sets: 3, reps: 20, rest: 45, desc: "Hip-hinging power movement", video: "https://www.youtube.com/embed/mKDIuUbH94Q" }, |
| { name: "Box Jumps", met: 7.5, sets: 3, reps: 10, rest: 60, desc: "Plyometric leg exercise", video: "https://www.youtube.com/embed/5yL9pERqeCE" }, |
| ], |
| strength: [ |
| { name: "Push-ups", met: 3.8, sets: 4, reps: 12, rest: 60, desc: "Classic chest exercise", video: "https://www.youtube.com/embed/IODxDxX7oi4" }, |
| { name: "Pull-ups", met: 4.0, sets: 3, reps: 8, rest: 90, desc: "Upper back development", video: "https://www.youtube.com/embed/eGo4IYlbE5g" }, |
| { name: "Dumbbell Squats", met: 5.0, sets: 4, reps: 12, rest: 60, desc: "Leg strengthening", video: "https://www.youtube.com/embed/ECxYgeX5o0s" }, |
| { name: "Shoulder Press", met: 4.5, sets: 3, reps: 10, rest: 75, desc: "Shoulder development", video: "https://www.youtube.com/embed/qEwKCR5JCog" }, |
| { name: "Deadlifts", met: 6.0, sets: 4, reps: 8, rest: 120, desc: "Posterior chain strength", video: "https://www.youtube.com/embed/VL5Ab0T07e4" }, |
| ], |
| cardio: [ |
| { name: "Running (6 mph)", met: 9.8, duration: "5 min", desc: "Moderate pace running", video: "https://www.youtube.com/embed/5hcm1boZE7Q" }, |
| { name: "Jump Rope", met: 10.0, duration: "3 min", desc: "High intensity skipping", video: "https://www.youtube.com/embed/1BZM2Vre5oc" }, |
| { name: "Rowing Machine", met: 7.0, duration: "10 min", desc: "Full-body cardio", video: "https://www.youtube.com/embed/GXi7x5qOavU" }, |
| { name: "Cycling (moderate)", met: 7.0, duration: "10 min", desc: "Steady state cycling", video: "https://www.youtube.com/embed/9Y_1K6NqMCQ" }, |
| { name: "Stair Climbing", met: 8.0, duration: "5 min", desc: "Lower body endurance", video: "https://www.youtube.com/embed/N4Bx7k8iiF0" }, |
| ] |
| }; |
| |
| |
| const intensityFactor = [0.8, 1.0, 1.2][currentIntensity - 1]; |
| |
| |
| const ageFactor = 1 - ((age - 30) * 0.0025); |
| |
| let availableExercises = [...exerciseDB[currentType]]; |
| |
| |
| const numExercises = Math.min(Math.max( |
| Math.floor(duration / (currentType === 'cardio' ? 5 : 7)), |
| 3), |
| 6 |
| ); |
| |
| const exercises = []; |
| let remainingDuration = duration * 60; |
| let remainingCalories = targetCalories; |
| |
| |
| if (currentType === 'cardio') { |
| const rounds = Math.max(1, Math.min(3, Math.floor(duration / 15))); |
| const perRoundDuration = Math.floor(duration * 60 / rounds); |
| const perRoundCalories = Math.floor(targetCalories / rounds); |
| |
| for (let round = 0; round < rounds; round++) { |
| |
| const selected = []; |
| let roundDuration = 0; |
| let roundCalories = 0; |
| |
| |
| while (roundDuration < perRoundDuration * 0.8 && availableExercises.length > 0) { |
| const randomIndex = Math.floor(Math.random() * availableExercises.length); |
| const exercise = availableExercises[randomIndex]; |
| const exDuration = parseInt(exercise.duration) * 60; |
| |
| |
| const exCalories = Math.round( |
| (exercise.met * intensityFactor * ageFactor * weight * (parseInt(exercise.duration) / 60) * 3.5) / 200 |
| ); |
| |
| selected.push({ |
| ...exercise, |
| duration: exercise.duration, |
| calories: exCalories, |
| isCardio: true |
| }); |
| |
| roundDuration += exDuration; |
| roundCalories += exCalories; |
| availableExercises.splice(randomIndex, 1); |
| } |
| |
| |
| if (selected.length > 0) { |
| const restDuration = Math.floor(perRoundDuration * 0.2 / selected.length); |
| for (let i = 0; i < selected.length; i++) { |
| selected[i].rest = restDuration + " sec"; |
| } |
| |
| exercises.push({ |
| name: `Cardio Round ${round + 1}`, |
| exercises: [...selected], |
| duration: roundDuration + (restDuration * selected.length), |
| calories: roundCalories, |
| isRound: true |
| }); |
| } |
| } |
| } |
| else { |
| for (let i = 0; i < numExercises; i++) { |
| |
| const isLast = i === numExercises - 1; |
| const targetExDuration = isLast ? |
| remainingDuration : |
| Math.max( |
| 90, |
| Math.min( |
| 600, |
| Math.floor(remainingDuration / (numExercises - i)) |
| ) |
| ); |
| |
| |
| const randomIndex = Math.floor(Math.random() * availableExercises.length); |
| let exercise = availableExercises[randomIndex]; |
| |
| |
| |
| const repTime = 4; |
| const totalExTime = exercise.sets * ( |
| (exercise.reps ? (exercise.reps * repTime) : parseInt(exercise.duration)) + |
| (exercise.rest || 0) |
| ); |
| |
| |
| let exDuration = totalExTime; |
| let scaledSets = exercise.sets; |
| if (exDuration > targetExDuration && exercise.sets > 1) { |
| scaledSets = Math.max(1, Math.floor(targetExDuration / ( |
| (exercise.reps ? (exercise.reps * repTime) : parseInt(exercise.duration)) + |
| (exercise.rest || 0) |
| ))); |
| exDuration = scaledSets * ( |
| (exercise.reps ? (exercise.reps * repTime) : parseInt(exercise.duration)) + |
| (exercise.rest || 0) |
| ); |
| } |
| |
| |
| const activeTime = scaledSets * (exercise.reps ? (exercise.reps * repTime) : parseInt(exercise.duration)); |
| const exCalories = Math.round( |
| (exercise.met * intensityFactor * ageFactor * weight * (activeTime / 3600) * 3.5) / 200 |
| ); |
| |
| exercises.push({ |
| ...exercise, |
| sets: scaledSets, |
| duration: exDuration, |
| calories: exCalories |
| }); |
| |
| remainingDuration -= exDuration; |
| remainingCalories -= exCalories; |
| availableExercises.splice(randomIndex, 1); |
| |
| if (availableExercises.length === 0) { |
| availableExercises = [...exerciseDB[currentType]]; |
| } |
| } |
| |
| |
| if (exercises.length > 0 && remainingCalories > 0) { |
| const lastEx = exercises[exercises.length - 1]; |
| const additionalSets = Math.min( |
| 2, |
| Math.ceil(remainingCalories / (lastEx.calories / lastEx.sets)) |
| ); |
| |
| if (additionalSets > 0) { |
| lastEx.sets += additionalSets; |
| const activeTime = additionalSets * (lastEx.reps ? (lastEx.reps * 4) : parseInt(lastEx.duration)); |
| const additionalCalories = Math.round( |
| (lastEx.met * intensityFactor * ageFactor * weight * (activeTime / 3600) * 3.5) / 200 |
| ); |
| lastEx.calories += additionalCalories; |
| lastEx.duration += additionalSets * ( |
| (lastEx.reps ? (lastEx.reps * 4) : parseInt(lastEx.duration)) + |
| (lastEx.rest || 0) |
| ); |
| } |
| } |
| } |
| |
| return exercises; |
| } |
| |
| function displayWorkout(exercises, targetCalories, duration) { |
| |
| resultsPlaceholder.classList.add('hidden'); |
| resultsContainer.classList.remove('hidden'); |
| |
| |
| workoutType.textContent = currentType === 'fullbody' ? 'Full Body' : |
| currentType === 'strength' ? 'Strength' : 'Cardio'; |
| workoutIntensity.textContent = intensityNames[currentIntensity - 1]; |
| |
| |
| let totalCalories = exercises.reduce((sum, ex) => sum + (ex.calories || 0), 0); |
| const progressPercent = Math.min(100, Math.round((totalCalories / targetCalories) * 100)); |
| |
| |
| workoutSummary.textContent = `${exercises.length} ${currentType === 'cardio' ? 'rounds' : 'exercises'} • ${duration} minutes • ~${totalCalories} calories`; |
| |
| |
| const circle = document.querySelector('.progress-ring__circle'); |
| const radius = 16; |
| const circumference = radius * 2 * Math.PI; |
| const offset = circumference - (progressPercent / 100) * circumference; |
| circle.style.strokeDasharray = `${circumference} ${circumference}`; |
| circle.style.strokeDashoffset = offset; |
| caloriesProgress.textContent = `${progressPercent}%`; |
| |
| |
| exercisesContainer.innerHTML = ''; |
| |
| |
| exercises.forEach((exercise, index) => { |
| if (exercise.isRound) { |
| |
| const roundElement = document.createElement('div'); |
| roundElement.className = 'exercise-card bg-white p-5 rounded-xl border-2 border-indigo-100 fade-in mb-4'; |
| roundElement.style.animationDelay = `${index * 0.1}s`; |
| |
| roundElement.innerHTML = ` |
| <div class="flex justify-between items-center mb-3"> |
| <h3 class="font-bold text-indigo-700 text-lg">${exercise.name}</h3> |
| <span class="px-2 py-1 rounded-full bg-indigo-100 text-indigo-800 text-xs font-medium"> |
| Total: ${Math.floor(exercise.duration / 60)} min |
| </span> |
| </div> |
| <div class="bg-indigo-50 p-3 rounded-lg mb-3"> |
| <div class="flex justify-between text-sm mb-2"> |
| <span><i class="fas fa-fire text-indigo-500 mr-1"></i> ${exercise.calories} kcal</span> |
| </div> |
| <div class="flex justify-between text-xs text-gray-500"> |
| <span>${exercise.exercises.length} exercises</span> |
| <span>${Math.floor(exercise.exercises[0].rest)} sec rest between</span> |
| </div> |
| </div> |
| <div class="space-y-3"> |
| ${exercise.exercises.map((ex, i) => ` |
| <div class="flex items-start bg-gray-50 p-2 rounded-lg"> |
| <div class="mr-3 mt-1"> |
| <div class="w-8 h-8 rounded-full bg-red-100 flex items-center justify-center"> |
| <i class="fas fa-heartbeat text-red-500 text-sm"></i> |
| </div> |
| </div> |
| <div class="flex-1"> |
| <div class="flex justify-between items-start"> |
| <h4 class="font-medium text-gray-800 text-sm">${ex.name}</h4> |
| </div> |
| <div class="flex justify-between text-xs text-gray-500 mt-1"> |
| <span><i class="far fa-clock mr-1"></i> ${ex.duration}</span> |
| <span><i class="fas fa-stopwatch mr-1"></i> Rest: ${ex.rest}</span> |
| </div> |
| </div> |
| </div> |
| `).join('')} |
| </div> |
| `; |
| |
| exercisesContainer.appendChild(roundElement); |
| } else { |
| |
| const exerciseElement = document.createElement('div'); |
| exerciseElement.className = 'exercise-card bg-white p-5 rounded-xl border border-gray-200 hover:border-indigo-300 fade-in'; |
| exerciseElement.style.animationDelay = `${index * 0.1}s`; |
| |
| |
| let iconClass, bgColor, textColor; |
| if (currentType === 'cardio') { |
| iconClass = 'fas fa-heartbeat'; |
| bgColor = 'red'; |
| textColor = 'red'; |
| } else if (currentType === 'strength') { |
| iconClass = 'fas fa-dumbbell'; |
| bgColor = 'blue'; |
| textColor = 'blue'; |
| } else { |
| iconClass = 'fas fa-running'; |
| bgColor = 'purple'; |
| textColor = 'purple'; |
| } |
| |
| exerciseElement.innerHTML = ` |
| <div class="flex items-start"> |
| <div class="mr-4"> |
| <div class="w-12 h-12 rounded-xl bg-${bgColor}-100 flex items-center justify-center"> |
| <i class="${iconClass} text-${textColor}-500"></i> |
| </div> |
| </div> |
| <div class="flex-1"> |
| <div class="flex justify-between items-start"> |
| <h3 class="font-bold text-gray-800">${exercise.name}</h3> |
| <div class="flex space-x-2"> |
| ${exercise.sets ? `<span class="px-2 py-0.5 rounded-full bg-gray-100 text-gray-800 text-xs font-medium">${exercise.sets}x sets</span>` : ''} |
| <span class="px-2 py-0.5 rounded-full bg-${bgColor}-100 text-${textColor}-800 text-xs font-medium"> |
| ${Math.floor(exercise.duration / 60)} min |
| </span> |
| </div> |
| </div> |
| <p class="text-xs text-gray-500 mt-1">${exercise.desc}</p> |
| |
| <div class="mt-3"> |
| <div class="flex justify-between text-sm"> |
| <span class="flex items-center"><i class="fas fa-fire text-${textColor}-500 mr-1"></i> ~${exercise.calories} kcal</span> |
| ${exercise.reps ? `<span class="flex items-center"><i class="fas fa-redo text-${textColor}-500 mr-1"></i> ${exercise.reps} reps</span>` : ''} |
| ${exercise.rest ? `<span class="flex items-center"><i class="fas fa-stopwatch text-${textColor}-500 mr-1"></i> ${exercise.rest} sec rest</span>` : ''} |
| </div> |
| </div> |
| |
| <div class="mt-3"> |
| <details class="text-sm"> |
| <summary class="text-${textColor}-600 font-medium cursor-pointer">Show demonstration</summary> |
| <div class="mt-2 aspect-w-16 aspect-h-9"> |
| <iframe class="w-full h-48 rounded-lg" src="${exercise.video}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> |
| </div> |
| </details> |
| </div> |
| </div> |
| </div> |
| `; |
| |
| exercisesContainer.appendChild(exerciseElement); |
| } |
| }); |
| } |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=biggdadda/biggfitt" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> |
| </html> |