1417 lines
43 KiB
Vue
1417 lines
43 KiB
Vue
<template>
|
|
<div class="zones-page">
|
|
<!-- Use the shared navbar -->
|
|
<ModernNavbar />
|
|
|
|
<div class="zones-content">
|
|
<!-- Page Header with Info Box -->
|
|
<div class="section-header">
|
|
<div class="section-header-content">
|
|
<h2>Training Zones</h2>
|
|
<p class="section-subtitle">Your personalized training zones for optimal performance</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Info Box Explaining Zones -->
|
|
<div class="info-box-info">
|
|
<div class="info-box-icon">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
|
<path d="M12 16V12M12 8H12.01" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
</svg>
|
|
</div>
|
|
<div class="info-box-content">
|
|
<h3 class="info-box-title">What are Training Zones?</h3>
|
|
<p class="info-box-text">Training zones are intensity ranges that help you train more effectively. Heart rate zones are based on your maximum heart rate, while power zones are calculated from your Functional Threshold Power (FTP). Training in different zones targets specific physiological adaptations to improve your cycling performance.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Metric Cards for FTP and Max HR -->
|
|
<div class="metric-cards-grid">
|
|
<div v-if="profile.profile?.ftp" class="metric-card">
|
|
<div class="metric-icon power-icon">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<polygon points="13,2 3,14 12,14 11,22 21,10 12,10 13,2" fill="currentColor"/>
|
|
</svg>
|
|
</div>
|
|
<div class="metric-content">
|
|
<div class="metric-label">Functional Threshold Power</div>
|
|
<div class="metric-value">{{ profile.profile.ftp }}<span class="metric-unit">W</span></div>
|
|
<div class="metric-description">Your sustainable 1-hour power output</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="profile.profile?.max_hr" class="metric-card">
|
|
<div class="metric-icon heart-icon">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<path d="M20.84 4.61C19.32 3.09 17.35 2.25 15.3 2.25C13.74 2.25 12.26 2.83 11.1 3.9C9.94 2.83 8.46 2.25 6.9 2.25C4.85 2.25 2.88 3.09 1.36 4.61C-0.64 6.61 -0.45 9.96 1.92 12.75L11.1 23.1L20.28 12.75C22.65 9.96 22.84 6.61 20.84 4.61Z" fill="currentColor"/>
|
|
</svg>
|
|
</div>
|
|
<div class="metric-content">
|
|
<div class="metric-label">Maximum Heart Rate</div>
|
|
<div class="metric-value">{{ profile.profile.max_hr }}<span class="metric-unit">bpm</span></div>
|
|
<div class="metric-description">Your highest recorded heart rate</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="profile.profile?.resting_hr" class="metric-card">
|
|
<div class="metric-icon rest-icon">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
|
<path d="M12 6V12L16 14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
</svg>
|
|
</div>
|
|
<div class="metric-content">
|
|
<div class="metric-label">Resting Heart Rate</div>
|
|
<div class="metric-value">{{ profile.profile.resting_hr }}<span class="metric-unit">bpm</span></div>
|
|
<div class="metric-description">Your baseline heart rate at rest</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Empty State -->
|
|
<div v-if="!profile.profile?.ftp && !profile.profile?.max_hr" class="empty-state">
|
|
<div class="empty-icon">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
|
<path d="M8 14S9.5 16 12 16S16 14 16 14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
<line x1="9" y1="9" x2="9.01" y2="9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
<line x1="15" y1="9" x2="15.01" y2="9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
</svg>
|
|
</div>
|
|
<h3>No Training Data Available</h3>
|
|
<p>Set your FTP and Max Heart Rate in your profile to see personalized training zones</p>
|
|
<RouterLink to="/profile" class="btn-primary">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<path d="M20 21V19C20 17.9391 19.5786 16.9217 18.8284 16.1716C18.0783 15.4214 17.0609 15 16 15H8C6.93913 15 5.92172 15.4214 5.17157 16.1716C4.42143 16.9217 4 17.9391 4 19V21" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
<circle cx="12" cy="7" r="4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
Complete Profile Setup
|
|
</RouterLink>
|
|
</div>
|
|
|
|
<!-- Zones Container -->
|
|
<div v-else class="zones-container">
|
|
<!-- Heart Rate Zones -->
|
|
<div v-if="profile.profile?.max_hr" class="zones-section">
|
|
<div class="section-header">
|
|
<div class="section-header-content">
|
|
<div class="section-title-row">
|
|
<svg class="section-icon heart-rate-icon" viewBox="0 0 24 24" fill="none">
|
|
<path d="M20.84 4.61C19.32 3.09 17.35 2.25 15.3 2.25C13.74 2.25 12.26 2.83 11.1 3.9C9.94 2.83 8.46 2.25 6.9 2.25C4.85 2.25 2.88 3.09 1.36 4.61C-0.64 6.61 -0.45 9.96 1.92 12.75L11.1 23.1L20.28 12.75C22.65 9.96 22.84 6.61 20.84 4.61Z" fill="currentColor"/>
|
|
</svg>
|
|
<h3>Heart Rate Zones</h3>
|
|
</div>
|
|
<p class="section-explanation">Train at the right intensity based on your cardiovascular capacity. Each zone targets specific fitness adaptations.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="zones-grid">
|
|
<div v-for="(zone, index) in hrZones" :key="index" class="card-modern zone-visual-card" :style="{ '--zone-color': zone.color }">
|
|
<div class="zone-number-badge" :style="{ backgroundColor: zone.color }">
|
|
<span>Z{{ index + 1 }}</span>
|
|
</div>
|
|
|
|
<div class="zone-main-content">
|
|
<div class="zone-name-section">
|
|
<h4 class="zone-name-large">{{ zone.name }}</h4>
|
|
<p class="zone-description-text">{{ getHRZoneDescription(index) }}</p>
|
|
</div>
|
|
|
|
<div class="zone-range-display">
|
|
<div class="range-icon">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<path d="M20.84 4.61C19.32 3.09 17.35 2.25 15.3 2.25C13.74 2.25 12.26 2.83 11.1 3.9C9.94 2.83 8.46 2.25 6.9 2.25C4.85 2.25 2.88 3.09 1.36 4.61C-0.64 6.61 -0.45 9.96 1.92 12.75L11.1 23.1L20.28 12.75C22.65 9.96 22.84 6.61 20.84 4.61Z" fill="currentColor"/>
|
|
</svg>
|
|
</div>
|
|
<div class="range-values">
|
|
<span class="range-number">{{ zone.min }}</span>
|
|
<span class="range-separator">-</span>
|
|
<span class="range-number">{{ zone.max }}</span>
|
|
<span class="range-unit">bpm</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="zone-bar-container">
|
|
<div class="zone-bar-background">
|
|
<div class="zone-bar-fill" :style="{
|
|
width: `${((index + 1) / hrZones.length) * 100}%`,
|
|
background: `linear-gradient(90deg, ${zone.color}dd, ${zone.color})`
|
|
}"></div>
|
|
</div>
|
|
<div class="zone-bar-labels">
|
|
<span class="zone-bar-label">{{ calculateHRPercentage(zone.min) }}%</span>
|
|
<span class="zone-bar-label">{{ calculateHRPercentage(zone.max) }}%</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Power Zones -->
|
|
<div v-if="profile.profile?.ftp" class="zones-section">
|
|
<div class="section-header">
|
|
<div class="section-header-content">
|
|
<div class="section-title-row">
|
|
<svg class="section-icon power-icon" viewBox="0 0 24 24" fill="none">
|
|
<polygon points="13,2 3,14 12,14 11,22 21,10 12,10 13,2" fill="currentColor"/>
|
|
</svg>
|
|
<h3>Power Zones</h3>
|
|
</div>
|
|
<p class="section-explanation">Train with precision using power-based zones. These zones are calculated from your FTP and provide the most accurate training targets.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="zones-grid">
|
|
<div v-for="(zone, index) in powerZones" :key="index" class="card-modern zone-visual-card" :style="{ '--zone-color': zone.color }">
|
|
<div class="zone-number-badge" :style="{ backgroundColor: zone.color }">
|
|
<span>Z{{ index + 1 }}</span>
|
|
</div>
|
|
|
|
<div class="zone-main-content">
|
|
<div class="zone-name-section">
|
|
<h4 class="zone-name-large">{{ zone.name }}</h4>
|
|
<p class="zone-description-text">{{ getPowerZoneDescription(index) }}</p>
|
|
</div>
|
|
|
|
<div class="zone-range-display">
|
|
<div class="range-icon">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<polygon points="13,2 3,14 12,14 11,22 21,10 12,10 13,2" fill="currentColor"/>
|
|
</svg>
|
|
</div>
|
|
<div class="range-values">
|
|
<span class="range-number">{{ zone.min }}</span>
|
|
<span class="range-separator">-</span>
|
|
<span class="range-number">{{ zone.max }}</span>
|
|
<span class="range-unit">W</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="zone-bar-container">
|
|
<div class="zone-bar-background">
|
|
<div class="zone-bar-fill" :style="{
|
|
width: `${calculatePercentage(zone.max)}%`,
|
|
background: `linear-gradient(90deg, ${zone.color}dd, ${zone.color})`
|
|
}"></div>
|
|
</div>
|
|
<div class="zone-bar-labels">
|
|
<span class="zone-bar-label">{{ calculatePercentage(zone.min) }}%</span>
|
|
<span class="zone-bar-label">{{ calculatePercentage(zone.max) }}%</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Training Guide -->
|
|
<div class="guides-section">
|
|
<div class="section-header">
|
|
<div class="section-header-content">
|
|
<div class="section-title-row">
|
|
<svg class="section-icon" viewBox="0 0 24 24" fill="none">
|
|
<path d="M9 11H15M9 15H15M17 21L12 16L7 21V5C7 4.46957 7.21071 3.96086 7.58579 3.58579C7.96086 3.21071 8.46957 3 9 3H15C15.5304 3 16.0391 3.21071 16.4142 3.58579C16.7893 3.96086 17 4.46957 17 5V21Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<h3>Training Guide</h3>
|
|
</div>
|
|
<p class="section-explanation">Learn how to effectively use each training zone to reach your cycling goals</p>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="guide-cards">
|
|
<div class="card-modern guide-card" data-zone="1">
|
|
<div class="guide-header">
|
|
<div class="guide-icon recovery">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="currentColor" stroke-width="2"/>
|
|
<path d="M8 14S9.5 16 12 16S16 14 16 14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
<line x1="9" y1="9" x2="9.01" y2="9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
<line x1="15" y1="9" x2="15.01" y2="9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
</svg>
|
|
</div>
|
|
<div class="header-text">
|
|
<h4>Recovery Rides</h4>
|
|
<span class="zone-badge">Zone 1</span>
|
|
</div>
|
|
</div>
|
|
<p>Very light intensity for active recovery. Focus on easy spinning and improving pedal technique. You should be able to carry on a conversation easily.</p>
|
|
<div class="guide-benefits">
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Promotes recovery</span>
|
|
</div>
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Improves technique</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-modern guide-card" data-zone="2">
|
|
<div class="guide-header">
|
|
<div class="guide-icon endurance">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="currentColor" stroke-width="2"/>
|
|
<circle cx="12" cy="12" r="3" fill="currentColor"/>
|
|
</svg>
|
|
</div>
|
|
<div class="header-text">
|
|
<h4>Endurance Base</h4>
|
|
<span class="zone-badge">Zone 2</span>
|
|
</div>
|
|
</div>
|
|
<p>Sustainable aerobic pace for building your engine. The foundation of all cycling fitness. Should feel comfortable for hours.</p>
|
|
<div class="guide-benefits">
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Builds aerobic base</span>
|
|
</div>
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Increases fat burning</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-modern guide-card" data-zone="3">
|
|
<div class="guide-header">
|
|
<div class="guide-icon tempo">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<polygon points="12,2 15,8 22,8 17,13 19,21 12,17 5,21 7,13 2,8 9,8" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
</svg>
|
|
</div>
|
|
<div class="header-text">
|
|
<h4>Tempo Training</h4>
|
|
<span class="zone-badge">Zone 3</span>
|
|
</div>
|
|
</div>
|
|
<p>Comfortably hard pace that improves muscular endurance. Breathing becomes more noticeable but still controlled.</p>
|
|
<div class="guide-benefits">
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Improves efficiency</span>
|
|
</div>
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Builds strength</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-modern guide-card" data-zone="4">
|
|
<div class="guide-header">
|
|
<div class="guide-icon threshold">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<polyline points="22,12 18,12 15,21 9,3 6,12 2,12" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
</svg>
|
|
</div>
|
|
<div class="header-text">
|
|
<h4>Threshold Power</h4>
|
|
<span class="zone-badge">Zone 4</span>
|
|
</div>
|
|
</div>
|
|
<p>Hard sustainable effort around your FTP. This is where the magic happens for improving your power output and racing ability.</p>
|
|
<div class="guide-benefits">
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Increases FTP</span>
|
|
</div>
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Improves racing power</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-modern guide-card" data-zone="5">
|
|
<div class="guide-header">
|
|
<div class="guide-icon vo2max">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
|
<polyline points="16,12 12,8 8,12" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
<polyline points="16,16 12,12 8,16" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
</svg>
|
|
</div>
|
|
<div class="header-text">
|
|
<h4>VO2 Max Power</h4>
|
|
<span class="zone-badge">Zone 5</span>
|
|
</div>
|
|
</div>
|
|
<p>Very hard efforts that improve your aerobic capacity. Short intervals with significant rest periods. Breathing is very hard.</p>
|
|
<div class="guide-benefits">
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Boosts VO2 max</span>
|
|
</div>
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Increases capacity</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-modern guide-card" data-zone="6">
|
|
<div class="guide-header">
|
|
<div class="guide-icon anaerobic">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<path d="M13 2L3 14L12 14L11 22L21 10L12 10L13 2Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
|
</svg>
|
|
</div>
|
|
<div class="header-text">
|
|
<h4>Anaerobic Power</h4>
|
|
<span class="zone-badge">Zone 6</span>
|
|
</div>
|
|
</div>
|
|
<p>Severe intensity efforts above VO2 Max. Use sparingly for peak performance and developing maximum power production.</p>
|
|
<div class="guide-benefits">
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Peak performance</span>
|
|
</div>
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Max power output</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-modern guide-card" data-zone="7">
|
|
<div class="guide-header">
|
|
<div class="guide-icon sprint">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<path d="M3 12C3 12 8 4 12 4S21 12 21 12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/>
|
|
<path d="M3 12C3 12 8 20 12 20S21 12 21 12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/>
|
|
</svg>
|
|
</div>
|
|
<div class="header-text">
|
|
<h4>Sprint Power</h4>
|
|
<span class="zone-badge">Zone 7</span>
|
|
</div>
|
|
</div>
|
|
<p>Maximum intensity efforts lasting seconds. Develops explosive power and neuromuscular strength for competitive racing.</p>
|
|
<div class="guide-benefits">
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Explosive power</span>
|
|
</div>
|
|
<div class="benefit-item">
|
|
<svg viewBox="0 0 16 16" fill="none">
|
|
<circle cx="8" cy="8" r="7" stroke="currentColor" fill="none"/>
|
|
<path d="M6 8L7.5 9.5L10 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
|
</svg>
|
|
<span>Racing strength</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, computed } from 'vue'
|
|
import api from '@/services/api'
|
|
import ModernNavbar from '@/components/ModernNavbar.vue'
|
|
|
|
const profile = ref({ profile: null })
|
|
const zones = ref({})
|
|
|
|
const hrZones = computed(() => {
|
|
return zones.value.hr_zones ? [
|
|
{ ...zones.value.hr_zones.zone_1, name: 'Active Recovery', color: '#10b981' },
|
|
{ ...zones.value.hr_zones.zone_2, name: 'Endurance', color: '#22c55e' },
|
|
{ ...zones.value.hr_zones.zone_3, name: 'Tempo', color: '#3b82f6' },
|
|
{ ...zones.value.hr_zones.zone_4, name: 'Threshold', color: '#f59e0b' },
|
|
{ ...zones.value.hr_zones.zone_5, name: 'VO2 Max', color: '#ef4444' },
|
|
] : []
|
|
})
|
|
|
|
const powerZones = computed(() => {
|
|
return zones.value.power_zones ? [
|
|
{ ...zones.value.power_zones.zone_1, name: 'Active Recovery', color: '#10b981' },
|
|
{ ...zones.value.power_zones.zone_2, name: 'Endurance', color: '#22c55e' },
|
|
{ ...zones.value.power_zones.zone_3, name: 'Tempo', color: '#3b82f6' },
|
|
{ ...zones.value.power_zones.zone_4, name: 'Threshold', color: '#1d4ed8' },
|
|
{ ...zones.value.power_zones.zone_5, name: 'VO2 Max', color: '#f59e0b' },
|
|
{ ...zones.value.power_zones.zone_6, name: 'Anaerobic', color: '#ef4444' },
|
|
{ ...zones.value.power_zones.zone_7, name: 'Sprint', color: '#8b5cf6' },
|
|
] : []
|
|
})
|
|
|
|
onMounted(async () => {
|
|
try {
|
|
const profileRes = await api.get('/api/protected/profile')
|
|
profile.value = profileRes.data
|
|
|
|
const zonesRes = await api.get('/api/protected/zones')
|
|
zones.value = zonesRes.data
|
|
} catch (error) {
|
|
console.error('Failed to load zones:', error)
|
|
}
|
|
})
|
|
|
|
function calculatePercentage(watts) {
|
|
return Math.round((watts / profile.value.profile.ftp) * 100)
|
|
}
|
|
|
|
function calculateHRPercentage(bpm) {
|
|
if (!profile.value.profile?.max_hr) return 0
|
|
return Math.round((bpm / profile.value.profile.max_hr) * 100)
|
|
}
|
|
|
|
function getHRZoneDescription(index) {
|
|
const descriptions = [
|
|
'Very light effort for active recovery and warm-up. Easy conversation pace.',
|
|
'Comfortable pace for building aerobic base. Can maintain for hours.',
|
|
'Moderately hard tempo pace. Controlled breathing, some discomfort.',
|
|
'Hard sustainable effort at lactate threshold. Heavy breathing.',
|
|
'Very hard efforts at VO2 Max. Short intervals only, maximum sustainable intensity.'
|
|
]
|
|
return descriptions[index] || ''
|
|
}
|
|
|
|
function getPowerZoneDescription(index) {
|
|
const descriptions = [
|
|
'Very light spinning for recovery. Minimal fatigue, easy pedaling.',
|
|
'Steady endurance pace for base building. Fat burning zone.',
|
|
'Tempo efforts for muscular endurance. Comfortably hard.',
|
|
'Lactate threshold power around FTP. Key zone for racing fitness.',
|
|
'VO2 Max intervals for aerobic capacity. Very demanding efforts.',
|
|
'Anaerobic capacity work above VO2 Max. Severe intensity.',
|
|
'Maximum power sprints. Neuromuscular training, explosive efforts.'
|
|
]
|
|
return descriptions[index] || ''
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.zones-page {
|
|
min-height: 100vh;
|
|
background: linear-gradient(135deg, var(--color-background) 0%, #f8fafc 100%);
|
|
}
|
|
|
|
/* ============================================
|
|
ZONES CONTENT
|
|
============================================ */
|
|
.zones-content {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
padding: var(--spacing-xl);
|
|
}
|
|
|
|
/* ============================================
|
|
SECTION HEADER
|
|
============================================ */
|
|
.section-header {
|
|
margin-bottom: var(--spacing-2xl);
|
|
animation: slideInDown var(--transition-slower) ease-out;
|
|
}
|
|
|
|
.section-header-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.section-header-content h2 {
|
|
margin: 0;
|
|
font-size: var(--font-size-4xl);
|
|
font-weight: var(--font-weight-bold);
|
|
background: linear-gradient(135deg, var(--color-primary) 0%, #6366f1 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.section-subtitle {
|
|
margin: 0;
|
|
font-size: var(--font-size-lg);
|
|
color: var(--color-text-secondary);
|
|
font-weight: var(--font-weight-medium);
|
|
}
|
|
|
|
.section-title-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-md);
|
|
margin-bottom: var(--spacing-xs);
|
|
}
|
|
|
|
.section-icon {
|
|
width: 32px;
|
|
height: 32px;
|
|
color: var(--color-primary);
|
|
}
|
|
|
|
.section-icon.heart-rate-icon {
|
|
color: #ef4444;
|
|
}
|
|
|
|
.section-icon.power-icon {
|
|
color: #f59e0b;
|
|
}
|
|
|
|
.section-header-content h3 {
|
|
margin: 0;
|
|
font-size: var(--font-size-2xl);
|
|
font-weight: var(--font-weight-bold);
|
|
color: var(--color-text-primary);
|
|
}
|
|
|
|
.section-explanation {
|
|
margin: 0;
|
|
color: var(--color-text-secondary);
|
|
font-size: var(--font-size-base);
|
|
line-height: var(--line-height-relaxed);
|
|
}
|
|
|
|
/* ============================================
|
|
INFO BOX
|
|
============================================ */
|
|
.info-box-info {
|
|
display: flex;
|
|
gap: var(--spacing-lg);
|
|
padding: var(--spacing-xl);
|
|
background: linear-gradient(135deg, #dbeafe 0%, #e0e7ff 100%);
|
|
border: 1px solid #93c5fd;
|
|
border-radius: var(--radius-xl);
|
|
margin-bottom: var(--spacing-2xl);
|
|
animation: slideInUp var(--transition-slower) ease-out;
|
|
}
|
|
|
|
.info-box-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
flex-shrink: 0;
|
|
color: #3b82f6;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: white;
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
.info-box-icon svg {
|
|
width: 28px;
|
|
height: 28px;
|
|
}
|
|
|
|
.info-box-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.info-box-title {
|
|
margin: 0 0 var(--spacing-sm) 0;
|
|
font-size: var(--font-size-lg);
|
|
font-weight: var(--font-weight-bold);
|
|
color: #1e40af;
|
|
}
|
|
|
|
.info-box-text {
|
|
margin: 0;
|
|
font-size: var(--font-size-base);
|
|
color: #1e3a8a;
|
|
line-height: var(--line-height-relaxed);
|
|
}
|
|
|
|
/* ============================================
|
|
METRIC CARDS
|
|
============================================ */
|
|
.metric-cards-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
gap: var(--spacing-lg);
|
|
margin-bottom: var(--spacing-2xl);
|
|
}
|
|
|
|
.metric-card {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-lg);
|
|
padding: var(--spacing-xl);
|
|
background: white;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-xl);
|
|
box-shadow: var(--shadow-md);
|
|
transition: all var(--transition-normal);
|
|
animation: slideInUp var(--transition-slower) ease-out;
|
|
}
|
|
|
|
.metric-card:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: var(--shadow-xl);
|
|
}
|
|
|
|
.metric-icon {
|
|
width: 56px;
|
|
height: 56px;
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: var(--radius-lg);
|
|
color: white;
|
|
}
|
|
|
|
.metric-icon.power-icon {
|
|
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
|
}
|
|
|
|
.metric-icon.heart-icon {
|
|
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
|
}
|
|
|
|
.metric-icon.rest-icon {
|
|
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
|
}
|
|
|
|
.metric-icon svg {
|
|
width: 28px;
|
|
height: 28px;
|
|
}
|
|
|
|
.metric-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.metric-label {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--color-text-secondary);
|
|
font-weight: var(--font-weight-medium);
|
|
margin-bottom: var(--spacing-xs);
|
|
}
|
|
|
|
.metric-value {
|
|
font-size: var(--font-size-3xl);
|
|
font-weight: var(--font-weight-bold);
|
|
color: var(--color-text-primary);
|
|
line-height: 1;
|
|
margin-bottom: var(--spacing-xs);
|
|
}
|
|
|
|
.metric-unit {
|
|
font-size: var(--font-size-lg);
|
|
font-weight: var(--font-weight-semibold);
|
|
color: var(--color-text-secondary);
|
|
margin-left: var(--spacing-xs);
|
|
}
|
|
|
|
.metric-description {
|
|
font-size: var(--font-size-xs);
|
|
color: var(--color-text-tertiary);
|
|
line-height: var(--line-height-relaxed);
|
|
}
|
|
|
|
/* ============================================
|
|
EMPTY STATE
|
|
============================================ */
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: var(--spacing-4xl) var(--spacing-xl);
|
|
background: var(--glass-background);
|
|
backdrop-filter: var(--glass-blur);
|
|
border-radius: var(--radius-2xl);
|
|
border: 1px solid var(--glass-border);
|
|
box-shadow: var(--shadow-glass);
|
|
}
|
|
|
|
.empty-icon {
|
|
width: 120px;
|
|
height: 120px;
|
|
margin: 0 auto var(--spacing-xl);
|
|
padding: var(--spacing-xl);
|
|
background: linear-gradient(135deg, var(--color-surface-hover), var(--color-border));
|
|
border-radius: var(--radius-full);
|
|
color: var(--color-text-secondary);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.empty-icon svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.empty-state h3 {
|
|
margin: 0 0 var(--spacing-md) 0;
|
|
font-size: var(--font-size-2xl);
|
|
font-weight: var(--font-weight-bold);
|
|
color: var(--color-text-primary);
|
|
}
|
|
|
|
.empty-state p {
|
|
margin: 0 0 var(--spacing-xl) 0;
|
|
color: var(--color-text-secondary);
|
|
font-size: var(--font-size-lg);
|
|
line-height: var(--line-height-relaxed);
|
|
}
|
|
|
|
.btn-primary {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--spacing-xs);
|
|
padding: var(--spacing-md) var(--spacing-xl);
|
|
background: var(--gradient-primary);
|
|
color: var(--color-text-inverse);
|
|
text-decoration: none;
|
|
border-radius: var(--radius-lg);
|
|
font-weight: var(--font-weight-semibold);
|
|
font-size: var(--font-size-base);
|
|
transition: all var(--transition-slow);
|
|
box-shadow: var(--shadow-md);
|
|
border: none;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.btn-primary svg {
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
transform: translateY(-3px);
|
|
box-shadow: var(--shadow-lg);
|
|
}
|
|
|
|
/* ============================================
|
|
ZONES CONTAINER
|
|
============================================ */
|
|
.zones-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-3xl);
|
|
}
|
|
|
|
.zones-section {
|
|
margin-bottom: var(--spacing-2xl);
|
|
animation: slideInUp var(--transition-slower) ease-out;
|
|
}
|
|
|
|
|
|
/* ============================================
|
|
ZONES GRID
|
|
============================================ */
|
|
.zones-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
gap: var(--spacing-xl);
|
|
}
|
|
|
|
/* ============================================
|
|
ZONE VISUAL CARD
|
|
============================================ */
|
|
.zone-visual-card {
|
|
position: relative;
|
|
overflow: visible;
|
|
transition: all var(--transition-normal);
|
|
}
|
|
|
|
.zone-visual-card:hover {
|
|
transform: translateY(-6px);
|
|
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.zone-number-badge {
|
|
position: absolute;
|
|
top: -12px;
|
|
right: var(--spacing-lg);
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: var(--radius-full);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: var(--shadow-lg);
|
|
z-index: 10;
|
|
}
|
|
|
|
.zone-number-badge span {
|
|
font-size: var(--font-size-lg);
|
|
font-weight: var(--font-weight-bold);
|
|
color: white;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.zone-main-content {
|
|
padding-top: var(--spacing-md);
|
|
}
|
|
|
|
.zone-name-section {
|
|
margin-bottom: var(--spacing-xl);
|
|
padding-bottom: var(--spacing-lg);
|
|
border-bottom: 2px solid var(--color-border);
|
|
}
|
|
|
|
.zone-name-large {
|
|
margin: 0 0 var(--spacing-sm) 0;
|
|
font-size: var(--font-size-2xl);
|
|
font-weight: var(--font-weight-bold);
|
|
color: var(--color-text-primary);
|
|
}
|
|
|
|
.zone-description-text {
|
|
margin: 0;
|
|
font-size: var(--font-size-sm);
|
|
color: var(--color-text-secondary);
|
|
line-height: var(--line-height-relaxed);
|
|
}
|
|
|
|
.zone-range-display {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-lg);
|
|
margin-bottom: var(--spacing-xl);
|
|
padding: var(--spacing-lg);
|
|
background: linear-gradient(135deg, var(--color-surface) 0%, var(--color-surface-hover) 100%);
|
|
border-radius: var(--radius-lg);
|
|
border-left: 4px solid var(--zone-color);
|
|
}
|
|
|
|
.range-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: white;
|
|
border-radius: var(--radius-md);
|
|
color: var(--zone-color);
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
.range-icon svg {
|
|
width: 24px;
|
|
height: 24px;
|
|
}
|
|
|
|
.range-values {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: var(--spacing-sm);
|
|
flex: 1;
|
|
}
|
|
|
|
.range-number {
|
|
font-size: var(--font-size-3xl);
|
|
font-weight: var(--font-weight-bold);
|
|
color: var(--zone-color);
|
|
line-height: 1;
|
|
}
|
|
|
|
.range-separator {
|
|
font-size: var(--font-size-2xl);
|
|
font-weight: var(--font-weight-bold);
|
|
color: var(--color-text-tertiary);
|
|
margin: 0 var(--spacing-xs);
|
|
}
|
|
|
|
.range-unit {
|
|
font-size: var(--font-size-base);
|
|
font-weight: var(--font-weight-semibold);
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
/* ============================================
|
|
ZONE BAR
|
|
============================================ */
|
|
.zone-bar-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.zone-bar-background {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 24px;
|
|
background: var(--color-border);
|
|
border-radius: var(--radius-lg);
|
|
overflow: hidden;
|
|
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.zone-bar-fill {
|
|
height: 100%;
|
|
border-radius: var(--radius-lg);
|
|
transition: all var(--transition-slow);
|
|
box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.3);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.zone-bar-fill::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.2) 0%, transparent 50%, rgba(0, 0, 0, 0.1) 100%);
|
|
}
|
|
|
|
.zone-bar-labels {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.zone-bar-label {
|
|
font-size: var(--font-size-xs);
|
|
font-weight: var(--font-weight-semibold);
|
|
color: var(--color-text-tertiary);
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
background: var(--color-surface);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
/* ============================================
|
|
TRAINING GUIDE
|
|
============================================ */
|
|
.guides-section {
|
|
margin-top: var(--spacing-3xl);
|
|
animation: slideInUp var(--transition-slower) ease-out;
|
|
}
|
|
|
|
.guide-cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
gap: var(--spacing-xl);
|
|
}
|
|
|
|
.guide-card {
|
|
transition: all var(--transition-normal);
|
|
border: 2px solid transparent;
|
|
position: relative;
|
|
}
|
|
|
|
.guide-card:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
|
border-color: rgba(102, 126, 234, 0.3);
|
|
}
|
|
|
|
.guide-header {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: var(--spacing-md);
|
|
margin-bottom: var(--spacing-lg);
|
|
}
|
|
|
|
.guide-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: var(--radius-lg);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
color: var(--color-text-inverse);
|
|
}
|
|
|
|
.guide-icon.recovery {
|
|
background: var(--gradient-secondary);
|
|
}
|
|
|
|
.guide-icon.endurance {
|
|
background: linear-gradient(135deg, var(--color-info), #1d4ed8);
|
|
}
|
|
|
|
.guide-icon.tempo {
|
|
background: linear-gradient(135deg, var(--color-warning), #d97706);
|
|
}
|
|
|
|
.guide-icon.threshold {
|
|
background: var(--gradient-danger);
|
|
}
|
|
|
|
.guide-icon.vo2max {
|
|
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
|
|
}
|
|
|
|
.guide-icon.anaerobic {
|
|
background: linear-gradient(135deg, #ec4899, #be185d);
|
|
}
|
|
|
|
.guide-icon.sprint {
|
|
background: linear-gradient(135deg, #06b6d4, #0891b2);
|
|
}
|
|
|
|
.guide-icon svg {
|
|
width: 24px;
|
|
height: 24px;
|
|
}
|
|
|
|
.header-text {
|
|
flex: 1;
|
|
}
|
|
|
|
.guide-card h4 {
|
|
margin: 0 0 var(--spacing-xs) 0;
|
|
font-size: var(--font-size-xl);
|
|
font-weight: var(--font-weight-bold);
|
|
color: var(--color-text-primary);
|
|
}
|
|
|
|
.zone-badge {
|
|
padding: 0.25rem var(--spacing-sm);
|
|
background: rgba(102, 126, 234, 0.1);
|
|
color: var(--color-primary);
|
|
border-radius: var(--radius-lg);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: var(--font-weight-semibold);
|
|
}
|
|
|
|
.guide-card p {
|
|
margin: 0 0 var(--spacing-lg) 0;
|
|
color: var(--color-text-secondary);
|
|
line-height: var(--line-height-relaxed);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.guide-benefits {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.benefit-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
padding: var(--spacing-sm);
|
|
background: rgba(16, 185, 129, 0.05);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.benefit-item svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
color: var(--color-secondary);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.benefit-item span {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--color-text-primary);
|
|
font-weight: var(--font-weight-medium);
|
|
}
|
|
|
|
/* ============================================
|
|
RESPONSIVE DESIGN
|
|
============================================ */
|
|
@media (max-width: 1024px) {
|
|
.metric-cards-grid {
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
}
|
|
|
|
.zones-grid {
|
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
}
|
|
|
|
.guide-cards {
|
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.zones-content {
|
|
padding: var(--spacing-lg);
|
|
}
|
|
|
|
.section-header-content h2 {
|
|
font-size: var(--font-size-3xl);
|
|
}
|
|
|
|
.section-subtitle {
|
|
font-size: var(--font-size-base);
|
|
}
|
|
|
|
.metric-cards-grid {
|
|
grid-template-columns: 1fr;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.zones-grid {
|
|
grid-template-columns: 1fr;
|
|
gap: var(--spacing-lg);
|
|
}
|
|
|
|
.guide-cards {
|
|
grid-template-columns: 1fr;
|
|
gap: var(--spacing-lg);
|
|
}
|
|
|
|
.info-box-info {
|
|
flex-direction: column;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.zone-number-badge {
|
|
width: 40px;
|
|
height: 40px;
|
|
}
|
|
|
|
.zone-number-badge span {
|
|
font-size: var(--font-size-base);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.zones-content {
|
|
padding: var(--spacing-md);
|
|
}
|
|
|
|
.section-header-content h2 {
|
|
font-size: var(--font-size-2xl);
|
|
}
|
|
|
|
.section-subtitle {
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.section-header-content h3 {
|
|
font-size: var(--font-size-xl);
|
|
}
|
|
|
|
.section-explanation {
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.section-icon {
|
|
width: 24px;
|
|
height: 24px;
|
|
}
|
|
|
|
.info-box-info {
|
|
padding: var(--spacing-lg);
|
|
}
|
|
|
|
.info-box-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
}
|
|
|
|
.info-box-icon svg {
|
|
width: 24px;
|
|
height: 24px;
|
|
}
|
|
|
|
.info-box-title {
|
|
font-size: var(--font-size-base);
|
|
}
|
|
|
|
.info-box-text {
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.metric-card {
|
|
padding: var(--spacing-lg);
|
|
}
|
|
|
|
.metric-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
}
|
|
|
|
.metric-icon svg {
|
|
width: 24px;
|
|
height: 24px;
|
|
}
|
|
|
|
.metric-value {
|
|
font-size: var(--font-size-2xl);
|
|
}
|
|
|
|
.metric-unit {
|
|
font-size: var(--font-size-base);
|
|
}
|
|
|
|
.zones-grid {
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.zone-name-large {
|
|
font-size: var(--font-size-xl);
|
|
}
|
|
|
|
.zone-range-display {
|
|
padding: var(--spacing-md);
|
|
}
|
|
|
|
.range-icon {
|
|
width: 32px;
|
|
height: 32px;
|
|
}
|
|
|
|
.range-icon svg {
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
|
|
.range-number {
|
|
font-size: var(--font-size-2xl);
|
|
}
|
|
|
|
.range-separator {
|
|
font-size: var(--font-size-xl);
|
|
}
|
|
|
|
.zone-bar-background {
|
|
height: 20px;
|
|
}
|
|
|
|
.guide-cards {
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.guide-header {
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.zone-badge {
|
|
align-self: center;
|
|
}
|
|
}
|
|
|
|
/* ============================================
|
|
ANIMATIONS
|
|
============================================ */
|
|
@keyframes slideInDown {
|
|
from {
|
|
transform: translateY(-20px);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateY(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes slideInUp {
|
|
from {
|
|
transform: translateY(20px);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateY(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* ============================================
|
|
DARK MODE SUPPORT
|
|
============================================ */
|
|
:global(.dark) .zones-page {
|
|
background: linear-gradient(135deg, var(--color-background) 0%, #1e293b 100%);
|
|
}
|
|
|
|
:global(.dark) .info-box-info {
|
|
background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 100%);
|
|
border-color: #3b82f6;
|
|
}
|
|
|
|
:global(.dark) .info-box-icon {
|
|
background: rgba(59, 130, 246, 0.2);
|
|
}
|
|
|
|
:global(.dark) .info-box-title {
|
|
color: #93c5fd;
|
|
}
|
|
|
|
:global(.dark) .info-box-text {
|
|
color: #bfdbfe;
|
|
}
|
|
|
|
:global(.dark) .metric-card {
|
|
background: var(--color-surface);
|
|
border-color: var(--color-border);
|
|
}
|
|
|
|
:global(.dark) .card-modern {
|
|
background: var(--color-surface);
|
|
border-color: var(--color-border);
|
|
}
|
|
|
|
:global(.dark) .zone-range-display {
|
|
background: linear-gradient(135deg, var(--color-surface-hover) 0%, var(--color-surface) 100%);
|
|
}
|
|
|
|
:global(.dark) .range-icon {
|
|
background: var(--color-surface-hover);
|
|
}
|
|
|
|
:global(.dark) .zone-bar-background {
|
|
background: var(--color-surface-hover);
|
|
}
|
|
|
|
:global(.dark) .zone-bar-label {
|
|
background: var(--color-surface-hover);
|
|
}
|
|
|
|
:global(.dark) .guide-card:hover {
|
|
border-color: rgba(102, 126, 234, 0.5);
|
|
}
|
|
|
|
:global(.dark) .empty-state {
|
|
background: var(--color-surface);
|
|
border-color: var(--color-border);
|
|
}
|
|
|
|
:global(.dark) .empty-state h3 {
|
|
color: var(--color-text-primary);
|
|
}
|
|
|
|
:global(.dark) .empty-state p {
|
|
color: var(--color-text-secondary);
|
|
}
|
|
</style> |