diff --git a/public/icons/camera-square.svg b/public/icons/camera-square.svg
new file mode 100644
index 0000000..3bf11bf
--- /dev/null
+++ b/public/icons/camera-square.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/icons/check-circle.svg b/public/icons/check-circle.svg
new file mode 100644
index 0000000..7c5d66f
--- /dev/null
+++ b/public/icons/check-circle.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/icons/check-square.svg b/public/icons/check-square.svg
new file mode 100644
index 0000000..f672b63
--- /dev/null
+++ b/public/icons/check-square.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/icons/close-circle.svg b/public/icons/close-circle.svg
new file mode 100644
index 0000000..264c82a
--- /dev/null
+++ b/public/icons/close-circle.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/icons/close-square.svg b/public/icons/close-square.svg
new file mode 100644
index 0000000..6164cad
--- /dev/null
+++ b/public/icons/close-square.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/icons/confetti-minimalistic.svg b/public/icons/confetti-minimalistic.svg
new file mode 100644
index 0000000..71c5b43
--- /dev/null
+++ b/public/icons/confetti-minimalistic.svg
@@ -0,0 +1,13 @@
+
+
diff --git a/public/icons/crown-minimalistic.svg b/public/icons/crown-minimalistic.svg
new file mode 100644
index 0000000..7e32a29
--- /dev/null
+++ b/public/icons/crown-minimalistic.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/public/icons/cup-first.svg b/public/icons/cup-first.svg
new file mode 100644
index 0000000..0f09e04
--- /dev/null
+++ b/public/icons/cup-first.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/public/icons/cup-star.svg b/public/icons/cup-star.svg
new file mode 100644
index 0000000..100e9be
--- /dev/null
+++ b/public/icons/cup-star.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/public/icons/cup.svg b/public/icons/cup.svg
new file mode 100644
index 0000000..2a1c675
--- /dev/null
+++ b/public/icons/cup.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/public/icons/danger-square.svg b/public/icons/danger-square.svg
new file mode 100644
index 0000000..ef30cf3
--- /dev/null
+++ b/public/icons/danger-square.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/public/icons/fail-square.svg b/public/icons/fail-square.svg
new file mode 100644
index 0000000..6164cad
--- /dev/null
+++ b/public/icons/fail-square.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/icons/filter.svg b/public/icons/filter.svg
new file mode 100644
index 0000000..0512682
--- /dev/null
+++ b/public/icons/filter.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/full-screen-square.svg b/public/icons/full-screen-square.svg
new file mode 100644
index 0000000..e840422
--- /dev/null
+++ b/public/icons/full-screen-square.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/public/icons/gamepad.svg b/public/icons/gamepad.svg
new file mode 100644
index 0000000..0199881
--- /dev/null
+++ b/public/icons/gamepad.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/public/icons/icons-source.url b/public/icons/icons-source.url
new file mode 100644
index 0000000..f205f71
--- /dev/null
+++ b/public/icons/icons-source.url
@@ -0,0 +1 @@
+https://www.svgrepo.com/collection/solar-broken-line-icons/
diff --git a/public/icons/info-circle.svg b/public/icons/info-circle.svg
new file mode 100644
index 0000000..223f4c5
--- /dev/null
+++ b/public/icons/info-circle.svg
@@ -0,0 +1,7 @@
+
diff --git a/public/icons/info-square.svg b/public/icons/info-square.svg
new file mode 100644
index 0000000..c4096a2
--- /dev/null
+++ b/public/icons/info-square.svg
@@ -0,0 +1,7 @@
+
diff --git a/public/icons/lock-locked.svg b/public/icons/lock-locked.svg
new file mode 100644
index 0000000..23fa085
--- /dev/null
+++ b/public/icons/lock-locked.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/icons/lock-unlocked.svg b/public/icons/lock-unlocked.svg
new file mode 100644
index 0000000..8c95e9b
--- /dev/null
+++ b/public/icons/lock-unlocked.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/icons/medal-ribbon-star.svg b/public/icons/medal-ribbon-star.svg
new file mode 100644
index 0000000..a31e531
--- /dev/null
+++ b/public/icons/medal-ribbon-star.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/public/icons/medal-ribbon.svg b/public/icons/medal-ribbon.svg
new file mode 100644
index 0000000..fc17ff2
--- /dev/null
+++ b/public/icons/medal-ribbon.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/icons/medal-ribbons-star.svg b/public/icons/medal-ribbons-star.svg
new file mode 100644
index 0000000..3187c16
--- /dev/null
+++ b/public/icons/medal-ribbons-star.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/public/icons/medal-star-circle.svg b/public/icons/medal-star-circle.svg
new file mode 100644
index 0000000..1dfba72
--- /dev/null
+++ b/public/icons/medal-star-circle.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/public/icons/medal-star-square.svg b/public/icons/medal-star-square.svg
new file mode 100644
index 0000000..cbd4e77
--- /dev/null
+++ b/public/icons/medal-star-square.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/public/icons/medal-star.svg b/public/icons/medal-star.svg
new file mode 100644
index 0000000..5031e2b
--- /dev/null
+++ b/public/icons/medal-star.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/public/icons/moon.svg b/public/icons/moon.svg
new file mode 100644
index 0000000..5b2251c
--- /dev/null
+++ b/public/icons/moon.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/public/icons/ok-square.svg b/public/icons/ok-square.svg
new file mode 100644
index 0000000..f672b63
--- /dev/null
+++ b/public/icons/ok-square.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/icons/record.svg b/public/icons/record.svg
new file mode 100644
index 0000000..cf43145
--- /dev/null
+++ b/public/icons/record.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/public/icons/running.svg b/public/icons/running.svg
new file mode 100644
index 0000000..87d8fa9
--- /dev/null
+++ b/public/icons/running.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/public/icons/sad-circle.svg b/public/icons/sad-circle.svg
new file mode 100644
index 0000000..6d546d0
--- /dev/null
+++ b/public/icons/sad-circle.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/public/icons/sad-square.svg b/public/icons/sad-square.svg
new file mode 100644
index 0000000..f564584
--- /dev/null
+++ b/public/icons/sad-square.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/public/icons/skip-square.svg b/public/icons/skip-square.svg
new file mode 100644
index 0000000..a6a7f44
--- /dev/null
+++ b/public/icons/skip-square.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/public/icons/smile-squre.svg b/public/icons/smile-squre.svg
new file mode 100644
index 0000000..a8bf9b2
--- /dev/null
+++ b/public/icons/smile-squre.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/public/icons/sun.svg b/public/icons/sun.svg
new file mode 100644
index 0000000..329c8f2
--- /dev/null
+++ b/public/icons/sun.svg
@@ -0,0 +1,12 @@
+
+
diff --git a/public/icons/unread.svg b/public/icons/unread.svg
new file mode 100644
index 0000000..0a8317f
--- /dev/null
+++ b/public/icons/unread.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/src/components/AchievementButton.svelte b/src/components/AchievementButton.svelte
new file mode 100644
index 0000000..249185f
--- /dev/null
+++ b/src/components/AchievementButton.svelte
@@ -0,0 +1,70 @@
+
+
+
+
+
diff --git a/src/components/Achievements.svelte b/src/components/Achievements.svelte
new file mode 100644
index 0000000..113b479
--- /dev/null
+++ b/src/components/Achievements.svelte
@@ -0,0 +1,458 @@
+
+
+
+{#if show}
+
+
+
+
+
+ {#each Object.entries(achievementDefinitions) as [key, def]}
+
+
+
+
+
+
{def.name}
+
{def.description}
+ {#if achievements[key]?.unlocked}
+
+ {:else}
+
+ {/if}
+
+
+ {/each}
+
+
+
+{/if}
+
+
+{#if newAchievements.length > 0}
+
+ {#each newAchievements as achievement}
+
+
+
+
+
+
Achievement Unlocked!
+
{achievement.name}
+
+
+ {/each}
+
+{/if}
+
+
diff --git a/src/components/Filter.svelte b/src/components/Filter.svelte
index 6f4c573..d8c5cec 100644
--- a/src/components/Filter.svelte
+++ b/src/components/Filter.svelte
@@ -1,4 +1,5 @@
@@ -10,66 +11,21 @@
class:active={theme === "system"}
aria-label="System theme"
>
-
+
@@ -81,4 +37,10 @@
gap: 0.2rem;
margin-left: auto;
}
+
+ .theme-switch-group .icon {
+ width: 20px;
+ height: 20px;
+ display: inline-flex;
+ }
diff --git a/src/pages/FlagQuiz.svelte b/src/pages/FlagQuiz.svelte
index 73a4b98..b283b6f 100644
--- a/src/pages/FlagQuiz.svelte
+++ b/src/pages/FlagQuiz.svelte
@@ -1,6 +1,9 @@
-
+ showAchievements = true}
+/>
@@ -334,6 +412,41 @@
{/if}
+
+
+ {#if showResetConfirmation}
+ e.target === e.currentTarget && cancelReset()}>
+
+
+
+
This action will permanently delete:
+
+ - ✗ All game statistics (correct, wrong, skipped answers)
+ - ✗ Current session score
+ - ✗ All unlocked achievements
+ - ✗ Achievement progress
+
+
This cannot be undone!
+
+
+
+
+
+
+
+ {/if}
+
+
+ showAchievements = false}
+ on:achievementsUnlocked={handleAchievementsUnlocked}
+ />
{#if gameState === 'loading'}
Loading flags...
{:else if currentQuestion}
@@ -350,12 +463,28 @@
{#if showResult}
{#if selectedAnswer === correctAnswer}
-
✅ Correct!
+
Correct!
{:else}
- ❌ Wrong!
+
Wrong!
{#if currentQuestion.type === 'flag-to-country'}
- The correct answer is: {getCountryName(currentQuestion.correct)}.
+
+ The correct answer is: {getCountryName(currentQuestion.correct)}.
+
+ {#if showResultCountryInfo}
+
+ {currentQuestion.correct.meta.description}
+
+ {/if}
+
{:else}
You selected the {getCountryName(currentQuestion.options[selectedAnswer])} flag.
{/if}
@@ -386,7 +515,25 @@
{:else}
-
{getCountryName(currentQuestion.correct)}
+
+ {getCountryName(currentQuestion.correct)}
+ {#if currentQuestion.correct?.meta?.description}
+
+ {#if showCountryInfo}
+
+ {currentQuestion.correct.meta.description}
+
+ {/if}
+ {/if}
+
@@ -414,9 +561,9 @@
{/if}
@@ -532,6 +679,101 @@
background: #cc3333;
}
+ /* Confirmation Dialog Styles */
+ .confirmation-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.6);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1001;
+ }
+
+ .confirmation-dialog {
+ background: var(--color-bg-primary);
+ border: 2px solid var(--color-border);
+ border-radius: 1rem;
+ padding: 0;
+ max-width: 500px;
+ width: 90%;
+ box-shadow: 0 10px 24px rgba(0,0,0,0.25);
+ }
+
+ .confirmation-header {
+ padding: 1.5rem 1.5rem 1rem 1.5rem;
+ border-bottom: 1px solid var(--color-border);
+ }
+
+ .confirmation-header h3 {
+ margin: 0;
+ font-size: 1.3rem;
+ color: #dc2626;
+ }
+
+ .confirmation-content {
+ padding: 1.5rem;
+ }
+
+ .confirmation-content p {
+ margin: 0 0 1rem 0;
+ color: var(--color-text-primary);
+ }
+
+ .confirmation-content ul {
+ margin: 1rem 0;
+ padding-left: 1.5rem;
+ color: var(--color-text-secondary);
+ }
+
+ .confirmation-content li {
+ margin: 0.5rem 0;
+ }
+
+ .confirmation-actions {
+ display: flex;
+ gap: 1rem;
+ padding: 1rem 1.5rem 1.5rem 1.5rem;
+ justify-content: flex-end;
+ border-top: 1px solid var(--color-border);
+ }
+
+ .cancel-btn {
+ background: var(--color-bg-secondary);
+ border: 1px solid var(--color-border);
+ color: var(--color-text-primary);
+ padding: 0.75rem 1.5rem;
+ border-radius: 0.5rem;
+ font-size: 0.9rem;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ }
+
+ .cancel-btn:hover {
+ background: var(--color-bg-hover);
+ border-color: var(--color-primary);
+ }
+
+ .confirm-btn {
+ background: #dc2626;
+ border: 1px solid #dc2626;
+ color: white;
+ padding: 0.75rem 1.5rem;
+ border-radius: 0.5rem;
+ font-size: 0.9rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ }
+
+ .confirm-btn:hover {
+ background: #b91c1c;
+ border-color: #b91c1c;
+ }
+
.loading {
text-align: center;
@@ -592,6 +834,7 @@
.country-display {
text-align: center;
margin-bottom: 2rem;
+ position: relative;
}
.country-name {
@@ -601,6 +844,52 @@
margin: 0;
}
+ .info-icon {
+ margin-left: 0.5rem;
+ width: 2rem;
+ height: 2rem;
+ vertical-align: middle;
+ background: none;
+ color: var(--color-text-primary);
+ cursor: pointer;
+ padding: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 5px;
+ }
+
+ .info-icon :global(.svg-wrapper) {
+ width: 100%;
+ height: 100%;
+ }
+
+ .info-icon:hover,
+ .info-icon:focus {
+ color: var(--color-text-primary);
+ border-color: var(--color-border);
+ outline: none;
+ }
+
+ .info-tooltip {
+ position: absolute;
+ left: 50%;
+ top: calc(100% + 8px);
+ transform: translateX(-50%);
+ background: var(--color-bg-secondary);
+ color: var(--color-text-primary);
+ border: 1px solid var(--color-border);
+ border-radius: 8px;
+ padding: 0.75rem 1rem;
+ width: min(90vw, 520px);
+ max-height: 40vh;
+ overflow: auto;
+ box-shadow: 0 8px 20px rgba(0,0,0,0.2);
+ z-index: 5;
+ text-align: left;
+ font-size: 0.95rem;
+ }
+
.options {
display: grid;
gap: 1rem;
@@ -700,6 +989,100 @@
color: #ef4444;
}
+ .result-icon {
+ display: inline-flex;
+ width: 24px;
+ height: 24px;
+ vertical-align: middle;
+ margin-right: 0.5rem;
+ }
+
+ .result-icon.smile-icon {
+ color: #22c55e; /* green for correct */
+ animation: correctBounce 0.6s ease-out;
+ }
+
+ .result-icon.sad-icon {
+ color: #ef4444; /* red for wrong */
+ animation: wrongShake 0.5s ease-in-out;
+ }
+
+ @keyframes correctBounce {
+ 0% {
+ transform: scale(0) rotate(0deg);
+ opacity: 0;
+ }
+ 50% {
+ transform: scale(1.3) rotate(5deg);
+ opacity: 1;
+ }
+ 100% {
+ transform: scale(1) rotate(0deg);
+ opacity: 1;
+ }
+ }
+
+ @keyframes wrongShake {
+ 0% {
+ transform: translateX(0) scale(0);
+ opacity: 0;
+ }
+ 25% {
+ transform: translateX(-5px) scale(1);
+ opacity: 1;
+ }
+ 50% {
+ transform: translateX(5px) scale(1);
+ }
+ 75% {
+ transform: translateX(-3px) scale(1);
+ }
+ 100% {
+ transform: translateX(0) scale(1);
+ opacity: 1;
+ }
+ }
+
+ .result-country-info {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ }
+
+ .result-info-btn {
+ margin-left: 0.25rem;
+ width: 2rem;
+ height: 2rem;
+ vertical-align: middle;
+ background: none;
+ border: none;
+ color: var(--color-text-primary);
+ cursor: pointer;
+ padding: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0.7;
+ transition: opacity 0.2s;
+ }
+
+ .result-info-btn:hover,
+ .result-info-btn:focus {
+ opacity: 1;
+ outline: none;
+ }
+
+ .result-info-tooltip {
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ transform: translateX(-50%);
+ margin-top: 0.5rem;
+ min-width: 200px;
+ max-width: 300px;
+ }
+
.controls {
display: flex;
justify-content: center;
@@ -757,8 +1140,8 @@
@media (max-width: 768px) {
.container {
- padding: 0.75rem;
- padding-top: 0.75rem;
+ padding: 0.75rem;
+ padding-top: 0.75rem;
}
.options {
@@ -775,6 +1158,12 @@
max-height: 180px;
}
+ .info-tooltip {
+ width: 92vw;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+
.controls {
flex-direction: row;
flex-wrap: nowrap;