-
+
{{ d.reward }} XP
mdi-lightning-bolt
@@ -115,20 +202,35 @@
mdi-flag-checkered
{{ m.title }}
-
{{ doneCount(m.id) }}/{{ m.lessons.length }}
+
+ {{ moduleStatusIcon(m.id) }}
+ {{ moduleStatusText(m.id) }}
+
{{ m.subtitle }}
+
+ {{ doneCount(m.id) }}/{{ m.lessons.length }} lessons
+ {{ moduleHasProgress(m.id) ? avgScore(m.id) + '%' : '—' }} avg
+
-
@@ -162,14 +264,15 @@
:class="{ active: activeLesson && activeLesson.id===l.id, ok: bestScore(current.id,l.id)>=80 }"
@click="selectLesson(l)"
>
+
+ {{ lessonScoreIcon(current.id, l.id) }}
+ {{ lessonScoreLabel(current.id, l.id) }}
+
mdi-headset
{{ l.title }}
-
- {{ bestScore(current.id, l.id) ? bestScore(current.id, l.id) + '%' : 'new' }}
-
{{ l.desc }}
@@ -1856,11 +1959,69 @@ const defaultCfg = createDefaultLearnConfig()
const cfg = ref({ ...defaultCfg })
audioReveal.value = !cfg.value.audioChallenge
+const XP_PER_LEVEL = 300
const xp = ref(0)
const progress = ref({})
-const level = computed(() => 1 + Math.floor(xp.value / 300))
+const level = computed(() => 1 + Math.floor(xp.value / XP_PER_LEVEL))
+const nextLevel = computed(() => level.value + 1)
+const levelProgress = computed(() => Math.min(100, Math.round(((xp.value % XP_PER_LEVEL) / XP_PER_LEVEL) * 100)))
+const xpToNextLevel = computed(() => Math.max(0, level.value * XP_PER_LEVEL - xp.value))
const seasonPct = computed(() => Math.min(100, Math.round((xp.value % 1000) / 10)))
+type BadgeTier = { id: string; name: string; xp: number; description: string }
+
+const badgeTrack: BadgeTier[] = [
+ { id: 'rookie', name: 'Runway Rookie', xp: 0, description: 'Complete your first guided mission.' },
+ { id: 'cadet', name: 'Clearance Cadet', xp: 120, description: 'Score three missions with ≥80%.' },
+ { id: 'navigator', name: 'Taxi Navigator', xp: 360, description: 'Keep the flow going with solid readbacks.' },
+ { id: 'tower', name: 'Tower Pro', xp: 720, description: 'Master departures with confident phraseology.' }
+]
+
+const currentBadge = computed(() => {
+ let unlocked = badgeTrack[0]
+ for (const badge of badgeTrack) {
+ if (xp.value >= badge.xp) {
+ unlocked = badge
+ }
+ }
+ return unlocked
+})
+
+const nextBadge = computed(() => badgeTrack.find(badge => badge.xp > xp.value) || null)
+
+const nextBadgeProgress = computed(() => {
+ const upcoming = nextBadge.value
+ if (!upcoming) return 100
+ const previous = badgeTrack.slice().reverse().find(badge => badge.xp <= xp.value) || badgeTrack[0]
+ const span = Math.max(1, upcoming.xp - previous.xp)
+ const gained = xp.value - previous.xp
+ return Math.min(100, Math.round((gained / span) * 100))
+})
+
+const nextBadgeXpRemaining = computed(() => {
+ const upcoming = nextBadge.value
+ if (!upcoming) return 0
+ return Math.max(0, upcoming.xp - xp.value)
+})
+
+const totalLessons = computed(() => modules.value.reduce((sum, module) => sum + module.lessons.length, 0))
+const finishedLessons = computed(() => modules.value.reduce((sum, module) => sum + doneCount(module.id), 0))
+const missionCompletionPct = computed(() => (totalLessons.value ? Math.round((finishedLessons.value / totalLessons.value) * 100) : 0))
+
+const globalAccuracy = computed(() => {
+ const scores: number[] = []
+ Object.values(progress.value || {}).forEach(module => {
+ Object.values(module || {}).forEach(entry => {
+ if (entry && typeof entry.best === 'number' && entry.best > 0) {
+ scores.push(entry.best)
+ }
+ })
+ })
+ if (!scores.length) return 0
+ const avg = scores.reduce((acc, value) => acc + value, 0) / scores.length
+ return Math.round(avg)
+})
+
const audioContentHidden = computed(() => cfg.value.audioChallenge && !audioReveal.value)
type LearnStateResponse = LearnState
@@ -2280,6 +2441,22 @@ function bestScore(modId: string, lesId: string) {
return progress.value[modId]?.[lesId]?.best || 0
}
+function moduleHasProgress(modId: string) {
+ const moduleProgress = progress.value[modId]
+ if (!moduleProgress) return false
+ return Object.values(moduleProgress).some(entry => {
+ if (!entry) return false
+ return Boolean(entry.done) || (typeof entry.best === 'number' && entry.best > 0)
+ })
+}
+
+function moduleCompleted(modId: string) {
+ const module = modules.value.find(item => item.id === modId)
+ if (!module) return false
+ if (!module.lessons.length) return false
+ return doneCount(modId) >= module.lessons.length
+}
+
function doneCount(modId: string) {
const module = modules.value.find(item => item.id === modId)
if (!module) return 0
@@ -2302,12 +2479,82 @@ function avgScore(modId: string) {
return Math.round(sum / (values.length || 1))
}
+function handleModulePrimary(modId: string) {
+ quickContinue(modId)
+}
+
+function modulePrimaryLabel(modId: string) {
+ if (!moduleHasProgress(modId)) return 'Start'
+ return moduleCompleted(modId) ? 'Review' : 'Continue'
+}
+
+function modulePrimaryIcon(modId: string) {
+ if (!moduleHasProgress(modId)) return 'mdi-play'
+ return moduleCompleted(modId) ? 'mdi-refresh' : 'mdi-skip-next'
+}
+
+function moduleSecondaryLabel(modId: string) {
+ return moduleHasProgress(modId) ? 'Overview' : 'Preview'
+}
+
+function moduleSecondaryIcon(modId: string) {
+ return moduleHasProgress(modId) ? 'mdi-view-grid-outline' : 'mdi-information-outline'
+}
+
+function moduleStatusText(modId: string) {
+ if (!isModuleUnlocked(modId)) return 'Locked'
+ if (moduleCompleted(modId)) return 'Complete'
+ if (moduleHasProgress(modId)) return 'In progress'
+ return 'Ready'
+}
+
+function moduleStatusIcon(modId: string) {
+ if (!isModuleUnlocked(modId)) return 'mdi-lock'
+ if (moduleCompleted(modId)) return 'mdi-check-decagram'
+ if (moduleHasProgress(modId)) return 'mdi-progress-clock'
+ return 'mdi-play-circle'
+}
+
+function tileStatusClass(modId: string) {
+ if (!isModuleUnlocked(modId)) return 'is-locked'
+ if (moduleCompleted(modId)) return 'is-complete'
+ if (moduleHasProgress(modId)) return 'is-progress'
+ return 'is-ready'
+}
+
+function lessonScoreLabel(modId: string, lesId: string) {
+ const score = bestScore(modId, lesId)
+ return score ? `${score}%` : 'New'
+}
+
+function lessonScoreIcon(modId: string, lesId: string) {
+ const score = bestScore(modId, lesId)
+ if (!score) return 'mdi-star-four-points-outline'
+ if (score >= 80) return 'mdi-star-circle'
+ return 'mdi-progress-check'
+}
+
+function lessonScoreClass(modId: string, lesId: string) {
+ const score = bestScore(modId, lesId)
+ if (!score) return 'is-new'
+ if (score >= 80) return 'is-great'
+ return 'is-progress'
+}
+
const dailies = ref([
{ id: 'd1', title: '3 readbacks ≥80%', sub: 'Reward: +50 XP', reward: 50 },
{ id: 'd2', title: 'Start 1 module', sub: 'Reward: +20 XP', reward: 20 },
{ id: 'd3', title: 'Play the target phrase', sub: 'Reward: +10 XP', reward: 10 }
])
+const activeDailies = computed(() => dailies.value.length)
+
+const dailyHint = computed(() => {
+ const count = activeDailies.value
+ if (!count) return 'fresh missions'
+ return `${count} daily${count === 1 ? '' : 's'}`
+})
+
function startDaily(daily: { id: string; reward: number; title: string }) {
xp.value += daily.reward
toastNow(`Daily: ${daily.title} · +${daily.reward} XP`)
@@ -2711,34 +2958,232 @@ onMounted(() => {
.hero-panel {
display: grid;
- grid-template-columns: 1.1fr .9fr;
+ grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr);
gap: 20px;
border: 1px solid var(--border);
background: color-mix(in srgb, var(--text) 6%, transparent);
- padding: 20px;
- transform-style: preserve-3d
+ padding: 24px;
+ transform-style: preserve-3d;
+ border-radius: 24px;
+ box-shadow: 0 30px 80px rgba(0, 0, 0, .35)
+}
+
+.hero-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ margin-bottom: 6px
}
.hero-left .eyebrow {
font-size: 12px;
- letter-spacing: .16em;
+ letter-spacing: .18em;
+ color: var(--t3);
+ text-transform: uppercase
+}
+
+.hero-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 6px 12px;
+ border-radius: 999px;
+ border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
+ background: color-mix(in srgb, var(--accent) 14%, transparent);
+ color: var(--accent);
+ font-size: 12px;
+ letter-spacing: .14em;
+ text-transform: uppercase;
+ box-shadow: 0 6px 20px rgba(34, 211, 238, .25)
+}
+
+.hero-sub {
+ max-width: 520px;
+ margin-top: 4px
+}
+
+.hero-highlight {
+ margin-top: 18px;
+ border: 1px solid var(--border);
+ background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 18%, transparent), color-mix(in srgb, var(--accent2) 10%, transparent));
+ padding: 18px;
+ border-radius: 20px;
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: 18px;
+ position: relative;
+ overflow: hidden
+}
+
+.hero-highlight::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: radial-gradient(160% 160% at 0% 100%, color-mix(in srgb, var(--accent) 18%, transparent), transparent 60%);
+ opacity: .7;
+ pointer-events: none
+}
+
+.hero-orb {
+ --progress: 0%;
+ width: 96px;
+ height: 96px;
+ border-radius: 50%;
+ background: conic-gradient(var(--accent) var(--progress), color-mix(in srgb, var(--text) 10%, transparent) var(--progress));
+ position: relative;
+ display: grid;
+ place-items: center;
+ animation: orbFloat 9s ease-in-out infinite
+}
+
+.hero-orb::after {
+ content: '';
+ position: absolute;
+ inset: 8px;
+ border-radius: 50%;
+ background: color-mix(in srgb, var(--bg) 82%, transparent)
+}
+
+.hero-orb-core {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ align-items: center
+}
+
+.hero-orb-level {
+ font-weight: 700;
+ font-size: 16px;
+ text-transform: uppercase
+}
+
+.hero-orb-progress {
+ font-size: 11px;
+ letter-spacing: .18em;
color: var(--t3)
}
+.hero-highlight-meta {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ justify-content: center
+}
+
+.hero-tag {
+ font-size: 11px;
+ letter-spacing: .18em;
+ text-transform: uppercase;
+ color: var(--t3)
+}
+
+.hero-highlight-title {
+ font-size: 22px;
+ font-weight: 600;
+ color: var(--t2)
+}
+
+.hero-highlight-bar {
+ height: 8px;
+ border-radius: 999px;
+ border: 1px solid color-mix(in srgb, var(--text) 20%, transparent);
+ background: color-mix(in srgb, var(--text) 6%, transparent);
+ overflow: hidden
+}
+
+.hero-highlight-fill {
+ height: 100%;
+ background: linear-gradient(90deg, var(--accent), var(--accent2));
+ transition: width .4s ease
+}
+
+.hero-metrics {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
+ gap: 12px;
+ margin-top: 18px
+}
+
+.metric-card {
+ border: 1px solid var(--border);
+ background: color-mix(in srgb, var(--text) 4%, transparent);
+ padding: 14px;
+ border-radius: 16px;
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ box-shadow: 0 16px 32px rgba(0, 0, 0, .22);
+ animation: float 12s ease-in-out infinite
+}
+
+.metric-card:nth-child(2) {
+ animation-delay: 2s
+}
+
+.metric-card:nth-child(3) {
+ animation-delay: 4s
+}
+
+.metric-icon {
+ width: 40px;
+ height: 40px;
+ border-radius: 14px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: color-mix(in srgb, var(--accent) 18%, transparent);
+ color: var(--accent)
+}
+
+.metric-main {
+ display: flex;
+ flex-direction: column;
+ gap: 2px
+}
+
+.metric-value {
+ font-weight: 700;
+ font-size: 18px
+}
+
+.metric-label {
+ font-size: 12px;
+ color: var(--t3);
+ letter-spacing: .12em;
+ text-transform: uppercase
+}
+
.actions {
display: flex;
gap: 10px;
- margin-top: 12px
+ margin-top: 16px
}
.season {
- margin-top: 14px
+ margin-top: 18px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px
+}
+
+.season-head {
+ display: flex;
+ justify-content: space-between;
+ color: var(--t3);
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: .14em
}
.bar {
height: 8px;
background: color-mix(in srgb, var(--text) 8%, transparent);
- border: 1px solid var(--border)
+ border: 1px solid var(--border);
+ border-radius: 999px;
+ overflow: hidden
}
.fill {
@@ -2746,12 +3191,77 @@ onMounted(() => {
background: linear-gradient(90deg, var(--accent), var(--accent2))
}
-.meta {
+.hero-right {
display: flex;
- justify-content: space-between;
- margin-top: 6px;
- color: var(--t3);
- font-size: 12px
+ flex-direction: column;
+ gap: 18px
+}
+
+.badge-stack {
+ display: flex;
+ flex-direction: column;
+ gap: 12px
+}
+
+.badge-card {
+ border: 1px solid var(--border);
+ border-radius: 18px;
+ padding: 16px;
+ background: color-mix(in srgb, var(--text) 5%, transparent);
+ position: relative;
+ overflow: hidden;
+ box-shadow: 0 16px 40px rgba(0, 0, 0, .24);
+ animation: float 14s ease-in-out infinite
+}
+
+.badge-card.current {
+ background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 28%, transparent), color-mix(in srgb, var(--accent2) 18%, transparent));
+ color: var(--text)
+}
+
+.badge-card.next.complete {
+ opacity: .9
+}
+
+.badge-chip {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 6px 12px;
+ border-radius: 999px;
+ border: 1px solid color-mix(in srgb, var(--text) 20%, transparent);
+ background: color-mix(in srgb, var(--text) 12%, transparent);
+ font-size: 11px;
+ letter-spacing: .12em;
+ text-transform: uppercase;
+ color: var(--t2)
+}
+
+.badge-chip.accent {
+ color: var(--accent);
+ border-color: color-mix(in srgb, var(--accent) 38%, transparent);
+ background: color-mix(in srgb, var(--accent) 12%, transparent)
+}
+
+.badge-name {
+ font-size: 22px;
+ font-weight: 600;
+ margin-top: 10px
+}
+
+.badge-progress {
+ margin-top: 14px;
+ height: 8px;
+ border-radius: 999px;
+ border: 1px solid color-mix(in srgb, var(--text) 20%, transparent);
+ background: color-mix(in srgb, var(--text) 8%, transparent);
+ overflow: hidden
+}
+
+.badge-progress-fill {
+ height: 100%;
+ background: linear-gradient(90deg, var(--accent), var(--accent2));
+ transition: width .4s ease
}
.hero-right .rail-title {
@@ -2759,20 +3269,50 @@ onMounted(() => {
display: flex;
align-items: center;
gap: 6px;
- margin-bottom: 6px
+ margin-bottom: 6px;
+ text-transform: uppercase;
+ letter-spacing: .14em;
+ font-size: 12px
}
.rail {
display: flex;
- gap: 10px;
- overflow: auto
+ gap: 12px;
+ overflow: auto;
+ padding-bottom: 4px
}
.card {
border: 1px solid var(--border);
background: color-mix(in srgb, var(--text) 6%, transparent);
- padding: 12px;
- min-width: 240px
+ padding: 14px;
+ min-width: 240px;
+ border-radius: 16px
+}
+
+.challenge-card {
+ position: relative;
+ overflow: hidden;
+ transition: transform .3s ease, box-shadow .3s ease
+}
+
+.challenge-card::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: radial-gradient(140% 140% at 0% 100%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 60%);
+ opacity: 0;
+ transition: opacity .3s ease;
+ pointer-events: none
+}
+
+.challenge-card:hover {
+ transform: translateY(-6px);
+ box-shadow: 0 16px 34px rgba(0, 0, 0, .28)
+}
+
+.challenge-card:hover::after {
+ opacity: 1
}
.card-head {
@@ -2783,9 +3323,12 @@ onMounted(() => {
.badge {
border: 1px solid var(--border);
- padding: 2px 6px;
+ padding: 2px 8px;
font-size: 12px;
- color: var(--t2)
+ color: var(--t2);
+ border-radius: 999px;
+ letter-spacing: .08em;
+ text-transform: uppercase
}
.card-title {
@@ -2844,13 +3387,22 @@ onMounted(() => {
border: 1px solid var(--border);
background: color-mix(in srgb, var(--text) 6%, transparent);
display: flex;
- flex-direction: column
+ flex-direction: column;
+ border-radius: 18px;
+ overflow: hidden;
+ box-shadow: 0 12px 26px rgba(0, 0, 0, .25);
+ transition: transform .25s ease, box-shadow .25s ease
}
.tile.locked {
filter: saturate(.7) brightness(.9)
}
+.tile:hover {
+ transform: translateY(-6px);
+ box-shadow: 0 18px 40px rgba(0, 0, 0, .3)
+}
+
.tile-media {
height: 140px;
background-size: cover;
@@ -2858,7 +3410,10 @@ onMounted(() => {
}
.tile-body {
- padding: 12px
+ padding: 14px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px
}
.tile-top {
@@ -2875,10 +3430,49 @@ onMounted(() => {
gap: 6px
}
+.tile-status {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 10px;
+ border-radius: 999px;
+ font-size: 12px;
+ letter-spacing: .08em;
+ text-transform: uppercase;
+ border: 1px solid color-mix(in srgb, var(--text) 18%, transparent);
+ color: var(--t3)
+}
+
+.tile-status.is-ready {
+ color: var(--accent);
+ border-color: color-mix(in srgb, var(--accent) 35%, transparent);
+ background: color-mix(in srgb, var(--accent) 12%, transparent)
+}
+
+.tile-status.is-progress {
+ color: #fbbf24;
+ border-color: color-mix(in srgb, #fbbf24 40%, transparent);
+ background: color-mix(in srgb, #fbbf24 12%, transparent)
+}
+
+.tile-status.is-complete {
+ color: #22c55e;
+ border-color: color-mix(in srgb, #22c55e 40%, transparent);
+ background: color-mix(in srgb, #22c55e 14%, transparent)
+}
+
+.tile-status.is-locked {
+ color: var(--t3);
+ border-color: color-mix(in srgb, var(--text) 14%, transparent);
+ background: color-mix(in srgb, var(--text) 4%, transparent)
+}
+
.line {
height: 8px;
border: 1px solid var(--border);
- background: color-mix(in srgb, var(--text) 8%, transparent)
+ background: color-mix(in srgb, var(--text) 8%, transparent);
+ border-radius: 999px;
+ overflow: hidden
}
.line-fill {
@@ -2886,10 +3480,19 @@ onMounted(() => {
background: linear-gradient(90deg, var(--accent), var(--accent2))
}
+.tile-progress-meta {
+ display: flex;
+ justify-content: space-between;
+ color: var(--t3);
+ font-size: 12px;
+ letter-spacing: .08em;
+ text-transform: uppercase
+}
+
.tile-actions {
display: flex;
- gap: 8px;
- margin-top: 10px
+ flex-wrap: wrap;
+ gap: 10px
}
/* Play */
@@ -2933,19 +3536,65 @@ onMounted(() => {
}
.lesson {
+ position: relative;
text-align: left;
border: 1px solid var(--border);
- padding: 12px;
+ padding: 28px 16px 16px;
background: color-mix(in srgb, var(--text) 5%, transparent);
- cursor: pointer
+ cursor: pointer;
+ border-radius: 16px;
+ transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
+ box-shadow: 0 10px 20px rgba(0, 0, 0, .18)
+}
+
+.lesson:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 14px 26px rgba(0, 0, 0, .22)
}
.lesson.active {
- outline: 1px solid color-mix(in srgb, var(--accent) 50%, transparent)
+ outline: 1px solid color-mix(in srgb, var(--accent) 50%, transparent);
+ box-shadow: 0 18px 32px rgba(34, 211, 238, .22)
}
.lesson.ok {
- border-color: color-mix(in srgb, #4caf50 60%, transparent)
+ border-color: color-mix(in srgb, #22c55e 50%, transparent)
+}
+
+.lesson-score {
+ position: absolute;
+ top: 10px;
+ right: 12px;
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ padding: 4px 10px;
+ border-radius: 999px;
+ font-size: 12px;
+ letter-spacing: .08em;
+ text-transform: uppercase;
+ border: 1px solid color-mix(in srgb, var(--text) 18%, transparent);
+ color: var(--t3);
+ background: color-mix(in srgb, var(--text) 10%, transparent);
+ animation: badgePulse 8s ease-in-out infinite
+}
+
+.lesson-score.is-new {
+ color: var(--accent);
+ border-color: color-mix(in srgb, var(--accent) 40%, transparent);
+ background: color-mix(in srgb, var(--accent) 16%, transparent)
+}
+
+.lesson-score.is-progress {
+ color: #fbbf24;
+ border-color: color-mix(in srgb, #fbbf24 40%, transparent);
+ background: color-mix(in srgb, #fbbf24 14%, transparent)
+}
+
+.lesson-score.is-great {
+ color: #22c55e;
+ border-color: color-mix(in srgb, #22c55e 40%, transparent);
+ background: color-mix(in srgb, #22c55e 14%, transparent)
}
.tags {
@@ -3169,6 +3818,11 @@ onMounted(() => {
border: 1px solid var(--border);
background: color-mix(in srgb, var(--text) 4%, transparent);
border-radius: 12px;
+ position: sticky;
+ bottom: 20px;
+ z-index: 5;
+ backdrop-filter: blur(12px);
+ box-shadow: 0 16px 28px rgba(0, 0, 0, .28)
}
.lesson-actions-meta {
@@ -3319,6 +3973,33 @@ onMounted(() => {
}
}
+@keyframes orbFloat {
+ 0%, 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-8px);
+ }
+}
+
+@keyframes float {
+ 0%, 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-10px);
+ }
+}
+
+@keyframes badgePulse {
+ 0%, 100% {
+ box-shadow: 0 0 0 0 rgba(34, 211, 238, .25);
+ }
+ 50% {
+ box-shadow: 0 0 18px 0 rgba(34, 211, 238, .35);
+ }
+}
+
.settings {
display: flex;
flex-direction: column;
@@ -3363,6 +4044,33 @@ onMounted(() => {
.target-text {
font-size: 16px;
}
+
+ .lesson-actions {
+ position: static;
+ bottom: auto;
+ box-shadow: none;
+ backdrop-filter: none;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .hero-orb,
+ .metric-card,
+ .badge-card,
+ .lesson-score {
+ animation: none !important;
+ }
+
+ .tile,
+ .lesson,
+ .challenge-card,
+ .hero-orb,
+ .metric-card,
+ .badge-card,
+ .hero-highlight-fill,
+ .badge-progress-fill {
+ transition: none !important;
+ }
}