diff --git a/projects/bmi/index.html b/projects/bmi/index.html index 47842f9..5bf1cc1 100644 --- a/projects/bmi/index.html +++ b/projects/bmi/index.html @@ -1,24 +1,101 @@ - + - - + + BMI Calculator - + + -
-

BMI Calculator

-
- - -
-
-

Add validation and history with localStorage.

-
- +
+ +
+

BMI Calculator

+

Track your Body Mass Index, monitor progress & stay healthy

+
+ + +
+

Calculate Your BMI

+ + +
+ + +
+ + +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+ +
+ +
+
+
+
+
+ +
+
+ + +
+ + +
+
Your BMI Result
+
+
+
+
+
+
+
+
+
+
+ + +
+

History

+
+
+
+ + +
+ +
+
+ + +
+ + +
+
+
+ + \ No newline at end of file diff --git a/projects/bmi/main.js b/projects/bmi/main.js index c5b92ea..031788d 100644 --- a/projects/bmi/main.js +++ b/projects/bmi/main.js @@ -1,85 +1,579 @@ -const kg = document.getElementById('kg'); const cm = document.getElementById('cm'); const out = document.getElementById('out'); -function validateWeight(raw) { - if (!raw) return 'Please enter your weight (kg).'; - const v = Number(raw); - if (!Number.isFinite(v)) return 'Weight must be a number (e.g., 70).'; - if (v <= 0) return 'Weight must be greater than 0 kg.'; - if (v < 20 || v > 300) return 'Please enter a realistic weight (20–300 kg).'; - return ''; -} +class BMICalculator { + constructor() { + this.isMetric = true; + this.history = this.loadHistory(); + this.chart = null; + this.validationState = { + weight: false, + height: false + }; + this.initializeElements(); + this.bindEvents(); + this.updatePlaceholders(); + this.updateHistoryControls(); + this.renderHistory(); + } -function validateHeight(raw) { - if (!raw) return 'Please enter your height (cm).'; - const v = Number(raw); - if (!Number.isFinite(v)) return 'Height must be a number (e.g., 170).'; - if (v <= 0) return 'Height must be greater than 0 cm.'; - if (v < 50 || v > 250) return 'Please enter a realistic height (50–250 cm).'; - return ''; -} + initializeElements() { + this.form = document.getElementById('bmiForm'); + this.weightInput = document.getElementById('weightInput'); + this.heightInput = document.getElementById('heightInput'); + this.weightError = document.getElementById('weightError'); + this.heightError = document.getElementById('heightError'); + this.result = document.getElementById('result'); + this.bmiDisplay = document.getElementById('bmiDisplay'); + this.bmiValue = document.getElementById('bmiValue'); + this.bmiCategory = document.getElementById('bmiCategory'); + this.bmiRange = document.getElementById('bmiRange'); + this.bmiAdvice = document.getElementById('bmiAdvice'); + this.submitBtn = document.getElementById('submitBtn'); + this.metricBtn = document.getElementById('metricBtn'); + this.imperialBtn = document.getElementById('imperialBtn'); + this.historyList = document.getElementById('historyList'); + this.clearHistoryBtn = document.getElementById('clearHistory'); + this.listViewBtn = document.getElementById('listViewBtn'); + this.chartViewBtn = document.getElementById('chartViewBtn'); + this.chartContainer = document.getElementById('chartContainer'); + this.weightUp = document.getElementById('weightUp'); + this.weightDown = document.getElementById('weightDown'); + this.heightUp = document.getElementById('heightUp'); + this.heightDown = document.getElementById('heightDown'); + } -function clearCustomValidity() { - kg.setCustomValidity(''); - cm.setCustomValidity(''); - kg.removeAttribute('aria-invalid'); kg.style.borderColor = ''; - cm.removeAttribute('aria-invalid'); cm.style.borderColor = ''; -} + bindEvents() { + this.form.addEventListener('submit', (e) => this.handleSubmit(e)); + this.metricBtn.addEventListener('click', () => this.toggleUnit(true)); + this.imperialBtn.addEventListener('click', () => this.toggleUnit(false)); + this.clearHistoryBtn.addEventListener('click', () => this.clearHistory()); + this.listViewBtn.addEventListener('click', () => this.showListView()); + this.chartViewBtn.addEventListener('click', () => this.showChartView()); -function markFieldInvalid(el) { - if (!el) return; - el.setAttribute('aria-invalid', 'true'); - el.style.borderColor = '#ff6b6b'; -} + this.weightUp.addEventListener('click', () => this.incrementValue('weight', 0.1)); + this.weightDown.addEventListener('click', () => this.incrementValue('weight', -0.1)); + this.heightUp.addEventListener('click', () => this.incrementValue('height', 0.1)); + this.heightDown.addEventListener('click', () => this.incrementValue('height', -0.1)); -function clearFieldInvalid(el) { - if (!el) return; - el.removeAttribute('aria-invalid'); - el.style.borderColor = ''; -} + this.weightInput.addEventListener('input', () => this.validateField('weight')); + this.heightInput.addEventListener('input', () => this.validateField('height')); + } -form?.addEventListener('submit', e => { - e.preventDefault(); - if (!kg || !cm || !out) return; - - clearCustomValidity(); - - const weightRaw = (kg.value ?? '').trim(); - const heightRaw = (cm.value ?? '').trim(); - - const wErr = validateWeight(weightRaw); - const hErr = validateHeight(heightRaw); - - if (wErr) kg.setCustomValidity(wErr); - if (hErr) cm.setCustomValidity(hErr); - - if (wErr && hErr) { - markFieldInvalid(kg); - markFieldInvalid(cm); - } else { - if (wErr) markFieldInvalid(kg); else clearFieldInvalid(kg); - if (hErr) markFieldInvalid(cm); else clearFieldInvalid(cm); - } - - if (!form.checkValidity()) { - const firstInvalid = kg.validity.valid ? cm : kg; - firstInvalid.reportValidity(); - firstInvalid.focus(); - out.textContent = 'You have invalid Details'; - return; - } - - const w = parseFloat(weightRaw); - const heightCm = parseFloat(heightRaw); - const h = heightCm / 100; - const bmi = w / (h * h); - const cat = bmi < 18.5 ? 'Underweight' : bmi < 25 ? 'Normal' : bmi < 30 ? 'Overweight' : 'Obese'; - out.textContent = `BMI: ${bmi.toFixed(1)} (${cat})`; - clearCustomValidity(); -}); + incrementValue(fieldType, step) { + const input = fieldType === 'weight' ? this.weightInput : this.heightInput; + const currentValue = parseFloat(input.value) || 0; + const newValue = Math.max(0, currentValue + step); + input.value = newValue.toFixed(1); + input.dispatchEvent(new Event('input')); + this.validateField(fieldType); + } + + toggleUnit(isMetric) { + this.isMetric = isMetric; + this.metricBtn.classList.toggle('active', isMetric); + this.imperialBtn.classList.toggle('active', !isMetric); + this.updatePlaceholders(); + this.clearForm(); + } + + updatePlaceholders() { + if (this.isMetric) { + this.weightInput.placeholder = 'Enter weight in kg'; + this.heightInput.placeholder = 'Enter height in cm'; + } else { + this.weightInput.placeholder = 'Enter weight in lbs'; + this.heightInput.placeholder = 'Enter height in inches'; + } + } + + clearForm() { + this.weightInput.value = ''; + this.heightInput.value = ''; + this.clearValidation(); + this.hideResult(); + this.updateSubmitButton(); + } + + handleSubmit(e) { + e.preventDefault(); + + const weightValid = this.validateField('weight', true); + const heightValid = this.validateField('height', true); + + if (!weightValid || !heightValid) { + this.focusFirstInvalid(); + return; + } + + const weight = parseFloat(this.weightInput.value); + const height = parseFloat(this.heightInput.value); + + const bmi = this.calculateBMI(weight, height); + const category = this.getBMICategory(bmi); + + this.displayResult(bmi, category); + this.saveToHistory(weight, height, bmi, category); + } + + validateField(fieldType, forceShowError = false) { + const input = fieldType === 'weight' ? this.weightInput : this.heightInput; + const errorElement = fieldType === 'weight' ? this.weightError : this.heightError; + const value = input.value.trim(); + + input.classList.remove('error', 'success'); + errorElement.classList.remove('show'); + errorElement.textContent = ''; + input.setAttribute('aria-invalid', 'false'); + + if (value === '') { + if (forceShowError) { + this.setFieldError( + input, + errorElement, + `${fieldType === 'weight' ? 'Please enter your weight.' : 'Please enter your height.'}` + ); + } + this.validationState[fieldType] = false; + this.updateSubmitButton(); + return false; + } + + const numValue = parseFloat(value); + const unit = fieldType === 'weight' + ? (this.isMetric ? 'kg' : 'lbs') + : (this.isMetric ? 'cm' : 'inches'); + + if (numValue <= 0) { + this.setFieldError( + input, + errorElement, + `${fieldType === 'weight' ? 'Weight' : 'Height'} must be greater than 0 ${unit}.` + ); + this.validationState[fieldType] = false; + this.updateSubmitButton(); + return false; + } + + if (fieldType === 'weight') { + return this.validateWeight(numValue, input, errorElement); + } else { + return this.validateHeight(numValue, input, errorElement); + } + } + + validateWeight(weight, input, errorElement) { + const minWeight = this.isMetric ? 20 : 44; + const maxWeight = this.isMetric ? 300 : 660; + const unit = this.isMetric ? 'kg' : 'lbs'; + + if (weight < minWeight) { + this.setFieldError( + input, + errorElement, + `Entered weight looks too low (minimum ${minWeight} ${unit}).` + ); + this.validationState.weight = false; + this.updateSubmitButton(); + return false; + } + + if (weight > maxWeight) { + this.setFieldError( + input, + errorElement, + `Entered weight looks too high (maximum ${maxWeight} ${unit}).` + ); + this.validationState.weight = false; + this.updateSubmitButton(); + return false; + } + + this.setFieldSuccess(input); + this.validationState.weight = true; + this.updateSubmitButton(); + return true; + } + + validateHeight(height, input, errorElement) { + const minHeight = this.isMetric ? 100 : 39; + const maxHeight = this.isMetric ? 250 : 98; + const unit = this.isMetric ? 'cm' : 'inches'; + + if (height < minHeight) { + this.setFieldError( + input, + errorElement, + `Entered height looks too short (minimum ${minHeight} ${unit}).` + ); + this.validationState.height = false; + this.updateSubmitButton(); + return false; + } + + if (height > maxHeight) { + this.setFieldError( + input, + errorElement, + `Entered height looks too tall (maximum ${maxHeight} ${unit}).` + ); + this.validationState.height = false; + this.updateSubmitButton(); + return false; + } + + this.setFieldSuccess(input); + this.validationState.height = true; + this.updateSubmitButton(); + return true; + } + + focusFirstInvalid() { + const first = this.form.querySelector('[aria-invalid="true"], .error'); + if (first && typeof first.focus === 'function') { + first.focus(); + } + } + + setFieldError(input, errorElement, message) { + input.classList.add('error'); + input.setAttribute('aria-invalid', 'true'); + errorElement.textContent = message; + errorElement.classList.add('show'); + } + + setFieldSuccess(input) { + input.classList.add('success'); + input.setAttribute('aria-invalid', 'false'); + } + + clearValidation() { + this.weightInput.classList.remove('error', 'success'); + this.heightInput.classList.remove('error', 'success'); + this.weightError.classList.remove('show'); + this.heightError.classList.remove('show'); + this.weightError.textContent = ''; + this.heightError.textContent = ''; + this.weightInput.setAttribute('aria-invalid', 'false'); + this.heightInput.setAttribute('aria-invalid', 'false'); + this.validationState = { weight: false, height: false }; + } + + updateSubmitButton() { + const isValid = this.validationState.weight && this.validationState.height; + this.submitBtn.disabled = !isValid; + } + + calculateBMI(weight, height) { + let weightKg = weight; + let heightM = height; + + if (!this.isMetric) { + weightKg = weight * 0.453592; + heightM = height * 0.0254; + } else { + heightM = height / 100; + } + + return weightKg / (heightM * heightM); + } + + getBMICategory(bmi) { + const categories = [ + { + name: 'Severely Underweight', + min: -Infinity, max: 16.0, + class: 'bmi-severe-underweight', + historyClass: 'severe-underweight', + range: 'BMI < 16.0', + advice: 'This is very low. Please consult a healthcare professional.' + }, + { + name: 'Underweight', + min: 16.0, max: 18.5, + class: 'bmi-underweight', + historyClass: 'underweight', + range: '16.0 ≤ BMI < 18.5', + advice: 'Consider a balanced, calorie-rich diet and speak to a clinician if concerned.' + }, + { + name: 'Normal Weight', + min: 18.5, max: 25.0, + class: 'bmi-healthy', + historyClass: 'healthy', + range: '18.5 ≤ BMI < 25.0', + advice: 'Great. Maintain a balanced diet and regular activity.' + }, + { + name: 'Overweight', + min: 25.0, max: 30.0, + class: 'bmi-overweight', + historyClass: 'overweight', + range: '25.0 ≤ BMI < 30.0', + advice: 'Consider lifestyle changes (diet/activity). Small changes help.' + }, + { + name: 'Obese', + min: 30.0, max: 35.0, + class: 'bmi-obese', + historyClass: 'obese', + range: '30.0 ≤ BMI < 35.0', + advice: 'Higher health risk. Speak to a healthcare provider for guidance.' + }, + { + name: 'Extremely Obese', + min: 35.0, max: Infinity, + class: 'bmi-extreme', + historyClass: 'extreme', + range: 'BMI ≥ 35.0', + advice: 'Significant health risk. Please consult a healthcare professional.' + } + ]; + + return categories.find(c => bmi >= c.min && bmi < c.max); + } + + displayResult(bmi, category) { + this.bmiValue.textContent = bmi.toFixed(1); + this.bmiCategory.textContent = category.name; + this.bmiRange.textContent = category.range; + this.bmiAdvice.textContent = category.advice; + this.bmiDisplay.className = `bmi-display ${category.class}`; + this.result.classList.add('show'); + } + + saveToHistory(weight, height, bmi, category) { + const entry = { + id: Date.now(), + date: new Date(), + weight: weight, + height: height, + bmi: bmi.toFixed(1), + category: category.name, + categoryClass: category.historyClass, + unit: this.isMetric ? 'metric' : 'imperial' + }; + + this.history.unshift(entry); + + if (this.history.length > 10) { + this.history = this.history.slice(0, 10); + } + + this.saveHistory(); + this.renderHistory(); + this.updateHistoryControls(); + + if (this.chartViewBtn.classList.contains('active')) { + this.renderChart(); + } + } + + loadHistory() { + try { + const history = localStorage.getItem('bmiHistory'); + return history ? JSON.parse(history) : []; + } catch (error) { + console.error('Error loading history:', error); + return []; + } + } + + saveHistory() { + try { + localStorage.setItem('bmiHistory', JSON.stringify(this.history)); + } catch (error) { + if (error.name === 'QuotaExceededError') { + console.warn('Storage quota exceeded. Clearing old history.'); + this.history = this.history.slice(0, 5); + localStorage.setItem('bmiHistory', JSON.stringify(this.history)); + } else { + console.error('Error saving history:', error); + } + } + } + + renderHistory() { + if (this.history.length === 0) { + this.historyList.innerHTML = '

No calculations yet. Start by calculating your BMI above.

'; + return; + } + + this.historyList.innerHTML = this.history.map(entry => { + const date = new Date(entry.date); + const formattedDate = date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); + + return ` +
+
+
+
BMI: ${entry.bmi}
+
${entry.category}
+
+ ${entry.weight}${entry.unit === 'metric' ? 'kg' : 'lbs'} • + ${entry.height}${entry.unit === 'metric' ? 'cm' : 'in'} +
+
+
${formattedDate}
+
+
+ `; + }).join(''); + } + + clearHistory() { + if (this.history.length === 0) return; + + this.history = []; + this.saveHistory(); + this.updateHistoryControls(); + + if (this.chart) { + this.chart.destroy(); + this.chart = null; + } + + if (this.chartViewBtn.classList.contains('active')) { + this.chartContainer.innerHTML = '

No calculations yet. Start by calculating your BMI above.

'; + } else { + this.renderHistory(); + } + } + + showListView() { + this.listViewBtn.classList.add('active'); + this.chartViewBtn.classList.remove('active'); + this.historyList.style.display = 'flex'; + this.chartContainer.classList.remove('show'); + this.renderHistory(); + } + + showChartView() { + this.listViewBtn.classList.remove('active'); + this.chartViewBtn.classList.add('active'); + this.historyList.style.display = 'none'; + this.chartContainer.classList.add('show'); + + if (this.history.length === 0) { + this.chartContainer.innerHTML = '

No calculations yet. Start by calculating your BMI above.

'; + } else { + this.renderChart(); + } + } + + renderChart() { + if (this.history.length === 0) return; + + const labels = this.history.map(entry => { + const date = new Date(entry.date); + return date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric' + }); + }).reverse(); + + const bmiData = this.history.map(entry => parseFloat(entry.bmi)).reverse(); + + const pointBackgroundColors = this.history.map(entry => entry.color || (() => { + const b = parseFloat(entry.bmi); + if (b < 16) return '#1e3a8a'; + if (b < 18.5) return '#3b82f6'; + if (b < 25) return '#10b981'; + if (b < 30) return '#f59e0b'; + if (b < 35) return '#ef4444'; + return '#7f1d1d'; + })()).reverse(); + + if (this.chart) { + this.chart.destroy(); + } + + this.chartContainer.innerHTML = ''; + const ctx = document.getElementById('bmiChart').getContext('2d'); + + this.chart = new Chart(ctx, { + type: 'line', + data: { + labels: labels, + datasets: [{ + label: 'BMI', + data: bmiData, + borderColor: 'rgba(16, 185, 129, 0.9)', + backgroundColor: 'rgba(16, 185, 129, 0.1)', + pointBackgroundColor: pointBackgroundColors, + pointBorderColor: 'rgba(255, 255, 255, 0.9)', + pointBorderWidth: 2, + pointRadius: 6, + pointHoverRadius: 8, + fill: true, + tension: 0.3 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + }, + tooltip: { + backgroundColor: 'rgba(0, 0, 0, 0.9)', + titleColor: 'white', + bodyColor: 'white', + borderColor: 'rgba(255, 255, 255, 0.2)', + borderWidth: 1, + padding: 12, + displayColors: false, + callbacks: { + label: function (context) { + return `BMI: ${context.parsed.y}`; + } + } + } + }, + scales: { + x: { + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: 'rgba(255, 255, 255, 0.7)', + font: { + size: 12 + } + } + }, + y: { + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: 'rgba(255, 255, 255, 0.7)', + font: { + size: 12 + } + }, + suggestedMin: 15, + suggestedMax: 40 + } + } + } + }); + } + + updateHistoryControls() { + const hasHistory = this.history.length > 0; + this.clearHistoryBtn.disabled = !hasHistory; + } + + hideResult() { + this.result.classList.remove('show'); + } +} -[kg, cm].forEach(el => { - el?.addEventListener('input', () => { - el.setCustomValidity(''); - clearFieldInvalid(el); - }); +document.addEventListener('DOMContentLoaded', () => { + new BMICalculator(); }); diff --git a/projects/bmi/styles.css b/projects/bmi/styles.css index 15ac6ed..b8cc350 100644 --- a/projects/bmi/styles.css +++ b/projects/bmi/styles.css @@ -1,52 +1,663 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + body { - font-family: system-ui; - background: #0f0f12; - color: #eef1f8; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; + background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 25%, #0f0f23 50%, #16213e 75%, #000000 100%); + background-attachment: fixed; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + color: white; +} + +.container { + width: 100%; + max-width: 800px; +} + +.header { + text-align: center; + margin-bottom: 24px; +} + +.main-title { + font-size: 42px; + font-weight: 800; + background: linear-gradient(135deg, #10b981, #06b6d4, #8b5cf6); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + margin-bottom: 6px; + letter-spacing: -1px; +} + +.subtitle { + font-size: 16px; + color: rgba(255, 255, 255, 0.6); + font-weight: 400; +} + +.card { + background: rgba(255, 255, 255, 0.08); + backdrop-filter: blur(16px); + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 20px; + padding: 24px; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); + margin-bottom: 20px; +} + +.card-title { + font-size: 22px; + font-weight: 700; + margin-bottom: 20px; + color: rgba(255, 255, 255, 0.95); + text-align: center; +} + +.unit-toggle { + display: flex; + background: rgba(255, 255, 255, 0.1); + border-radius: 10px; + padding: 4px; + margin-bottom: 20px; +} + +.unit-btn { + flex: 1; + padding: 12px 20px; + border: none; + border-radius: 8px; + font-size: 14px; + font-weight: 600; + color: rgba(255, 255, 255, 0.6); + background: transparent; + cursor: pointer; + transition: all 0.3s ease; +} + +.unit-btn.active { + background: linear-gradient(135deg, #10b981, #059669); + color: white; + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3); +} + +.unit-btn:hover:not(.active) { + background: rgba(255, 255, 255, 0.15); + color: rgba(255, 255, 255, 0.85); +} + +.form { + display: flex; + flex-direction: column; + gap: 20px; +} + +.input-row { + display: flex; + gap: 20px; +} + +.input-group { + flex: 1; + position: relative; +} + +.input-label { + display: block; + margin-bottom: 8px; + font-weight: 600; + font-size: 14px; + color: rgba(255, 255, 255, 0.9); + letter-spacing: 0.3px; +} + +.input-wrapper { + position: relative; +} + +.input { + width: 100%; + padding: 14px 44px 14px 16px; + background: rgba(255, 255, 255, 0.08); + border: 2px solid rgba(255, 255, 255, 0.2); + border-radius: 10px; + color: white; + font-size: 16px; + transition: all 0.3s ease; + -webkit-appearance: none; + -moz-appearance: textfield; +} + +/* Custom number input arrows */ +.input::-webkit-inner-spin-button, +.input::-webkit-outer-spin-button { + -webkit-appearance: none; margin: 0; - padding: 2rem; - display: grid; - place-items: center } -main { - max-width: 560px; - width: 100% +.input-arrows { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + display: flex; + flex-direction: column; + gap: 2px; + opacity: 0; + transition: opacity 0.2s ease; + pointer-events: none; +} + +.input-wrapper:hover .input-arrows, +.input:focus~.input-arrows { + opacity: 1; + pointer-events: auto; +} + +.arrow-btn { + width: 24px; + height: 16px; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + color: rgba(255, 255, 255, 0.8); + font-size: 9px; + transition: all 0.2s ease; + user-select: none; +} + +.arrow-btn:hover { + background: rgba(255, 255, 255, 0.2); + border-color: rgba(255, 255, 255, 0.3); +} + +.arrow-btn:active { + background: rgba(255, 255, 255, 0.3); + transform: scale(0.95); +} + +.input::placeholder { + color: rgba(255, 255, 255, 0.4); +} + +.input:focus { + outline: none; + border-color: #10b981; + background: rgba(255, 255, 255, 0.12); + box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1); +} + +.input.error { + border-color: #ef4444; + background: rgba(239, 68, 68, 0.1); +} + +.input.success { + border-color: #10b981; + background: rgba(16, 185, 129, 0.1); +} + +.field-error { + color: #fca5a5; + font-size: 13px; + margin-top: 8px; + margin-left: 4px; + display: none; + font-weight: 500; + line-height: 1.4; +} + +.field-error.show { + display: block; +} + +.calculate-btn { + width: 100%; + background: linear-gradient(135deg, #10b981, #059669); + color: white; + border: none; + padding: 16px 24px; + border-radius: 10px; + font-size: 16px; + font-weight: 700; + cursor: pointer; + transition: all 0.3s ease; + margin-top: 8px; + box-shadow: 0 4px 14px rgba(16, 185, 129, 0.3); + letter-spacing: 0.5px; +} + +.calculate-btn:hover:not(:disabled) { + background: linear-gradient(135deg, #059669, #047857); + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4); +} + +.calculate-btn:active:not(:disabled) { + transform: translateY(0); +} + +.calculate-btn:disabled { + background: rgba(16, 185, 129, 0.3); + cursor: not-allowed; + box-shadow: none; +} + +.result { + margin-top: 24px; + display: none; +} + +.result.show { + display: block; + animation: slideDown 0.4s ease; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +.result-label { + font-size: 14px; + font-weight: 600; + color: rgba(255, 255, 255, 0.7); + margin-bottom: 10px; + text-transform: uppercase; + letter-spacing: 1px; + text-align: center; +} + +.bmi-display { + padding: 16px 20px; + padding-top: 8px; + border-radius: 12px; + color: white; + position: relative; + overflow: hidden; +} + +.bmi-display::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: inherit; + opacity: 0.1; + filter: blur(20px); +} + +.bmi-content { + position: relative; + text-align: center; +} + +.bmi-value { + font-size: 48px; + font-weight: 800; + margin-bottom: 6px; + letter-spacing: -2px; +} + +.bmi-category { + font-size: 20px; + margin-bottom: 6px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 1px; +} + +.bmi-range { + font-size: 14px; + font-weight: 500; + margin-bottom: 8px; +} + +.bmi-advice { + font-size: 14px; + font-weight: 500; + opacity: 0.9; + margin: 0 auto; + line-height: 1.4; + text-align: center; + width: 100%; + white-space: nowrap; + overflow: visible; + padding: 0 10px; + box-sizing: border-box; } -input, -button { - font: inherit +.bmi-severe-underweight { + background: linear-gradient(135deg, #1e3a8a, #1e40af); + box-shadow: 0 8px 24px rgba(30, 58, 138, 0.3); } -input { - padding: .5rem .75rem; - border-radius: .5rem; - border: 1px solid #262631; - background: #17171c; - color: #eef1f8 +.bmi-underweight { + background: linear-gradient(135deg, #3b82f6, #2563eb); + box-shadow: 0 8px 24px rgba(59, 130, 246, 0.3); } -input[aria-invalid="true"], -input.invalid { - border-color: #ff6b6b; - box-shadow: 0 0 0 4px rgba(255,107,107,0.06); +.bmi-healthy { + background: linear-gradient(135deg, #10b981, #059669); + box-shadow: 0 8px 24px rgba(16, 185, 129, 0.3); } -button { - background: #6ee7b7; - color: #0b1020; +.bmi-overweight { + background: linear-gradient(135deg, #f59e0b, #d97706); + box-shadow: 0 8px 24px rgba(245, 158, 11, 0.3); +} + +.bmi-obese { + background: linear-gradient(135deg, #ef4444, #dc2626); + box-shadow: 0 8px 24px rgba(239, 68, 68, 0.3); +} + +.bmi-extreme { + background: linear-gradient(135deg, #7f1d1d, #991b1b); + box-shadow: 0 8px 24px rgba(127, 29, 29, 0.3); +} + +.history-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 18px; + flex-wrap: wrap; + gap: 12px; +} + +.history-title { + margin-bottom: 10px; + font-size: 20px; + font-weight: 700; + color: rgba(255, 255, 255, 0.95); + text-align: center; + width: 100%; +} + +.history-controls { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; +} + +.view-toggle { + display: flex; + background: rgba(255, 255, 255, 0.1); + border-radius: 8px; + padding: 4px; +} + +.view-btn { + padding: 8px 16px; border: none; - padding: .5rem .75rem; - border-radius: .5rem; + border-radius: 6px; + font-size: 13px; + font-weight: 600; + color: rgba(255, 255, 255, 0.6); + background: transparent; + cursor: pointer; + transition: all 0.2s ease; +} + +.view-btn.active { + background: linear-gradient(135deg, #10b981, #059669); + color: white; +} + +.view-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.clear-btn { + color: #fca5a5; + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); + padding: 8px 14px; + border-radius: 6px; + font-size: 13px; font-weight: 600; - cursor: pointer + cursor: pointer; + transition: all 0.2s ease; +} + +.clear-btn:hover { + background: rgba(239, 68, 68, 0.2); + border-color: rgba(239, 68, 68, 0.5); +} + +.clear-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.history-list { + display: flex; + flex-direction: column; + gap: 12px; + max-height: 380px; + overflow-y: auto; + overflow-x: hidden; + padding-right: 4px; + margin-bottom: 20px; +} + +/* Custom scrollbar for history list */ +.history-list::-webkit-scrollbar { + width: 8px; +} + +.history-list::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.05); + border-radius: 4px; + margin: 4px 0; +} + +.history-list::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.2); + border-radius: 4px; + border: 1px solid rgba(255, 255, 255, 0.1); +} + +.history-list::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.3); +} + +.history-list::-webkit-scrollbar-thumb:active { + background: rgba(255, 255, 255, 0.4); +} + +/* Firefox scrollbar styling */ +.history-list { + scrollbar-width: thin; + scrollbar-color: rgba(255, 255, 255, 0.2) rgba(255, 255, 255, 0.05); +} + +.history-item { + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 8px; + padding: 16px; + width: 100%; + min-width: 0; + position: relative; +} + +.history-item.severe-underweight { + border-left: 6px solid #1e3a8a !important; +} + +.history-item.underweight { + border-left: 6px solid #3b82f6 !important; +} + +.history-item.healthy { + border-left: 6px solid #10b981 !important; } -#out { - margin-top: 1rem +.history-item.overweight { + border-left: 6px solid #f59e0b !important; } -.notes { - color: #a6adbb; - font-size: .9rem +.history-item.obese { + border-left: 6px solid #ef4444 !important; +} + +.history-item.extreme { + border-left: 6px solid #7f1d1d !important; +} + +.history-item:hover { + background: rgba(255, 255, 255, 0.15); + border-color: rgba(255, 255, 255, 0.25); +} + +.history-content { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 16px; + flex-wrap: wrap; +} + +.history-info { + flex: 1; + min-width: 0; +} + +.history-main { + font-weight: 700; + margin-bottom: 6px; + font-size: 18px; + color: rgba(255, 255, 255, 0.95); + white-space: nowrap; +} + +.history-category { + font-size: 14px; + margin-bottom: 4px; + font-weight: 600; +} + +.history-category.severe-underweight { + color: #60a5fa; +} + +.history-category.underweight { + color: #93c5fd; +} + +.history-category.healthy { + color: #34d399; +} + +.history-category.overweight { + color: #fbbf24; +} + +.history-category.obese { + color: #f87171; +} + +.history-category.extreme { + color: #fca5a5; +} + +.history-details { + color: rgba(255, 255, 255, 0.6); + font-size: 13px; + font-weight: 500; +} + +.history-date { + color: rgba(255, 255, 255, 0.6); + font-size: 12px; + white-space: nowrap; + font-weight: 500; + flex-shrink: 0; +} + +/* Mobile responsiveness */ +@media (max-width: 480px) { + .history-content { + flex-direction: column; + gap: 8px; + } + + .history-date { + align-self: flex-end; + margin-top: 4px; + } +} + +.empty-history { + color: rgba(255, 255, 255, 0.5); + text-align: center; + padding: 40px 16px; + font-size: 15px; +} + +.chart-container { + height: 300px; + position: relative; + display: none; +} + +.chart-container.show { + display: block; +} + +@media (max-width: 768px) { + .main-title { + font-size: 32px; + } + + .subtitle { + font-size: 14px; + } + + .card { + padding: 20px; + } + + .card-title { + font-size: 20px; + } + + .input-row { + flex-direction: column; + gap: 20px; + } + + .bmi-value { + font-size: 42px; + } + + .bmi-advice { + font-size: 13px; + white-space: normal; + padding: 0 5px; + } } \ No newline at end of file