Chapter1und2 / index.html
abeea's picture
Update index.html
1e3ae16 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Netzwerk A1 — German Vocabulary Practice | Flashcards, Quizzes & Hardcore Mode</title>
<!-- SEO Meta Tags -->
<meta name="description"
content="Master German vocabulary from Netzwerk A1 Kapitel 1 & 2 with interactive flashcards, MCQ quizzes, matching games, typing challenges, and hardcore practice modes. 170+ words across 6 levels with smart mistake repetition for better memorization.">
<meta name="keywords"
content="German vocabulary, Netzwerk A1, learn German, German flashcards, German quiz, Kapitel 1, Kapitel 2, A1 German, German practice, Deutsch lernen, German language, vocabulary trainer, MCQ German, German words">
<meta name="author" content="Abdullah Tarar">
<meta name="robots" content="index, follow">
<meta name="language" content="English">
<meta name="revisit-after" content="7 days">
<meta name="theme-color" content="#0e0c1a">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="Netzwerk A1 — German Vocabulary Practice Tool">
<meta property="og:description"
content="Interactive German vocabulary trainer with flashcards, quizzes, matching games & hardcore practice modes. 170+ words, 6 levels, smart mistake repetition.">
<meta property="og:site_name" content="Netzwerk A1 Practice">
<meta property="og:locale" content="en_US">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Netzwerk A1 — German Vocabulary Practice Tool">
<meta name="twitter:description"
content="Master German vocabulary with interactive flashcards, quizzes, matching games & hardcore practice modes. 170+ words across 6 levels.">
<!-- Canonical -->
<link rel="canonical" href="./">
<!-- Structured Data (JSON-LD) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "Netzwerk A1 German Vocabulary Practice",
"description": "An interactive German vocabulary learning tool for Netzwerk A1 (Kapitel 1 and 2) featuring flashcards, multiple-choice quizzes, matching games, typing challenges, fill-in-the-blank exercises, and hardcore practice modes with smart mistake repetition.",
"applicationCategory": "EducationalApplication",
"operatingSystem": "Any",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"author": {
"@type": "Person",
"name": "Abdullah Tarar",
"sameAs": "https://instagram.com/abdullahtarar.3"
},
"inLanguage": ["en", "de"],
"educationalLevel": "Beginner",
"learningResourceType": "Interactive Resource",
"keywords": "German, vocabulary, A1, Netzwerk, flashcards, quiz, language learning"
}
</script>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700;900&family=DM+Mono:wght@300;400;500&family=DM+Sans:wght@300;400;500&display=swap"
rel="stylesheet">
<style>
:root {
--bg: #0e0c1a;
--surface: #16132b;
--card: #1e1a35;
--border: #2e2850;
--accent: #e8c547;
--accent2: #7c6fff;
--accent3: #ff6b6b;
--accent4: #4ecdc4;
--text: #f0eeff;
--muted: #8a82aa;
--success: #4ecdc4;
--error: #ff6b6b;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'DM Sans', sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
overflow-x: hidden;
}
/* Background noise texture */
body::before {
content: '';
position: fixed;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.03'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 0;
}
/* Gradient blobs */
.blob {
position: fixed;
border-radius: 50%;
filter: blur(80px);
opacity: 0.12;
pointer-events: none;
z-index: 0;
}
.blob1 {
width: 400px;
height: 400px;
background: var(--accent2);
top: -100px;
left: -100px;
}
.blob2 {
width: 300px;
height: 300px;
background: var(--accent);
bottom: -50px;
right: -50px;
}
.blob3 {
width: 200px;
height: 200px;
background: var(--accent3);
top: 50%;
left: 60%;
}
/* Header */
header {
position: relative;
z-index: 10;
padding: 40px 40px 30px;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 20px;
}
.logo {
font-family: 'Playfair Display', serif;
font-size: 2rem;
font-weight: 900;
letter-spacing: -1px;
}
.logo span {
color: var(--accent);
}
.score-display {
font-family: 'DM Mono', monospace;
font-size: 0.85rem;
color: var(--muted);
background: var(--card);
border: 1px solid var(--border);
padding: 8px 18px;
border-radius: 100px;
display: flex;
gap: 20px;
}
.score-display b {
color: var(--accent);
}
/* Mode selector */
.mode-bar {
position: relative;
z-index: 10;
display: flex;
gap: 8px;
padding: 20px 40px;
border-bottom: 1px solid var(--border);
overflow-x: auto;
scrollbar-width: none;
}
.mode-bar::-webkit-scrollbar {
display: none;
}
/* Level selector bar */
.level-bar {
position: relative;
z-index: 10;
background: linear-gradient(135deg, rgba(124, 111, 255, 0.05) 0%, transparent 100%);
border-bottom: 1px solid var(--border);
}
.level-btn {
font-family: 'DM Mono', monospace;
font-size: 0.78rem;
letter-spacing: 0.05em;
padding: 10px 16px;
border-radius: 100px;
border: 1.5px solid var(--border);
background: transparent;
color: var(--muted);
cursor: pointer;
white-space: nowrap;
transition: all 0.2s;
}
.level-btn:hover:not(.locked) {
border-color: var(--accent2);
color: var(--text);
}
.level-btn.locked {
opacity: 0.4;
cursor: default;
}
.level-btn.active {
background: var(--accent2);
color: white;
border-color: var(--accent2);
font-weight: 600;
}
.level-btn.level-1 {
--accent-color: #7c6fff;
}
.level-btn.level-2 {
--accent-color: #e8c547;
}
.level-btn.level-3 {
--accent-color: #ff6b6b;
}
.level-btn.level-4 {
--accent-color: #4ecdc4;
}
.level-btn.level-5 {
--accent-color: #a8e6cf;
}
.level-btn.level-6 {
--accent-color: #ffd93d;
}
.mode-btn {
font-family: 'DM Mono', monospace;
font-size: 0.78rem;
letter-spacing: 0.05em;
padding: 8px 18px;
border-radius: 100px;
border: 1px solid var(--border);
background: transparent;
color: var(--muted);
cursor: pointer;
white-space: nowrap;
transition: all 0.2s;
}
.mode-btn:hover {
border-color: var(--accent2);
color: var(--text);
}
.mode-btn.active {
background: var(--accent);
color: var(--bg);
border-color: var(--accent);
font-weight: 500;
}
/* Main area */
main {
position: relative;
z-index: 10;
padding: 40px;
max-width: 900px;
margin: 0 auto;
}
.section-title {
font-family: 'Playfair Display', serif;
font-size: 1.5rem;
margin-bottom: 8px;
color: var(--text);
}
.section-sub {
font-size: 0.85rem;
color: var(--muted);
margin-bottom: 30px;
font-family: 'DM Mono', monospace;
}
/* Flashcard */
.flashcard-wrap {
perspective: 1200px;
width: 100%;
max-width: 580px;
margin: 0 auto 30px;
}
.flashcard {
width: 100%;
aspect-ratio: 1.6;
position: relative;
transform-style: preserve-3d;
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.flashcard.flipped {
transform: rotateY(180deg);
}
.flashcard-face {
position: absolute;
inset: 0;
backface-visibility: hidden;
border-radius: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
text-align: center;
border: 1px solid var(--border);
}
.flashcard-front {
background: var(--card);
background-image: linear-gradient(135deg, rgba(124, 111, 255, 0.08) 0%, transparent 60%);
}
.flashcard-back {
background: var(--surface);
background-image: linear-gradient(135deg, rgba(232, 197, 71, 0.08) 0%, transparent 60%);
transform: rotateY(180deg);
}
.card-tag {
font-family: 'DM Mono', monospace;
font-size: 0.7rem;
color: var(--muted);
letter-spacing: 0.1em;
text-transform: uppercase;
margin-bottom: 20px;
}
.card-word {
font-family: 'Playfair Display', serif;
font-size: clamp(1.5rem, 4vw, 2.5rem);
font-weight: 700;
color: var(--text);
margin-bottom: 12px;
line-height: 1.2;
}
.card-example {
font-size: 0.85rem;
color: var(--muted);
font-style: italic;
line-height: 1.5;
}
.card-translation {
font-family: 'Playfair Display', serif;
font-size: clamp(1.2rem, 3vw, 2rem);
color: var(--accent);
font-weight: 700;
}
.card-hint {
font-family: 'DM Mono', monospace;
font-size: 0.75rem;
color: var(--accent2);
margin-top: 12px;
}
.flip-hint {
text-align: center;
font-size: 0.78rem;
color: var(--muted);
font-family: 'DM Mono', monospace;
margin-bottom: 24px;
}
.card-controls {
display: flex;
gap: 12px;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 30px;
}
.btn {
font-family: 'DM Sans', sans-serif;
font-size: 0.85rem;
font-weight: 500;
padding: 10px 24px;
border-radius: 100px;
border: 1px solid var(--border);
cursor: pointer;
transition: all 0.2s;
background: var(--card);
color: var(--text);
}
.btn:hover {
transform: translateY(-2px);
}
.btn.primary {
background: var(--accent);
color: var(--bg);
border-color: var(--accent);
}
.btn.success {
background: rgba(78, 205, 196, 0.15);
border-color: var(--success);
color: var(--success);
}
.btn.danger {
background: rgba(255, 107, 107, 0.15);
border-color: var(--error);
color: var(--error);
}
.progress-bar {
height: 3px;
background: var(--border);
border-radius: 10px;
overflow: hidden;
margin-bottom: 20px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent2), var(--accent));
border-radius: 10px;
transition: width 0.4s ease;
}
.card-counter {
text-align: center;
font-family: 'DM Mono', monospace;
font-size: 0.78rem;
color: var(--muted);
margin-bottom: 16px;
}
/* Quiz */
.quiz-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 20px;
padding: 40px;
margin-bottom: 20px;
background-image: linear-gradient(135deg, rgba(124, 111, 255, 0.05) 0%, transparent 50%);
}
.quiz-question {
font-family: 'Playfair Display', serif;
font-size: clamp(1.3rem, 3vw, 1.8rem);
font-weight: 700;
margin-bottom: 10px;
color: var(--text);
}
.quiz-context {
font-size: 0.83rem;
color: var(--muted);
font-style: italic;
margin-bottom: 30px;
}
.quiz-options {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
@media (max-width: 500px) {
.quiz-options {
grid-template-columns: 1fr;
}
}
.quiz-option {
padding: 14px 20px;
border-radius: 12px;
border: 1px solid var(--border);
background: var(--surface);
color: var(--text);
font-size: 0.9rem;
cursor: pointer;
transition: all 0.2s;
text-align: left;
font-family: 'DM Sans', sans-serif;
}
.quiz-option:hover:not(:disabled) {
border-color: var(--accent2);
background: rgba(124, 111, 255, 0.1);
transform: translateY(-2px);
}
.quiz-option.correct {
border-color: var(--success);
background: rgba(78, 205, 196, 0.15);
color: var(--success);
}
.quiz-option.wrong {
border-color: var(--error);
background: rgba(255, 107, 107, 0.15);
color: var(--error);
}
.quiz-option:disabled {
cursor: default;
}
.feedback-msg {
text-align: center;
font-family: 'DM Mono', monospace;
font-size: 0.85rem;
padding: 12px;
border-radius: 10px;
margin-top: 16px;
display: none;
}
.feedback-msg.show {
display: block;
}
.feedback-msg.ok {
background: rgba(78, 205, 196, 0.1);
color: var(--success);
}
.feedback-msg.bad {
background: rgba(255, 107, 107, 0.1);
color: var(--error);
}
/* Matching */
.matching-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.match-col {
display: flex;
flex-direction: column;
gap: 10px;
}
.match-item {
padding: 12px 18px;
border-radius: 12px;
border: 1.5px solid var(--border);
background: var(--surface);
cursor: pointer;
transition: all 0.2s;
font-size: 0.88rem;
text-align: center;
user-select: none;
}
.match-item:hover:not(.matched):not(.selected) {
border-color: var(--accent2);
background: rgba(124, 111, 255, 0.1);
}
.match-item.selected {
border-color: var(--accent);
background: rgba(232, 197, 71, 0.1);
color: var(--accent);
}
.match-item.matched {
border-color: var(--success);
background: rgba(78, 205, 196, 0.1);
color: var(--success);
opacity: 0.7;
cursor: default;
}
.match-item.wrong-flash {
border-color: var(--error);
background: rgba(255, 107, 107, 0.15);
}
/* Type input */
.type-wrap {
display: flex;
gap: 12px;
flex-wrap: wrap;
align-items: center;
margin-top: 24px;
}
.type-input {
flex: 1;
min-width: 200px;
padding: 12px 20px;
border-radius: 12px;
border: 1px solid var(--border);
background: var(--surface);
color: var(--text);
font-size: 1rem;
font-family: 'DM Sans', sans-serif;
outline: none;
transition: border-color 0.2s;
}
.type-input:focus {
border-color: var(--accent2);
}
.type-input.correct-input {
border-color: var(--success);
}
.type-input.wrong-input {
border-color: var(--error);
}
/* Vocabulary browser */
.vocab-filters {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 20px;
}
.filter-btn {
padding: 6px 14px;
border-radius: 100px;
border: 1px solid var(--border);
background: transparent;
color: var(--muted);
font-size: 0.78rem;
font-family: 'DM Mono', monospace;
cursor: pointer;
transition: all 0.2s;
}
.filter-btn.active {
background: var(--accent2);
color: white;
border-color: var(--accent2);
}
.vocab-search {
width: 100%;
padding: 12px 20px;
border-radius: 12px;
border: 1px solid var(--border);
background: var(--surface);
color: var(--text);
font-size: 0.9rem;
font-family: 'DM Sans', sans-serif;
outline: none;
margin-bottom: 20px;
transition: border-color 0.2s;
}
.vocab-search:focus {
border-color: var(--accent2);
}
.vocab-search::placeholder {
color: var(--muted);
}
.vocab-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.vocab-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 20px;
border-radius: 12px;
border: 1px solid var(--border);
background: var(--card);
transition: all 0.2s;
gap: 16px;
flex-wrap: wrap;
}
.vocab-item:hover {
border-color: var(--border);
transform: translateX(4px);
}
.vocab-de {
font-family: 'Playfair Display', serif;
font-size: 1rem;
font-weight: 700;
color: var(--text);
flex: 1;
}
.vocab-en {
font-size: 0.85rem;
color: var(--muted);
flex: 1;
text-align: right;
}
.vocab-badge {
font-family: 'DM Mono', monospace;
font-size: 0.65rem;
padding: 3px 10px;
border-radius: 100px;
background: var(--surface);
border: 1px solid var(--border);
color: var(--muted);
white-space: nowrap;
}
.vocab-badge.kap1 {
border-color: var(--accent2);
color: var(--accent2);
}
.vocab-badge.kap2 {
border-color: var(--accent);
color: var(--accent);
}
.vocab-badge.high {
border-color: var(--accent3);
color: var(--accent3);
}
/* Fill-in-blank */
.fib-sentence {
font-size: 1.2rem;
line-height: 2;
margin-bottom: 20px;
color: var(--text);
}
.blank-input {
display: inline-block;
border: none;
border-bottom: 2px solid var(--accent);
background: transparent;
color: var(--accent);
font-size: 1.1rem;
font-family: 'DM Sans', sans-serif;
width: 120px;
outline: none;
text-align: center;
padding: 0 4px;
transition: border-color 0.2s;
}
.blank-input:focus {
border-color: var(--accent2);
}
.blank-input.correct-b {
border-color: var(--success);
color: var(--success);
}
.blank-input.wrong-b {
border-color: var(--error);
color: var(--error);
}
/* Result screen */
.result-screen {
text-align: center;
padding: 60px 20px;
}
.result-emoji {
font-size: 4rem;
margin-bottom: 20px;
}
.result-title {
font-family: 'Playfair Display', serif;
font-size: 2rem;
font-weight: 900;
margin-bottom: 10px;
color: var(--accent);
}
.result-sub {
font-family: 'DM Mono', monospace;
font-size: 0.9rem;
color: var(--muted);
margin-bottom: 30px;
}
/* Animations */
@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
@keyframes pop {
0% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
.fadeUp {
animation: fadeUp 0.4s ease forwards;
}
.animate-pop {
animation: pop 0.3s ease;
}
.hidden {
display: none !important;
}
/* Section container */
.practice-section {
animation: fadeUp 0.4s ease;
}
/* Category pills for vocab */
.cat-label {
font-family: 'DM Mono', monospace;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--muted);
padding: 16px 0 8px;
}
/* Days of week section */
.days-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
gap: 10px;
margin-bottom: 30px;
}
.day-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 14px 12px;
text-align: center;
transition: all 0.2s;
cursor: default;
}
.day-card:hover {
border-color: var(--accent2);
transform: translateY(-3px);
}
.day-de {
font-family: 'Playfair Display', serif;
font-size: 0.95rem;
font-weight: 700;
color: var(--text);
}
.day-en {
font-size: 0.75rem;
color: var(--muted);
margin-top: 4px;
}
.months-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 10px;
margin-bottom: 30px;
}
.month-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 12px;
text-align: center;
transition: all 0.2s;
cursor: default;
}
.month-card:hover {
border-color: var(--accent);
transform: translateY(-3px);
}
.month-de {
font-family: 'Playfair Display', serif;
font-size: 0.88rem;
font-weight: 700;
color: var(--text);
}
.month-num {
font-family: 'DM Mono', monospace;
font-size: 0.7rem;
color: var(--accent);
margin-top: 4px;
}
.seasons-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 14px;
margin-bottom: 30px;
}
.season-card {
padding: 20px;
border-radius: 16px;
border: 1px solid var(--border);
background: var(--card);
transition: all 0.2s;
cursor: default;
}
.season-card:hover {
transform: translateY(-3px);
}
.season-icon {
font-size: 2rem;
margin-bottom: 8px;
}
.season-de {
font-family: 'Playfair Display', serif;
font-size: 1.1rem;
font-weight: 700;
}
.season-en {
font-size: 0.8rem;
color: var(--muted);
margin-top: 4px;
}
.info-box {
background: var(--card);
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
border-radius: 12px;
padding: 16px 20px;
margin-bottom: 16px;
font-size: 0.88rem;
line-height: 1.6;
color: var(--muted);
}
.info-box strong {
color: var(--text);
}
.greeting-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-bottom: 24px;
}
@media (max-width: 480px) {
.greeting-grid {
grid-template-columns: 1fr;
}
}
.greeting-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 14px 18px;
transition: all 0.2s;
cursor: default;
}
.greeting-card:hover {
border-color: var(--accent2);
transform: translateX(4px);
}
.greeting-de {
font-family: 'Playfair Display', serif;
font-weight: 700;
font-size: 0.95rem;
color: var(--text);
}
.greeting-en {
font-size: 0.78rem;
color: var(--muted);
margin-top: 3px;
}
/* Jobs grid */
.jobs-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
gap: 10px;
margin-bottom: 20px;
}
.job-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 14px 16px;
transition: all 0.2s;
cursor: default;
}
.job-card:hover {
border-color: var(--accent4);
transform: translateY(-2px);
}
.job-de {
font-family: 'Playfair Display', serif;
font-size: 0.88rem;
font-weight: 700;
color: var(--text);
}
.job-de-f {
font-size: 0.76rem;
color: var(--accent2);
margin-top: 2px;
font-family: 'DM Mono', monospace;
}
.job-en {
font-size: 0.78rem;
color: var(--muted);
margin-top: 4px;
}
/* Hobbies */
.hobby-grid {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 24px;
}
.hobby-pill {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
border-radius: 100px;
border: 1px solid var(--border);
background: var(--card);
transition: all 0.2s;
cursor: default;
}
.hobby-pill:hover {
border-color: var(--accent4);
transform: scale(1.03);
}
.hobby-de {
font-weight: 500;
font-size: 0.88rem;
}
.hobby-en {
font-size: 0.78rem;
color: var(--muted);
}
/* Confetti burst (CSS only) */
.confetti-wrap {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 9999;
}
.confetti-piece {
position: absolute;
width: 8px;
height: 8px;
border-radius: 2px;
animation: confettiFall 1.2s ease-in forwards;
opacity: 0;
}
@keyframes confettiFall {
0% {
opacity: 1;
transform: translateY(-20px) rotate(0deg);
}
100% {
opacity: 0;
transform: translateY(60vh) rotate(360deg);
}
}
/* Scrollbar */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: var(--bg);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 10px;
}
/* Developer Footer */
.developer-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
background: linear-gradient(135deg, rgba(30, 26, 53, 0.97) 0%, rgba(14, 12, 26, 0.98) 100%);
border-top: 1px solid var(--border);
padding: 10px 20px;
text-align: center;
font-family: 'DM Mono', monospace;
font-size: 0.75rem;
color: var(--muted);
backdrop-filter: blur(12px);
}
.developer-footer a {
color: var(--accent);
text-decoration: none;
font-weight: 500;
transition: all 0.2s;
}
.developer-footer a:hover {
color: var(--accent2);
text-shadow: 0 0 8px rgba(124, 111, 255, 0.4);
}
/* Practice/Hardcore Status Bar */
.quiz-status-bar {
display: flex;
gap: 12px;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 20px;
padding: 12px 16px;
background: var(--card);
border: 1px solid var(--border);
border-radius: 14px;
}
.status-item {
display: flex;
align-items: center;
gap: 6px;
font-family: 'DM Mono', monospace;
font-size: 0.78rem;
color: var(--muted);
padding: 4px 12px;
border-radius: 100px;
background: var(--surface);
border: 1px solid var(--border);
}
.status-item .status-val {
font-weight: 600;
color: var(--text);
}
.status-item.mistakes .status-val {
color: var(--error);
}
.status-item.correct-stat .status-val {
color: var(--success);
}
/* Timer */
.quiz-timer {
display: flex;
align-items: center;
gap: 6px;
font-family: 'DM Mono', monospace;
font-size: 0.85rem;
color: var(--accent);
justify-content: center;
margin-bottom: 16px;
}
.quiz-timer .timer-icon {
font-size: 1rem;
}
/* Memory Tip */
.memory-tip {
margin-top: 12px;
padding: 10px 16px;
border-radius: 10px;
background: linear-gradient(135deg, rgba(232, 197, 71, 0.08) 0%, rgba(124, 111, 255, 0.06) 100%);
border: 1px solid rgba(232, 197, 71, 0.2);
font-family: 'DM Mono', monospace;
font-size: 0.78rem;
color: var(--accent);
display: none;
}
.memory-tip.show {
display: block;
animation: fadeUp 0.3s ease;
}
/* Hardcore Result Detail */
.result-detail-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
max-width: 400px;
margin: 20px auto;
}
.result-stat-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 16px;
text-align: center;
}
.result-stat-card .stat-label {
font-family: 'DM Mono', monospace;
font-size: 0.7rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 6px;
}
.result-stat-card .stat-value {
font-family: 'Playfair Display', serif;
font-size: 1.5rem;
font-weight: 700;
color: var(--accent);
}
.result-stat-card.error-stat .stat-value {
color: var(--error);
}
.result-stat-card.success-stat .stat-value {
color: var(--success);
}
/* Wrong Words List */
.wrong-words-list {
max-width: 500px;
margin: 20px auto;
text-align: left;
}
.wrong-words-list .ww-title {
font-family: 'DM Mono', monospace;
font-size: 0.78rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 10px;
}
.wrong-word-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 16px;
border-radius: 10px;
border: 1px solid rgba(255, 107, 107, 0.2);
background: rgba(255, 107, 107, 0.05);
margin-bottom: 6px;
font-size: 0.85rem;
}
.wrong-word-item .ww-de {
font-weight: 600;
color: var(--text);
}
.wrong-word-item .ww-en {
color: var(--muted);
font-size: 0.78rem;
}
.wrong-word-item .ww-tip {
font-family: 'DM Mono', monospace;
font-size: 0.68rem;
color: var(--accent);
}
/* Practice empty state */
.practice-empty {
text-align: center;
padding: 60px 20px;
}
.practice-empty .empty-icon {
font-size: 4rem;
margin-bottom: 16px;
}
.practice-empty .empty-title {
font-family: 'Playfair Display', serif;
font-size: 1.5rem;
font-weight: 700;
color: var(--text);
margin-bottom: 8px;
}
.practice-empty .empty-sub {
font-family: 'DM Mono', monospace;
font-size: 0.85rem;
color: var(--muted);
}
/* Pad bottom for footer */
body {
padding-bottom: 45px;
}
/* Confirmation Modal */
.confirm-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.72);
z-index: 300;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(8px);
animation: fadeUp 0.15s ease;
}
.confirm-overlay.hidden {
display: none;
}
.confirm-box {
background: var(--card);
border: 1px solid var(--border);
border-radius: 22px;
padding: 36px 28px 28px;
max-width: 360px;
width: 90%;
text-align: center;
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5);
}
.confirm-box .cbox-emoji {
font-size: 2.8rem;
margin-bottom: 12px;
}
.confirm-box .cbox-title {
font-family: 'Playfair Display', serif;
font-size: 1.25rem;
font-weight: 700;
color: var(--text);
margin-bottom: 8px;
}
.confirm-box .cbox-msg {
font-family: 'DM Mono', monospace;
font-size: 0.8rem;
color: var(--muted);
margin-bottom: 24px;
line-height: 1.6;
}
.confirm-btns {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
}
/* Hardcore Level Picker */
.hc-level-picker {
display: flex;
gap: 8px;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 20px;
}
.hc-level-btn {
padding: 8px 18px;
border-radius: 100px;
border: 1px solid var(--border);
background: var(--surface);
color: var(--muted);
font-family: 'DM Mono', monospace;
font-size: 0.78rem;
cursor: pointer;
transition: all 0.2s;
}
.hc-level-btn:hover {
border-color: var(--accent);
color: var(--accent);
}
.hc-level-btn.active {
background: var(--accent);
color: #0e0c1a;
border-color: var(--accent);
font-weight: 600;
}
</style>
</head>
<body>
<div class="blob blob1"></div>
<div class="blob blob2"></div>
<div class="blob blob3"></div>
<header>
<div class="logo">Netz<span>werk</span> A1</div>
<div class="score-display">
Score: <b id="totalScore">0</b> pts &nbsp;|&nbsp; Streak: <b id="streakCount">0</b> 🔥
</div>
</header>
<div class="mode-bar">
<button class="mode-btn active" onclick="showSection('flashcards')">📚 Flashcards</button>
<button class="mode-btn" onclick="showSection('quiz')">🧠 Quiz</button>
<button class="mode-btn" onclick="showSection('matching')">🔗 Matching</button>
<button class="mode-btn" onclick="showSection('typeit')">✍️ Type It</button>
<button class="mode-btn" onclick="showSection('fillin')">📝 Fill-in-Blank</button>
<button class="mode-btn" onclick="showSection('practice')">🔄 Practice</button>
<button class="mode-btn" onclick="showSection('hardcore')">🔥 Hardcore</button>
<button class="mode-btn" onclick="showSection('allhardcore')">💀 All Hardcore</button>
<button class="mode-btn" onclick="showSection('reference')">📖 Reference</button>
</div>
<div class="level-bar" id="levelBar">
<div
style="font-family:'DM Mono',monospace;font-size:0.75rem;color:var(--muted);padding:8px 40px;text-transform:uppercase;letter-spacing:0.05em;">
📊 Your Level Progress</div>
<div style="display:flex;gap:8px;padding:12px 40px;overflow-x:auto;scrollbar-width:none;">
<button class="level-btn level-1" onclick="selectLevel(1)" id="levelBtn1">🎯 Level 1</button>
<button class="level-btn level-2 locked" onclick="selectLevel(2)" id="levelBtn2">🔒 Level 2</button>
<button class="level-btn level-3 locked" onclick="selectLevel(3)" id="levelBtn3">🔒 Level 3</button>
<button class="level-btn level-4 locked" onclick="selectLevel(4)" id="levelBtn4">🔒 Level 4</button>
<button class="level-btn level-5 locked" onclick="selectLevel(5)" id="levelBtn5">🔒 Level 5</button>
<button class="level-btn level-6 locked" onclick="selectLevel(6)" id="levelBtn6">🔒 Level 6</button>
</div>
</div>
<main>
<!-- ═══════════ FLASHCARDS ═══════════ -->
<div id="sec-flashcards" class="practice-section">
<div class="section-title">Flashcards</div>
<div class="section-sub">Click the card to reveal the translation</div>
<div class="vocab-filters" style="margin-bottom:20px">
<button class="filter-btn active" onclick="setCardFilter('all',this)">All</button>
<button class="filter-btn" onclick="setCardFilter('1',this)">Kapitel 1</button>
<button class="filter-btn" onclick="setCardFilter('2',this)">Kapitel 2</button>
<button class="filter-btn" onclick="setCardFilter('high',this)">⭐ High-Freq</button>
</div>
<div class="progress-bar">
<div class="progress-fill" id="cardProgress" style="width:0%"></div>
</div>
<div class="card-counter" id="cardCounter">Card 1 of ?</div>
<div class="flashcard-wrap">
<div class="flashcard" id="mainCard" onclick="flipCard()">
<div class="flashcard-face flashcard-front">
<div class="card-tag" id="cardTag">GERMAN</div>
<div class="card-word" id="cardWord"></div>
<div class="card-example" id="cardExample"></div>
</div>
<div class="flashcard-face flashcard-back">
<div class="card-tag">ENGLISH</div>
<div class="card-translation" id="cardTranslation"></div>
<div class="card-hint" id="cardHint"></div>
</div>
</div>
</div>
<div class="flip-hint">↑ Click card to flip</div>
<div class="card-controls">
<button class="btn danger" onclick="cardKnew(false)">✗ Still Learning</button>
<button class="btn" onclick="flipCard()">Flip</button>
<button class="btn success" onclick="cardKnew(true)">✓ Got It!</button>
</div>
<div class="card-controls">
<button class="btn" onclick="prevCard()">← Prev</button>
<button class="btn primary" onclick="shuffleCards()">🔀 Shuffle</button>
<button class="btn" onclick="nextCard()">Next →</button>
</div>
</div>
<!-- ═══════════ QUIZ ═══════════ -->
<div id="sec-quiz" class="practice-section hidden">
<div class="section-title">Multiple Choice Quiz</div>
<div class="section-sub">Select the correct English translation</div>
<div class="progress-bar">
<div class="progress-fill" id="quizProgress" style="width:0%"></div>
</div>
<div class="card-counter" id="quizCounter">Question 1 of 20</div>
<div class="quiz-card" id="quizCard">
<div class="quiz-question" id="quizQ">Loading...</div>
<div class="quiz-context" id="quizCtx"></div>
<div class="quiz-options" id="quizOpts"></div>
<div class="feedback-msg" id="quizFeedback"></div>
</div>
<div class="card-controls">
<button class="btn primary" id="quizNextBtn" onclick="nextQuiz()" style="display:none">Next Question →</button>
<button class="btn" onclick="resetQuiz()">↺ Restart Quiz</button>
</div>
<div id="quizResult" class="result-screen hidden">
<div class="result-emoji" id="quizEmoji">🎉</div>
<div class="result-title" id="quizScore">0 / 20</div>
<div class="result-sub" id="quizMsg">Keep practicing!</div>
<button class="btn primary" onclick="resetQuiz()">Play Again</button>
</div>
</div>
<!-- ═══════════ MATCHING ═══════════ -->
<div id="sec-matching" class="practice-section hidden">
<div class="section-title">Matching Game</div>
<div class="section-sub">Click a German word, then its English match</div>
<div class="card-controls" style="margin-bottom:20px">
<button class="btn primary" onclick="resetMatching()">New Round</button>
<span id="matchScore" style="font-family:'DM Mono',monospace;font-size:0.85rem;color:var(--muted)">Matched:
0/8</span>
</div>
<div class="matching-grid">
<div class="match-col" id="matchLeft"></div>
<div class="match-col" id="matchRight"></div>
</div>
<div id="matchDone" class="result-screen hidden">
<div class="result-emoji">🎊</div>
<div class="result-title">Perfect Match!</div>
<div class="result-sub">You matched all pairs correctly.</div>
<button class="btn primary" onclick="resetMatching()">Play Again</button>
</div>
</div>
<!-- ═══════════ TYPE IT ═══════════ -->
<div id="sec-typeit" class="practice-section hidden">
<div class="section-title">Type It</div>
<div class="section-sub">Type the German word for the English prompt</div>
<div class="progress-bar">
<div class="progress-fill" id="typeProgress" style="width:0%"></div>
</div>
<div class="card-counter" id="typeCounter">Word 1 of ?</div>
<div class="quiz-card" id="typeCard">
<div class="card-tag">TRANSLATE TO GERMAN</div>
<div class="quiz-question" id="typeQ" style="margin-top:12px"></div>
<div class="quiz-context" id="typeCtx"></div>
<div class="type-wrap">
<input class="type-input" id="typeInput" placeholder="Type German here..."
onkeydown="if(event.key==='Enter')checkType()">
<button class="btn primary" onclick="checkType()">Check</button>
<button class="btn" onclick="skipType()">Skip</button>
</div>
<div class="feedback-msg" id="typeFeedback"></div>
</div>
<div class="card-controls" style="margin-top:16px">
<button class="btn" onclick="resetType()">↺ Restart</button>
</div>
</div>
<!-- ═══════════ FILL-IN-BLANK ═══════════ -->
<div id="sec-fillin" class="practice-section hidden">
<div class="section-title">Fill in the Blank</div>
<div class="section-sub">Complete the German sentence</div>
<div class="progress-bar">
<div class="progress-fill" id="fibProgress" style="width:0%"></div>
</div>
<div class="card-counter" id="fibCounter">Sentence 1 of ?</div>
<div class="quiz-card" id="fibCard">
<div class="fib-sentence" id="fibSentence"></div>
<div class="card-controls" style="justify-content:flex-start;margin-top:10px;margin-bottom:0">
<button class="btn primary" onclick="checkFib()">Check</button>
<button class="btn" onclick="skipFib()">Skip</button>
<button class="btn" onclick="hintFib()">💡 Hint</button>
</div>
<div class="feedback-msg" id="fibFeedback"></div>
</div>
<div class="card-controls" style="margin-top:16px">
<button class="btn" onclick="resetFib()">↺ Restart</button>
</div>
</div>
<!-- ═══════════ REFERENCE ═══════════ -->
<div id="sec-reference" class="practice-section hidden">
<div class="section-title">Reference Guide</div>
<div class="section-sub">Browse all vocabulary organized by topic</div>
<!-- Greetings -->
<div class="cat-label">👋 Greetings & Farewells — Kapitel 1</div>
<div class="greeting-grid">
<div class="greeting-card">
<div class="greeting-de">Hallo!</div>
<div class="greeting-en">Hello!</div>
</div>
<div class="greeting-card">
<div class="greeting-de">Guten Morgen!</div>
<div class="greeting-en">Good morning!</div>
</div>
<div class="greeting-card">
<div class="greeting-de">Guten Tag!</div>
<div class="greeting-en">Good day! Hello!</div>
</div>
<div class="greeting-card">
<div class="greeting-de">Guten Abend!</div>
<div class="greeting-en">Good evening!</div>
</div>
<div class="greeting-card">
<div class="greeting-de">Gute Nacht!</div>
<div class="greeting-en">Good night!</div>
</div>
<div class="greeting-card">
<div class="greeting-de">Tschüs!</div>
<div class="greeting-en">Bye!</div>
</div>
<div class="greeting-card">
<div class="greeting-de">Auf Wiedersehen!</div>
<div class="greeting-en">Goodbye!</div>
</div>
<div class="greeting-card">
<div class="greeting-de">Bis bald!</div>
<div class="greeting-en">See you soon!</div>
</div>
</div>
<div class="info-box">
<strong>How are you?</strong><br>
Wie geht's? / Wie geht es Ihnen? → Danke, gut. / Es geht. / Sehr gut! / Nicht schlecht.
</div>
<!-- Days -->
<div class="cat-label">📅 Days of the Week — Kapitel 2</div>
<div class="days-grid">
<div class="day-card">
<div class="day-de">der Montag</div>
<div class="day-en">Monday</div>
</div>
<div class="day-card">
<div class="day-de">der Dienstag</div>
<div class="day-en">Tuesday</div>
</div>
<div class="day-card">
<div class="day-de">der Mittwoch</div>
<div class="day-en">Wednesday</div>
</div>
<div class="day-card">
<div class="day-de">der Donnerstag</div>
<div class="day-en">Thursday</div>
</div>
<div class="day-card">
<div class="day-de">der Freitag</div>
<div class="day-en">Friday</div>
</div>
<div class="day-card">
<div class="day-de">der Samstag</div>
<div class="day-en">Saturday</div>
</div>
<div class="day-card">
<div class="day-de">der Sonntag</div>
<div class="day-en">Sunday</div>
</div>
</div>
<!-- Months -->
<div class="cat-label">📆 Months — Kapitel 2</div>
<div class="months-grid">
<div class="month-card">
<div class="month-de">Januar</div>
<div class="month-num">01</div>
</div>
<div class="month-card">
<div class="month-de">Februar</div>
<div class="month-num">02</div>
</div>
<div class="month-card">
<div class="month-de">März</div>
<div class="month-num">03</div>
</div>
<div class="month-card">
<div class="month-de">April</div>
<div class="month-num">04</div>
</div>
<div class="month-card">
<div class="month-de">Mai</div>
<div class="month-num">05</div>
</div>
<div class="month-card">
<div class="month-de">Juni</div>
<div class="month-num">06</div>
</div>
<div class="month-card">
<div class="month-de">Juli</div>
<div class="month-num">07</div>
</div>
<div class="month-card">
<div class="month-de">August</div>
<div class="month-num">08</div>
</div>
<div class="month-card">
<div class="month-de">September</div>
<div class="month-num">09</div>
</div>
<div class="month-card">
<div class="month-de">Oktober</div>
<div class="month-num">10</div>
</div>
<div class="month-card">
<div class="month-de">November</div>
<div class="month-num">11</div>
</div>
<div class="month-card">
<div class="month-de">Dezember</div>
<div class="month-num">12</div>
</div>
</div>
<!-- Seasons -->
<div class="cat-label">🍂 Seasons — Kapitel 2</div>
<div class="seasons-grid">
<div class="season-card">
<div class="season-icon">🌸</div>
<div class="season-de">der Frühling</div>
<div class="season-en">spring</div>
</div>
<div class="season-card">
<div class="season-icon">☀️</div>
<div class="season-de">der Sommer</div>
<div class="season-en">summer</div>
</div>
<div class="season-card">
<div class="season-icon">🍂</div>
<div class="season-de">der Herbst</div>
<div class="season-en">fall / autumn</div>
</div>
<div class="season-card">
<div class="season-icon">❄️</div>
<div class="season-de">der Winter</div>
<div class="season-en">winter</div>
</div>
</div>
<!-- Hobbies -->
<div class="cat-label">🎯 Hobbies — Kapitel 2</div>
<div class="hobby-grid">
<div class="hobby-pill"><span>🗨️</span><span class="hobby-de">chatten</span><span class="hobby-en">to
chat</span></div>
<div class="hobby-pill"><span>📷</span><span class="hobby-de">fotografieren</span><span class="hobby-en">to
photograph</span></div>
<div class="hobby-pill"><span>🏃</span><span class="hobby-de">joggen</span><span class="hobby-en">to jog</span>
</div>
<div class="hobby-pill"><span>🍳</span><span class="hobby-de">kochen</span><span class="hobby-en">to cook</span>
</div>
<div class="hobby-pill"><span>✈️</span><span class="hobby-de">reisen</span><span class="hobby-en">to
travel</span></div>
<div class="hobby-pill"><span>🏊</span><span class="hobby-de">schwimmen</span><span class="hobby-en">to
swim</span></div>
<div class="hobby-pill"><span>🎵</span><span class="hobby-de">singen</span><span class="hobby-en">to sing</span>
</div>
<div class="hobby-pill"><span>💃</span><span class="hobby-de">tanzen</span><span class="hobby-en">to
dance</span></div>
<div class="hobby-pill"><span>📚</span><span class="hobby-de">lesen</span><span class="hobby-en">to read</span>
</div>
<div class="hobby-pill"><span>🎮</span><span class="hobby-de">spielen</span><span class="hobby-en">to
play</span></div>
<div class="hobby-pill"><span>🎬</span><span class="hobby-de">Kino</span><span class="hobby-en">movie
theater</span></div>
<div class="hobby-pill"><span>🎨</span><span class="hobby-de">malen</span><span class="hobby-en">to paint</span>
</div>
</div>
<!-- Jobs -->
<div class="cat-label">💼 Jobs & Professions — Kapitel 2</div>
<div class="jobs-grid">
<div class="job-card">
<div class="job-de">der Arzt</div>
<div class="job-de-f">die Ärztin</div>
<div class="job-en">physician</div>
</div>
<div class="job-card">
<div class="job-de">der Architekt</div>
<div class="job-de-f">die Architektin</div>
<div class="job-en">architect</div>
</div>
<div class="job-card">
<div class="job-de">der Ingenieur</div>
<div class="job-de-f">die Ingenieurin</div>
<div class="job-en">engineer</div>
</div>
<div class="job-card">
<div class="job-de">der Journalist</div>
<div class="job-de-f">die Journalistin</div>
<div class="job-en">journalist</div>
</div>
<div class="job-card">
<div class="job-de">der Professor</div>
<div class="job-de-f">die Professorin</div>
<div class="job-en">professor</div>
</div>
<div class="job-card">
<div class="job-de">der Taxifahrer</div>
<div class="job-de-f"></div>
<div class="job-en">taxi driver</div>
</div>
<div class="job-card">
<div class="job-de">der Techniker</div>
<div class="job-de-f"></div>
<div class="job-en">technician</div>
</div>
<div class="job-card">
<div class="job-de">der Boxer</div>
<div class="job-de-f"></div>
<div class="job-en">boxer</div>
</div>
<div class="job-card">
<div class="job-de">der DJ</div>
<div class="job-de-f"></div>
<div class="job-en">DJ</div>
</div>
<div class="job-card">
<div class="job-de">der Fotograf</div>
<div class="job-de-f">die Fotografin</div>
<div class="job-en">photographer</div>
</div>
<div class="job-card">
<div class="job-de">der Hausmeister</div>
<div class="job-de-f"></div>
<div class="job-en">caretaker</div>
</div>
<div class="job-card">
<div class="job-de">der Jurist</div>
<div class="job-de-f">die Juristin</div>
<div class="job-en">attorney</div>
</div>
<div class="job-card">
<div class="job-de">der Lehrer</div>
<div class="job-de-f">die Lehrerin</div>
<div class="job-en">teacher</div>
</div>
<div class="job-card">
<div class="job-de">der Programmierer</div>
<div class="job-de-f"></div>
<div class="job-en">programmer</div>
</div>
<div class="job-card">
<div class="job-de">der Elektriker</div>
<div class="job-de-f"></div>
<div class="job-en">electrician</div>
</div>
<div class="job-card">
<div class="job-de">der Student</div>
<div class="job-de-f">die Studentin</div>
<div class="job-en">student</div>
</div>
</div>
<div class="info-box">
<strong>Was sind Sie von Beruf?</strong> — What is your profession?<br>
<strong>Ich bin Arzt / Ich bin Lehrerin.</strong> — I am a doctor / I am a teacher.
</div>
<!-- Key Verbs -->
<div class="cat-label">🔤 Key Verbs — Both Chapters</div>
<div class="vocab-list">
<div class="vocab-item"><span class="vocab-de">heißen</span><span class="vocab-en">to be named (Ich
heiße...)</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">kommen</span><span class="vocab-en">to come from</span><span
class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">wohnen</span><span class="vocab-en">to live (in a
place)</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">sprechen</span><span class="vocab-en">to speak</span><span
class="vocab-badge high">⭐ High-Freq</span></div>
<div class="vocab-item"><span class="vocab-de">lernen</span><span class="vocab-en">to learn</span><span
class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">verstehen</span><span class="vocab-en">to understand</span><span
class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">buchstabieren</span><span class="vocab-en">to spell</span><span
class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">sein (ich bin)</span><span class="vocab-en">to be</span><span
class="vocab-badge high">⭐ High-Freq</span></div>
<div class="vocab-item"><span class="vocab-de">haben (er hat)</span><span class="vocab-en">to have</span><span
class="vocab-badge high">⭐ High-Freq</span></div>
<div class="vocab-item"><span class="vocab-de">arbeiten</span><span class="vocab-en">to work</span><span
class="vocab-badge kap2">Kap 2</span></div>
<div class="vocab-item"><span class="vocab-de">studieren</span><span class="vocab-en">to study</span><span
class="vocab-badge kap2">Kap 2</span></div>
<div class="vocab-item"><span class="vocab-de">machen</span><span class="vocab-en">to do, to make</span><span
class="vocab-badge kap2">Kap 2</span></div>
<div class="vocab-item"><span class="vocab-de">fahren (er fährt)</span><span class="vocab-en">to
drive</span><span class="vocab-badge kap2">Kap 2</span></div>
<div class="vocab-item"><span class="vocab-de">geben (es gibt)</span><span class="vocab-en">to give / there
is</span><span class="vocab-badge kap2">Kap 2</span></div>
<div class="vocab-item"><span class="vocab-de">lieben</span><span class="vocab-en">to love</span><span
class="vocab-badge kap2">Kap 2</span></div>
</div>
<!-- Question words -->
<div class="cat-label">❓ Question Words</div>
<div class="vocab-list">
<div class="vocab-item"><span class="vocab-de">Wie?</span><span class="vocab-en">How?</span><span
class="vocab-badge high"></span></div>
<div class="vocab-item"><span class="vocab-de">Was?</span><span class="vocab-en">What?</span><span
class="vocab-badge high"></span></div>
<div class="vocab-item"><span class="vocab-de">Wer?</span><span class="vocab-en">Who?</span><span
class="vocab-badge high"></span></div>
<div class="vocab-item"><span class="vocab-de">Wo?</span><span class="vocab-en">Where?</span><span
class="vocab-badge high"></span></div>
<div class="vocab-item"><span class="vocab-de">Woher?</span><span class="vocab-en">From where?</span><span
class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">Wann?</span><span class="vocab-en">When?</span><span
class="vocab-badge kap2">Kap 2</span></div>
<div class="vocab-item"><span class="vocab-de">Welche/r/s?</span><span class="vocab-en">Which?</span><span
class="vocab-badge kap1">Kap 1</span></div>
</div>
<!-- Countries & Languages -->
<div class="cat-label">🌍 Countries & Languages — Kapitel 1</div>
<div class="vocab-list">
<div class="vocab-item"><span class="vocab-de">Deutschland → Deutsch</span><span class="vocab-en">Germany →
German</span><span class="vocab-badge high"></span></div>
<div class="vocab-item"><span class="vocab-de">Österreich → Deutsch</span><span class="vocab-en">Austria →
German</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">die Schweiz → Deutsch/Rätoromanisch</span><span
class="vocab-en">Switzerland → German/Romansh</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">Frankreich → Französisch</span><span class="vocab-en">France →
French</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">Spanien → Spanisch</span><span class="vocab-en">Spain →
Spanish</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">Italien → Italienisch</span><span class="vocab-en">Italy →
Italian</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">Russland → Russisch</span><span class="vocab-en">Russia →
Russian</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">Japan → Japanisch</span><span class="vocab-en">Japan →
Japanese</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">China → Chinesisch</span><span class="vocab-en">China →
Chinese</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">die USA → Englisch</span><span class="vocab-en">USA →
English</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">Großbritannien → Englisch</span><span class="vocab-en">Great
Britain → English</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">Polen → Polnisch</span><span class="vocab-en">Poland →
Polish</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">die Türkei → Türkisch</span><span class="vocab-en">Turkey →
Turkish</span><span class="vocab-badge kap1">Kap 1</span></div>
<div class="vocab-item"><span class="vocab-de">Schweden → Schwedisch</span><span class="vocab-en">Sweden →
Swedish</span><span class="vocab-badge kap1">Kap 1</span></div>
</div>
<!-- Vocab browser -->
<div class="cat-label">🔍 Full Vocabulary Search</div>
<input class="vocab-search" id="vocabSearch" placeholder="Search German or English..." oninput="searchVocab()">
<div class="vocab-filters">
<button class="filter-btn active" onclick="filterVocab('all',this)">All</button>
<button class="filter-btn" onclick="filterVocab('1',this)">Kapitel 1</button>
<button class="filter-btn" onclick="filterVocab('2',this)">Kapitel 2</button>
<button class="filter-btn" onclick="filterVocab('high',this)">⭐ High-Freq</button>
</div>
<div class="vocab-list" id="vocabBrowser"></div>
</div>
<!-- ═══════════ PRACTICE (Repeat Mistakes) ═══════════ -->
<div id="sec-practice" class="practice-section hidden">
<div class="section-title">🔄 Practice — Repeat Mistakes</div>
<div class="section-sub">Words you got wrong appear 3× each. Keep going until zero mistakes!</div>
<div id="practiceEmpty" class="practice-empty">
<div class="empty-icon"></div>
<div class="empty-title">No Mistakes Yet!</div>
<div class="empty-sub">Go take the Quiz first. Any words you get wrong will appear here for extra practice.
</div>
</div>
<div id="practiceActive" class="hidden">
<div class="quiz-timer" id="practiceTimerDisplay"><span id="practiceTimer">00:00</span></div>
<div class="quiz-status-bar">
<div class="status-item">📋 Remaining: <span class="status-val" id="practiceRemaining">0</span></div>
<div class="status-item">✏️ Attempted: <span class="status-val" id="practiceAttempted">0</span></div>
<div class="status-item mistakes">❌ Mistakes: <span class="status-val" id="practiceMistakes">0</span></div>
<div class="status-item correct-stat">✅ Correct: <span class="status-val" id="practiceCorrectCount">0</span>
</div>
</div>
<div class="progress-bar">
<div class="progress-fill" id="practiceProgress" style="width:0%"></div>
</div>
<div class="card-counter" id="practiceCounter">Question 1 of ?</div>
<div class="quiz-card" id="practiceCard">
<div class="quiz-question" id="practiceQ">Loading...</div>
<div class="quiz-context" id="practiceCtx"></div>
<div class="quiz-options" id="practiceOpts"></div>
<div class="feedback-msg" id="practiceFeedback"></div>
<div class="memory-tip" id="practiceMemoryTip"></div>
</div>
<div class="card-controls" style="margin-top:16px">
<button class="btn primary" id="practiceNextBtn" onclick="nextPractice()" style="display:none">Next Question
</button>
<button class="btn" onclick="resetPractice()">↺ Restart Practice</button>
</div>
</div>
<div id="practiceResult" class="result-screen hidden">
<div class="result-emoji" id="practiceResEmoji">🎉</div>
<div class="result-title" id="practiceResTitle">Practice Complete!</div>
<div class="result-sub" id="practiceResMsg">You mastered all mistaken words!</div>
<div class="result-detail-grid" id="practiceResultStats"></div>
<div class="wrong-words-list" id="practiceWrongList"></div>
<div class="card-controls" style="margin-top:20px">
<button class="btn primary" onclick="resetPractice()">🔄 Try Again</button>
<button class="btn" onclick="showSection('quiz')">🧠 Back to Quiz</button>
</div>
</div>
</div>
<!-- ═══════════ HARDCORE PRACTICE ═══════════ -->
<div id="sec-hardcore" class="practice-section hidden">
<div class="section-title">🔥 Hardcore Practice — Level Test</div>
<div class="section-sub">All levels unlocked. Pick any level and master every word!</div>
<!-- Level Picker -->
<div class="hc-level-picker" id="hcLevelPicker">
<button class="hc-level-btn active" onclick="selectHCLevel(1,this)">Level 1</button>
<button class="hc-level-btn" onclick="selectHCLevel(2,this)">Level 2</button>
<button class="hc-level-btn" onclick="selectHCLevel(3,this)">Level 3</button>
<button class="hc-level-btn" onclick="selectHCLevel(4,this)">Level 4</button>
<button class="hc-level-btn" onclick="selectHCLevel(5,this)">Level 5</button>
<button class="hc-level-btn" onclick="selectHCLevel(6,this)">Level 6</button>
</div>
<div id="hardcoreActive">
<div class="quiz-timer" id="hardcoreTimerDisplay"><span id="hardcoreTimer">00:00</span></div>
<div class="quiz-status-bar">
<div class="status-item">📋 Remaining: <span class="status-val" id="hardcoreRemaining">0</span></div>
<div class="status-item">✏️ Attempted: <span class="status-val" id="hardcoreAttempted">0</span></div>
<div class="status-item mistakes">❌ Mistakes: <span class="status-val" id="hardcoreMistakeCount">0</span>
</div>
<div class="status-item correct-stat">✅ Correct: <span class="status-val" id="hardcoreCorrectCount">0</span>
</div>
</div>
<div class="progress-bar">
<div class="progress-fill" id="hardcoreProgress" style="width:0%"></div>
</div>
<div class="card-counter" id="hardcoreCounter">Question 1 of ?</div>
<div class="quiz-card" id="hardcoreCard">
<div class="quiz-question" id="hardcoreQ">Loading...</div>
<div class="quiz-context" id="hardcoreCtx"></div>
<div class="quiz-options" id="hardcoreOpts"></div>
<div class="feedback-msg" id="hardcoreFeedback"></div>
<div class="memory-tip" id="hardcoreMemoryTip"></div>
</div>
<div class="card-controls" style="margin-top:16px">
<button class="btn primary" id="hardcoreNextBtn" onclick="nextHardcore()" style="display:none">Next Question
</button>
<button class="btn" onclick="confirmHCStop()">⏹ Stop</button>
<button class="btn" onclick="confirmHCRestart()">↺ Restart</button>
</div>
</div>
<div id="hardcoreResult" class="result-screen hidden">
<div class="result-emoji" id="hardcoreResEmoji">🏆</div>
<div class="result-title" id="hardcoreResTitle">Hardcore Complete!</div>
<div class="result-sub" id="hardcoreResMsg">Here are your results:</div>
<div class="result-detail-grid" id="hardcoreResultStats"></div>
<div class="wrong-words-list" id="hardcoreWrongList"></div>
<div class="card-controls" style="margin-top:20px">
<button class="btn primary" id="hcPracticeMistakesBtn" onclick="startHCMistakePractice()"
style="display:none">🔄 Practice Mistakes</button>
<button class="btn primary" onclick="resetHardcore()">🔥 Try Again</button>
<button class="btn" onclick="showSection('quiz')">🧠 Back to Quiz</button>
</div>
</div>
<!-- Practice Mistakes Sub-Section -->
<div id="hcMistakePractice" class="hidden">
<div class="section-title" style="font-size:1.2rem">🔄 Hardcore Mistakes Practice</div>
<div class="section-sub">Each wrong word appears 3×. Go until zero mistakes!</div>
<div class="quiz-timer" id="hcmpTimerDisplay"><span id="hcmpTimer">00:00</span></div>
<div class="quiz-status-bar">
<div class="status-item">📋 Remaining: <span class="status-val" id="hcmpRemaining">0</span></div>
<div class="status-item">✏️ Attempted: <span class="status-val" id="hcmpAttempted">0</span></div>
<div class="status-item mistakes">❌ Mistakes: <span class="status-val" id="hcmpMistakes">0</span></div>
<div class="status-item correct-stat">✅ Correct: <span class="status-val" id="hcmpCorrect">0</span></div>
</div>
<div class="progress-bar">
<div class="progress-fill" id="hcmpProgress" style="width:0%"></div>
</div>
<div class="card-counter" id="hcmpCounter">Round 1 — Question 1 of ?</div>
<div class="quiz-card">
<div class="quiz-question" id="hcmpQ">Loading...</div>
<div class="quiz-context" id="hcmpCtx"></div>
<div class="quiz-options" id="hcmpOpts"></div>
<div class="feedback-msg" id="hcmpFeedback"></div>
<div class="memory-tip" id="hcmpMemoryTip"></div>
</div>
<div class="card-controls" style="margin-top:16px">
<button class="btn primary" id="hcmpNextBtn" onclick="nextHCMP()" style="display:none">Next Question
</button>
<button class="btn" onclick="startHCMistakePractice()">↺ Restart Practice</button>
</div>
</div>
<!-- Practice Mistakes Result -->
<div id="hcMistakePracticeResult" class="result-screen hidden">
<div class="result-emoji" id="hcmpResEmoji">🏆</div>
<div class="result-title" id="hcmpResTitle">Mistakes Mastered!</div>
<div class="result-sub" id="hcmpResMsg">You fixed all your mistakes!</div>
<div class="result-detail-grid" id="hcmpResultStats"></div>
<div class="wrong-words-list" id="hcmpWrongList"></div>
<div class="card-controls" style="margin-top:20px">
<button class="btn primary" onclick="startHCMistakePractice()">🔄 Practice Again</button>
<button class="btn primary" onclick="resetHardcore()">🔥 Retake Test</button>
<button class="btn" onclick="showSection('quiz')">🧠 Back to Quiz</button>
</div>
</div>
</div>
<!-- ═══════════ ALL HARDCORE (Full Vocab All Levels) ═══════════ -->
<div id="sec-allhardcore" class="practice-section hidden">
<div class="section-title">💀 All Hardcore — Complete Memory Test</div>
<div class="section-sub">Every single word from ALL levels. The ultimate vocabulary challenge!</div>
<div id="allhardcoreActive">
<div class="quiz-timer" id="allhardcoreTimerDisplay"><span id="allhardcoreTimer">00:00</span></div>
<div class="quiz-status-bar">
<div class="status-item">📋 Remaining: <span class="status-val" id="allhardcoreRemaining">0</span></div>
<div class="status-item">✏️ Attempted: <span class="status-val" id="allhardcoreAttempted">0</span></div>
<div class="status-item mistakes">❌ Mistakes: <span class="status-val" id="allhardcoreMistakeCount">0</span>
</div>
<div class="status-item correct-stat">✅ Correct: <span class="status-val"
id="allhardcoreCorrectCount">0</span></div>
</div>
<div class="progress-bar">
<div class="progress-fill" id="allhardcoreProgress" style="width:0%"></div>
</div>
<div class="card-counter" id="allhardcoreCounter">Question 1 of ?</div>
<div class="quiz-card" id="allhardcoreCard">
<div class="quiz-question" id="allhardcoreQ">Loading...</div>
<div class="quiz-context" id="allhardcoreCtx"></div>
<div class="quiz-options" id="allhardcoreOpts"></div>
<div class="feedback-msg" id="allhardcoreFeedback"></div>
<div class="memory-tip" id="allhardcoreMemoryTip"></div>
</div>
<div class="card-controls" style="margin-top:16px">
<button class="btn primary" id="allhardcoreNextBtn" onclick="nextAllHardcore()" style="display:none">Next
Question →</button>
<button class="btn" onclick="confirmAHStop()">⏹ Stop</button>
<button class="btn" onclick="confirmAHRestart()">↺ Restart</button>
</div>
</div>
<div id="allhardcoreResult" class="result-screen hidden">
<div class="result-emoji" id="allhardcoreResEmoji">💀</div>
<div class="result-title" id="allhardcoreResTitle">All Hardcore Complete!</div>
<div class="result-sub" id="allhardcoreResMsg">Here are your results:</div>
<div class="result-detail-grid" id="allhardcoreResultStats"></div>
<div class="wrong-words-list" id="allhardcoreWrongList"></div>
<div class="card-controls" style="margin-top:20px">
<button class="btn primary" onclick="resetAllHardcore()">💀 Try Again</button>
<button class="btn primary" id="ahPracticeMistakesBtn" onclick="startAHMistakePractice()"
style="display:none">🔄 Practice Mistakes</button>
<button class="btn" onclick="showSection('quiz')">🧠 Back to Quiz</button>
</div>
</div>
<!-- Practice Mistakes Sub-Section -->
<div id="ahMistakePractice" class="hidden">
<div class="section-title" style="font-size:1.2rem">🔄 Practicing Your Mistakes</div>
<div class="section-sub">Each wrong word appears 3×. Keep going until zero mistakes!</div>
<div class="quiz-timer" id="ahmpTimerDisplay"><span id="ahmpTimer">00:00</span></div>
<div class="quiz-status-bar">
<div class="status-item">📋 Remaining: <span class="status-val" id="ahmpRemaining">0</span></div>
<div class="status-item">✏️ Attempted: <span class="status-val" id="ahmpAttempted">0</span></div>
<div class="status-item mistakes">❌ Mistakes: <span class="status-val" id="ahmpMistakes">0</span></div>
<div class="status-item correct-stat">✅ Correct: <span class="status-val" id="ahmpCorrect">0</span></div>
</div>
<div class="progress-bar">
<div class="progress-fill" id="ahmpProgress" style="width:0%"></div>
</div>
<div class="card-counter" id="ahmpCounter">Round 1 — Question 1 of ?</div>
<div class="quiz-card">
<div class="quiz-question" id="ahmpQ">Loading...</div>
<div class="quiz-context" id="ahmpCtx"></div>
<div class="quiz-options" id="ahmpOpts"></div>
<div class="feedback-msg" id="ahmpFeedback"></div>
<div class="memory-tip" id="ahmpMemoryTip"></div>
</div>
<div class="card-controls" style="margin-top:16px">
<button class="btn primary" id="ahmpNextBtn" onclick="nextAHMP()" style="display:none">Next Question
</button>
<button class="btn" onclick="startAHMistakePractice()">↺ Restart Practice</button>
</div>
</div>
<!-- Practice Mistakes Result -->
<div id="ahMistakePracticeResult" class="result-screen hidden">
<div class="result-emoji" id="ahmpResEmoji">🏆</div>
<div class="result-title" id="ahmpResTitle">Mistakes Mastered!</div>
<div class="result-sub" id="ahmpResMsg">You fixed all your mistakes!</div>
<div class="result-detail-grid" id="ahmpResultStats"></div>
<div class="wrong-words-list" id="ahmpWrongList"></div>
<div class="card-controls" style="margin-top:20px">
<button class="btn primary" onclick="startAHMistakePractice()">🔄 Practice Again</button>
<button class="btn primary" onclick="resetAllHardcore()">💀 Retake Full Test</button>
<button class="btn" onclick="showSection('quiz')">🧠 Back to Quiz</button>
</div>
</div>
</div>
</main>
<!-- Developer Footer -->
<div class="developer-footer">
Developed by <a href="https://instagram.com/abdullahtarar.3" target="_blank" rel="noopener noreferrer">Abdullah
Tarar</a>
</div>
<!-- Confirmation Modal (shared) -->
<div class="confirm-overlay hidden" id="confirmOverlay">
<div class="confirm-box">
<div class="cbox-emoji" id="confirmEmoji">⚠️</div>
<div class="cbox-title" id="confirmTitle">Are you sure?</div>
<div class="cbox-msg" id="confirmMsg">This action cannot be undone.</div>
<div class="confirm-btns">
<button class="btn primary" id="confirmYesBtn" onclick="confirmYes()">Yes, proceed</button>
<button class="btn" onclick="confirmNo()">Cancel</button>
</div>
</div>
</div>
<div class="confetti-wrap" id="confettiWrap"></div>
<script>
// ═══════════════════════════════════════════════════════════
// DATA — Comprehensive vocab from Chapters 1 & 2
// ═══════════════════════════════════════════════════════════
const vocab = [
// ════════════════════════════════════════════════════════════
// LEVEL 1: GREETINGS & BASICS (Greeting pairs & essential politeness)
// ════════════════════════════════════════════════════════════
{ de: "Hallo!", en: "Hello!", ex: "", ch: 1, high: true, level: 1, category: "greetings", memory: "Start of conversation", gender: "-" },
{ de: "Tschüs!", en: "Bye!", ex: "", ch: 1, high: true, level: 1, category: "greetings", memory: "Informal goodbye", gender: "-" },
{ de: "Guten Morgen!", en: "Good morning!", ex: "", ch: 1, high: true, level: 1, category: "greetings", memory: "Morning = Morgen", gender: "-" },
{ de: "Guten Tag!", en: "Good day! Hello!", ex: "", ch: 1, high: true, level: 1, category: "greetings", memory: "Daytime greeting", gender: "-" },
{ de: "Guten Abend!", en: "Good evening!", ex: "", ch: 1, high: true, level: 1, category: "greetings", memory: "Evening = Abend", gender: "-" },
{ de: "Gute Nacht!", en: "Good night!", ex: "", ch: 1, high: true, level: 1, category: "greetings", memory: "Before sleep", gender: "-" },
{ de: "Auf Wiedersehen!", en: "Goodbye!", ex: "", ch: 1, high: true, level: 1, category: "greetings", memory: "Formal: Wiedersehen=re-seeing", gender: "-" },
{ de: "Bis bald!", en: "See you soon!", ex: "", ch: 1, high: true, level: 1, category: "greetings", memory: "bald=soon", gender: "-" },
{ de: "danke", en: "thank you", ex: "Danke, gut!", ch: 1, high: true, level: 1, category: "politeness", memory: "Dance=Danke (memory trick)", gender: "-" },
{ de: "bitte", en: "please", ex: "Bitte langsam sprechen.", ch: 1, high: true, level: 1, category: "politeness", memory: "Bitte is basic politeness", gender: "-" },
{ de: "sehr", en: "very", ex: "Sehr gut!", ch: 1, high: true, level: 1, category: "politeness", memory: "sehr=so/very much", gender: "-" },
{ de: "gut", en: "good", ex: "Es geht mir gut.", ch: 1, high: true, level: 1, category: "politeness", memory: "Good mood & health", gender: "-" },
{ de: "Wie geht's?", en: "How's it going?", ex: "", ch: 1, high: true, level: 1, category: "greetings", memory: "geht's = how s it going?", gender: "-" },
{ de: "ein bisschen", en: "a little", ex: "", ch: 1, high: true, level: 1, category: "descriptions", memory: "little piece (bisschen)", gender: "-" },
{ de: "und", en: "and", ex: "", ch: 1, high: true, level: 1, category: "connectors", memory: "Connect ideas", gender: "-" },
{ de: "auch", en: "also", ex: "", ch: 1, high: true, level: 1, category: "connectors", memory: "auch=also/too", gender: "-" },
{ de: "nicht", en: "not", ex: "Ich verstehe das nicht.", ch: 1, high: true, level: 1, category: "negation", memory: "NICHT = nope!", gender: "-" },
{ de: "ich", en: "I", ex: "", ch: 1, high: true, level: 1, category: "pronouns", memory: "ich=I/me", gender: "-" },
{ de: "es", en: "it", ex: "Es geht mir gut.", ch: 1, high: true, level: 1, category: "pronouns", memory: "es=it (neutral)", gender: "-" },
{ de: "du", en: "you (informal)", ex: "", ch: 1, high: true, level: 1, category: "pronouns", memory: "du=you,casual", gender: "-" },
{ de: "Sie", en: "you (formal)", ex: "Wie heißen Sie?", ch: 1, high: true, level: 1, category: "pronouns", memory: "Sie=formal you (capital S)", gender: "-" },
{ de: "Wie bitte?", en: "Come again? / Pardon?", ex: "", ch: 1, high: true, level: 1, category: "politeness", memory: "Politely ask to repeat", gender: "-" },
{ de: "Entschuldigung", en: "excuse me / apology", ex: "", ch: 1, high: true, level: 1, category: "politeness", memory: "entschuldigen=apologize", gender: "f" },
{ de: "langsam", en: "slow(ly)", ex: "Bitte sprechen Sie langsam.", ch: 1, high: true, level: 1, category: "descriptions", memory: "lang=long, slow rhythm", gender: "-" },
{ de: "noch einmal", en: "once more", ex: "", ch: 1, high: false, level: 1, category: "phrases", memory: "More practice requested", gender: "-" },
// ════════════════════════════════════════════════════════════
// LEVEL 2: IDENTITY & ORIGINS (Names, where from, languages)
// ════════════════════════════════════════════════════════════
{ de: "heißen", en: "to be named", ex: "Ich heiße Anna.", ch: 1, high: true, level: 2, category: "verbs-core", memory: "heiß=hot > name is HOT topic", gender: "-" },
{ de: "der Name", en: "name", ex: "", ch: 1, high: true, level: 2, category: "nouns-personal", memory: "Name=Name (cognate)", gender: "m" },
{ de: "kommen", en: "to come (from)", ex: "Woher kommst du?", ch: 1, high: true, level: 2, category: "verbs-core", memory: "kommen=come", gender: "-" },
{ de: "aus", en: "from (origin)", ex: "Er kommt aus Italien.", ch: 1, high: true, level: 2, category: "prepositions", memory: "aus=aus-outside,from", gender: "-" },
{ de: "wohnen", en: "to live (in a place)", ex: "Ich wohne in Berlin.", ch: 1, high: true, level: 2, category: "verbs-core", memory: "Wohn=dwelling, home", gender: "-" },
{ de: "in", en: "in", ex: "Er lebt in Frankfurt.", ch: 1, high: true, level: 2, category: "prepositions", memory: "in=inside", gender: "-" },
{ de: "sein", en: "to be", ex: "Ich bin Gregor.", ch: 1, high: true, level: 2, category: "verbs-core", memory: "sein=to be (irregular)", gender: "-" },
{ de: "bin", en: "am (1st person of sein)", ex: "Ich bin Gregor.", ch: 1, high: true, level: 2, category: "verbs-core", memory: "I bin=I am", gender: "-" },
{ de: "ist", en: "is (3rd person of sein)", ex: "Das ist Julia.", ch: 1, high: true, level: 2, category: "verbs-core", memory: "is/ist sounds similar", gender: "-" },
{ de: "bist", en: "are (you inf.)", ex: "Du bist Anna.", ch: 1, high: true, level: 2, category: "verbs-core", memory: "bist=bijection with are", gender: "-" },
{ de: "Deutschland", en: "Germany", ex: "", ch: 1, high: true, level: 2, category: "countries", memory: "Deutschland=Deutsch+land", gender: "n" },
{ de: "Deutsch", en: "German (language)", ex: "", ch: 1, high: true, level: 2, category: "languages", memory: "Deutsch=language name", gender: "-" },
{ de: "Österreich", en: "Austria", ex: "", ch: 1, high: true, level: 2, category: "countries", memory: "Öster=eastern, Reich=realm", gender: "n" },
{ de: "die Schweiz", en: "Switzerland", ex: "", ch: 1, high: true, level: 2, category: "countries", memory: "Schweiz sounds like Swiss", gender: "f" },
{ de: "Frankreich", en: "France", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "Frank's Reich (kingdom)", gender: "n" },
{ de: "Spanien", en: "Spain", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "Span-ien = Spain", gender: "n" },
{ de: "Italien", en: "Italy", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "Itali-en = Italy", gender: "n" },
{ de: "Großbritannien", en: "Great Britain", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "Groß=Great, Britan-nien", gender: "n" },
{ de: "Russland", en: "Russia", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "Russ=Russian/Rus", gender: "n" },
{ de: "Japan", en: "Japan", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "Japan=Japan", gender: "n" },
{ de: "China", en: "China", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "China=China", gender: "n" },
{ de: "Polen", en: "Poland", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "Pol-en = Poland", gender: "n" },
{ de: "die Türkei", en: "Turkey", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "Türk=Turkish/Turk", gender: "f" },
{ de: "Schweden", en: "Sweden", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "Swede-n = Sweden", gender: "n" },
{ de: "die Ukraine", en: "Ukraine", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "Ukraine=Ukraine", gender: "f" },
{ de: "die USA", en: "USA", ex: "", ch: 1, high: false, level: 2, category: "countries", memory: "USA=USA (plural)", gender: "f" },
{ de: "Englisch", en: "English", ex: "", ch: 1, high: true, level: 2, category: "languages", memory: "English=Englisch", gender: "-" },
{ de: "Französisch", en: "French", ex: "", ch: 1, high: false, level: 2, category: "languages", memory: "Frankreich → Französisch", gender: "-" },
{ de: "Spanisch", en: "Spanish", ex: "", ch: 1, high: false, level: 2, category: "languages", memory: "Spanien → Spanisch", gender: "-" },
{ de: "Italienisch", en: "Italian", ex: "", ch: 1, high: false, level: 2, category: "languages", memory: "Italien → Italienisch", gender: "-" },
{ de: "Russisch", en: "Russian", ex: "", ch: 1, high: false, level: 2, category: "languages", memory: "Russland → Russisch", gender: "-" },
{ de: "Japanisch", en: "Japanese", ex: "", ch: 1, high: false, level: 2, category: "languages", memory: "Japan → Japanisch", gender: "-" },
{ de: "Chinesisch", en: "Chinese", ex: "", ch: 1, high: false, level: 2, category: "languages", memory: "China → Chinesisch", gender: "-" },
{ de: "Türkisch", en: "Turkish", ex: "", ch: 1, high: false, level: 2, category: "languages", memory: "Türkei → Türkisch", gender: "-" },
{ de: "Polnisch", en: "Polish", ex: "", ch: 1, high: false, level: 2, category: "languages", memory: "Polen → Polnisch", gender: "-" },
{ de: "Arabisch", en: "Arabic", ex: "", ch: 1, high: false, level: 2, category: "languages", memory: "Arab → Arabisch", gender: "-" },
{ de: "die Sprache", en: "language", ex: "Ich spreche viele Sprachen.", ch: 1, high: true, level: 2, category: "nouns-general", memory: "Sprach-e = speech", gender: "f" },
{ de: "das Land", en: "land, country", ex: "", ch: 1, high: true, level: 2, category: "nouns-general", memory: "Land = land (cognate)", gender: "n" },
{ de: "sprechen", en: "to speak", ex: "Er spricht Deutsch.", ch: 1, high: true, level: 2, category: "verbs-core", memory: "sprechen=speak", gender: "-" },
{ de: "lernen", en: "to learn", ex: "Ich lerne Deutsch.", ch: 1, high: true, level: 2, category: "verbs-core", memory: "lernen=learn", gender: "-" },
{ de: "verstehen", en: "to understand", ex: "Ich verstehe das nicht.", ch: 1, high: true, level: 2, category: "verbs-core", memory: "ver-stehen=grasp fully", gender: "-" },
{ de: "können", en: "to be able to / can", ex: "Kannst du das buchstabieren?", ch: 1, high: true, level: 2, category: "verbs-modal", memory: "können=to can/be able", gender: "-" },
{ de: "kennen", en: "to know (a person)", ex: "", ch: 1, high: true, level: 2, category: "verbs-core", memory: "kennen=know (person/thing)", gender: "-" },
{ de: "er", en: "he", ex: "", ch: 1, high: true, level: 2, category: "pronouns", memory: "Er=he (male)", gender: "-" },
{ de: "sie", en: "she / they", ex: "", ch: 1, high: true, level: 2, category: "pronouns", memory: "sie=she or they (context)", gender: "-" },
{ de: "die Person", en: "person", ex: "", ch: 1, high: false, level: 2, category: "nouns-personal", memory: "Person=person (cognate)", gender: "f" },
{ de: "die Frau", en: "woman / Mrs.", ex: "", ch: 1, high: true, level: 2, category: "nouns-titles", memory: "Frau=woman/Mrs (married)", gender: "f" },
{ de: "der Herr", en: "gentleman / Mr.", ex: "", ch: 1, high: true, level: 2, category: "nouns-titles", memory: "Herr=Mr./gentleman", gender: "m" },
// ════════════════════════════════════════════════════════════
// LEVEL 3: NUMBERS, SPELLING & INFORMATION
// ════════════════════════════════════════════════════════════
{ de: "die Zahl", en: "number", ex: "", ch: 1, high: true, level: 3, category: "nouns-general", memory: "Zahl=count, number", gender: "f" },
{ de: "der Buchstabe", en: "letter (of alphabet)", ex: "", ch: 1, high: false, level: 3, category: "nouns-general", memory: "Buchstabe=book-letter", gender: "m" },
{ de: "das Alphabet", en: "alphabet", ex: "", ch: 1, high: false, level: 3, category: "nouns-general", memory: "Alphabet=alphabet (cognate)", gender: "n" },
{ de: "buchstabieren", en: "to spell", ex: "Kannst du das buchstabieren?", ch: 1, high: false, level: 3, category: "verbs-action", memory: "spell letter-by-letter", gender: "-" },
{ de: "schreiben", en: "to write", ex: "", ch: 1, high: true, level: 3, category: "verbs-action", memory: "schreiben=write (with sh-)", gender: "-" },
{ de: "fragen", en: "to ask", ex: "", ch: 1, high: true, level: 3, category: "verbs-action", memory: "fragen=ask (question)", gender: "-" },
{ de: "hören", en: "to hear / to listen", ex: "", ch: 1, high: true, level: 3, category: "verbs-action", memory: "hören=hear/listen", gender: "-" },
{ de: "die Telefonnummer", en: "phone number", ex: "", ch: 1, high: true, level: 3, category: "nouns-info", memory: "Telefon=telephone + Nummer", gender: "f" },
{ de: "die Handynummer", en: "cell phone number", ex: "", ch: 1, high: false, level: 3, category: "nouns-info", memory: "Handy=mobile phone", gender: "f" },
{ de: "die E-Mail-Adresse", en: "e-mail address", ex: "", ch: 1, high: false, level: 3, category: "nouns-info", memory: "E-Mail=electronic mail", gender: "f" },
{ de: "die Webseite", en: "website", ex: "", ch: 1, high: false, level: 3, category: "nouns-info", memory: "Web-Seite=web page", gender: "f" },
{ de: "die Postleitzahl", en: "zip code", ex: "", ch: 1, high: false, level: 3, category: "nouns-info", memory: "Postleitz-ahl=postal code number", gender: "f" },
{ de: "die Hausnummer", en: "house number", ex: "", ch: 1, high: false, level: 3, category: "nouns-info", memory: "Haus-Nummer=house number", gender: "f" },
{ de: "notieren", en: "to note, to jot down", ex: "", ch: 1, high: false, level: 3, category: "verbs-action", memory: "notieren=note/write down", gender: "-" },
{ de: "wer?", en: "who?", ex: "", ch: 1, high: true, level: 3, category: "question-words", memory: "wer=who? (W-questions)", gender: "-" },
{ de: "wie?", en: "how?", ex: "", ch: 1, high: true, level: 3, category: "question-words", memory: "wie=how?/what way", gender: "-" },
{ de: "wo?", en: "where?", ex: "", ch: 1, high: true, level: 3, category: "question-words", memory: "wo=where? (location)", gender: "-" },
{ de: "woher?", en: "from where?", ex: "", ch: 1, high: true, level: 3, category: "question-words", memory: "woher=from where? (origin)", gender: "-" },
{ de: "was?", en: "what?", ex: "", ch: 1, high: true, level: 3, category: "question-words", memory: "was=what? (object)", gender: "-" },
{ de: "welche?", en: "which?", ex: "", ch: 1, high: true, level: 3, category: "question-words", memory: "welche=which? (selection)", gender: "-" },
{ de: "laut", en: "loud(ly)", ex: "Er spricht laut.", ch: 1, high: false, level: 3, category: "descriptions", memory: "laut=loud/aloud", gender: "-" },
{ de: "der Reiseführer", en: "travel guide", ex: "", ch: 1, high: false, level: 3, category: "nouns-objects", memory: "Reise-Führer=travel guide", gender: "m" },
{ de: "der Film", en: "film, movie", ex: "", ch: 1, high: false, level: 3, category: "nouns-media", memory: "Film=film/movie (cognate)", gender: "m" },
{ de: "wir", en: "we", ex: "", ch: 1, high: true, level: 3, category: "pronouns", memory: "wir=we (plural)", gender: "-" },
{ de: "ihr", en: "you (plural)", ex: "", ch: 1, high: true, level: 3, category: "pronouns", memory: "ihr=you all (informal)", gender: "-" },
// ════════════════════════════════════════════════════════════
// LEVEL 4: HOBBIES & ACTIVITIES
// ════════════════════════════════════════════════════════════
{ de: "tanzen", en: "to dance", ex: "", ch: 2, high: true, level: 4, category: "verbs-hobbies", memory: "tanzen=dance (trance-like)", gender: "-" },
{ de: "singen", en: "to sing", ex: "", ch: 2, high: true, level: 4, category: "verbs-hobbies", memory: "singen=sing", gender: "-" },
{ de: "spielen", en: "to play", ex: "", ch: 2, high: true, level: 4, category: "verbs-hobbies", memory: "spielen=play/game", gender: "-" },
{ de: "lesen", en: "to read", ex: "Er liest viel.", ch: 2, high: true, level: 4, category: "verbs-hobbies", memory: "lesen=read (light/letters)", gender: "-" },
{ de: "kochen", en: "to cook", ex: "", ch: 2, high: true, level: 4, category: "verbs-hobbies", memory: "kochen=cook/boil", gender: "-" },
{ de: "reisen", en: "to travel", ex: "", ch: 2, high: true, level: 4, category: "verbs-hobbies", memory: "reisen=travel/journey", gender: "-" },
{ de: "schwimmen", en: "to swim", ex: "", ch: 2, high: true, level: 4, category: "verbs-hobbies", memory: "schwimmen=swim", gender: "-" },
{ de: "joggen", en: "to jog", ex: "", ch: 2, high: false, level: 4, category: "verbs-hobbies", memory: "joggen=jog (English loanword)", gender: "-" },
{ de: "fotografieren", en: "to photograph", ex: "", ch: 2, high: false, level: 4, category: "verbs-hobbies", memory: "fotografieren=photograph/photo", gender: "-" },
{ de: "chatten", en: "to chat", ex: "", ch: 2, high: false, level: 4, category: "verbs-hobbies", memory: "chatten=chat (English origin)", gender: "-" },
{ de: "malen", en: "to draw, to paint", ex: "", ch: 2, high: false, level: 4, category: "verbs-hobbies", memory: "malen=paint/color", gender: "-" },
{ de: "lieben", en: "to love", ex: "Ich liebe Bücher.", ch: 2, high: true, level: 4, category: "verbs-emotions", memory: "lieben=love (lieb-)", gender: "-" },
{ de: "gern", en: "like to (gladly)", ex: "Hörst du gern Musik?", ch: 2, high: true, level: 4, category: "adverbs", memory: "gern=gladly/with pleasure", gender: "-" },
{ de: "das Hobby", en: "hobby", ex: "", ch: 2, high: true, level: 4, category: "nouns-activities", memory: "Hobby=hobby (English)", gender: "n" },
{ de: "das Kino", en: "movie theater", ex: "Ich gehe ins Kino.", ch: 2, high: true, level: 4, category: "nouns-places", memory: "Kino=cinema/movie house", gender: "n" },
{ de: "das Buch", en: "book", ex: "", ch: 2, high: true, level: 4, category: "nouns-objects", memory: "Buch=book", gender: "n" },
{ de: "die Musik", en: "music", ex: "", ch: 2, high: true, level: 4, category: "nouns-activities", memory: "Musik=music (cognate)", gender: "f" },
{ de: "die Leute", en: "people", ex: "", ch: 2, high: true, level: 4, category: "nouns-people", memory: "Leute=folks/people", gender: "" },
{ de: "die Stadt", en: "city", ex: "", ch: 2, high: true, level: 4, category: "nouns-places", memory: "Stadt=city/town", gender: "f" },
{ de: "der Fußball", en: "soccer ball / soccer", ex: "", ch: 2, high: false, level: 4, category: "nouns-sports", memory: "Fuß-ball=foot-ball", gender: "m" },
{ de: "das Foto", en: "photo", ex: "", ch: 2, high: false, level: 4, category: "nouns-objects", memory: "Foto=photo (short)", gender: "n" },
{ de: "die Spaghetti", en: "spaghetti", ex: "", ch: 2, high: false, level: 4, category: "nouns-food", memory: "Spaghetti=spaghetti (Italian)", gender: "" },
{ de: "das Wochenende", en: "weekend", ex: "", ch: 2, high: true, level: 4, category: "nouns-time", memory: "Wochen-ende=week end", gender: "n" },
{ de: "super", en: "super, awesome", ex: "", ch: 2, high: false, level: 4, category: "descriptions", memory: "Super=super/great", gender: "-" },
{ de: "lustig", en: "funny", ex: "", ch: 2, high: false, level: 4, category: "descriptions", memory: "Lust=joy/fun > lustig", gender: "-" },
{ de: "beliebt", en: "popular", ex: "", ch: 2, high: false, level: 4, category: "descriptions", memory: "beliebt=beloved/popular", gender: "-" },
{ de: "gehen", en: "to go", ex: "Ich gehe ins Kino.", ch: 2, high: true, level: 4, category: "verbs-movement", memory: "gehen=to go/walk", gender: "-" },
{ de: "ja", en: "yes", ex: "Ja, sehr gern.", ch: 2, high: true, level: 4, category: "responses", memory: "Ja=Yes (affirmative)", gender: "-" },
{ de: "nein", en: "no", ex: "", ch: 2, high: true, level: 4, category: "responses", memory: "Nein=No (negative)", gender: "-" },
{ de: "aber", en: "but", ex: "", ch: 2, high: true, level: 4, category: "connectors", memory: "Aber=but (contrast)", gender: "-" },
{ de: "oder", en: "or", ex: "", ch: 2, high: true, level: 4, category: "connectors", memory: "Oder=or (choice)", gender: "-" },
{ de: "mit", en: "with", ex: "", ch: 2, high: true, level: 4, category: "prepositions", memory: "Mit=with (company)", gender: "-" },
{ de: "von", en: "of / from", ex: "Das Hobby von Ben.", ch: 2, high: true, level: 4, category: "prepositions", memory: "Von=of/from (origin)", gender: "-" },
{ de: "nach", en: "to (direction)", ex: "Ich reise nach Paris.", ch: 2, high: true, level: 4, category: "prepositions", memory: "Nach=to (destination)", gender: "-" },
// ════════════════════════════════════════════════════════════
// LEVEL 5: DAYS, MONTHS & PLANNING
// ════════════════════════════════════════════════════════════
{ de: "der Montag", en: "Monday", ex: "", ch: 2, high: true, level: 5, category: "days", memory: "Montag=Moon-day (Monday)", gender: "m" },
{ de: "der Dienstag", en: "Tuesday", ex: "", ch: 2, high: true, level: 5, category: "days", memory: "Dienstag=service day", gender: "m" },
{ de: "der Mittwoch", en: "Wednesday", ex: "", ch: 2, high: true, level: 5, category: "days", memory: "Mitte=middle of week", gender: "m" },
{ de: "der Donnerstag", en: "Thursday", ex: "", ch: 2, high: true, level: 5, category: "days", memory: "Donner=thunder (Thor's day)", gender: "m" },
{ de: "der Freitag", en: "Friday", ex: "", ch: 2, high: true, level: 5, category: "days", memory: "Frei=free (day off idea)", gender: "m" },
{ de: "der Samstag", en: "Saturday", ex: "", ch: 2, high: true, level: 5, category: "days", memory: "Samstag (Jewish Sabbath origin)", gender: "m" },
{ de: "der Sonntag", en: "Sunday", ex: "", ch: 2, high: true, level: 5, category: "days", memory: "Sonne=sun (Sunday)", gender: "m" },
{ de: "der Januar", en: "January", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "Januar=January (Janus)", gender: "m" },
{ de: "der Februar", en: "February", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "Februar=February", gender: "m" },
{ de: "der März", en: "March", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "März=March (Mars), ä=ae", gender: "m" },
{ de: "der April", en: "April", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "April=April (cognate)", gender: "m" },
{ de: "der Mai", en: "May", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "Mai=May (cognate)", gender: "m" },
{ de: "der Juni", en: "June", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "Juni=June (Juno)", gender: "m" },
{ de: "der Juli", en: "July", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "Juli=July (Julius)", gender: "m" },
{ de: "der August", en: "August", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "August=August (Caesar)", gender: "m" },
{ de: "der September", en: "September", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "September=7th month (Latin)", gender: "m" },
{ de: "der Oktober", en: "October", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "Oktober=8th month (Latin)", gender: "m" },
{ de: "der November", en: "November", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "November=9th month (Latin)", gender: "m" },
{ de: "der Dezember", en: "December", ex: "", ch: 2, high: true, level: 5, category: "months", memory: "Dezember=10th month (Latin)", gender: "m" },
{ de: "der Frühling", en: "spring", ex: "", ch: 2, high: true, level: 5, category: "seasons", memory: "Früh=early > spring (early growth)", gender: "m" },
{ de: "der Sommer", en: "summer", ex: "", ch: 2, high: true, level: 5, category: "seasons", memory: "Sommer=summer (warm)", gender: "m" },
{ de: "der Herbst", en: "fall / autumn", ex: "", ch: 2, high: true, level: 5, category: "seasons", memory: "Herbst=harvest season", gender: "m" },
{ de: "der Winter", en: "winter", ex: "", ch: 2, high: true, level: 5, category: "seasons", memory: "Winter=winter (cold)", gender: "m" },
{ de: "die Jahreszeit", en: "season", ex: "", ch: 2, high: false, level: 5, category: "nouns-time", memory: "Jahr=year, Zeit=time", gender: "f" },
{ de: "wann?", en: "when?", ex: "", ch: 2, high: true, level: 5, category: "question-words", memory: "wann=when? (time question)", gender: "-" },
{ de: "oft", en: "often", ex: "Ich arbeite oft am Samstag.", ch: 2, high: true, level: 5, category: "adverbs", memory: "Oft=often/frequently", gender: "-" },
{ de: "leider", en: "unfortunately", ex: "Am Mittwoch geht es leider nicht.", ch: 2, high: true, level: 5, category: "adverbs", memory: "Leider=sadly/unfortunately", gender: "-" },
{ de: "bis", en: "to / until", ex: "von Montag bis Freitag", ch: 2, high: true, level: 5, category: "prepositions", memory: "Bis=until/to (endpoint)", gender: "-" },
{ de: "hier", en: "here", ex: "", ch: 2, high: true, level: 5, category: "adverbs", memory: "Hier=here (location)", gender: "-" },
{ de: "nachts", en: "at night", ex: "", ch: 2, high: false, level: 5, category: "adverbs-time", memory: "Nach+ts=during the night", gender: "-" },
{ de: "der Monat", en: "month", ex: "", ch: 2, high: true, level: 5, category: "nouns-time", memory: "Monat=month (measure)", gender: "m" },
{ de: "die Woche", en: "week", ex: "", ch: 2, high: true, level: 5, category: "nouns-time", memory: "Woche=week", gender: "f" },
{ de: "der Tag", en: "day", ex: "", ch: 2, high: true, level: 5, category: "nouns-time", memory: "Tag=day/light", gender: "m" },
{ de: "das Jahr", en: "year", ex: "", ch: 2, high: true, level: 5, category: "nouns-time", memory: "Jahr=year", gender: "n" },
{ de: "da", en: "there", ex: "Da ist eine Katze.", ch: 2, high: false, level: 5, category: "adverbs", memory: "Da=there (location)", gender: "-" },
// ════════════════════════════════════════════════════════════
// LEVEL 6: PROFESSIONS & WORK
// ════════════════════════════════════════════════════════════
{ de: "der Arzt", en: "physician (m)", ex: "Der Arzt arbeitet in der Klinik.", ch: 2, high: true, level: 6, category: "professions", memory: "Arzt=doctor (medical)", gender: "m" },
{ de: "die Ärztin", en: "physician (f)", ex: "", ch: 2, high: true, level: 6, category: "professions", memory: "Ärztin=female doctor (ä sound)", gender: "f" },
{ de: "der Lehrer", en: "teacher (m)", ex: "", ch: 2, high: true, level: 6, category: "professions", memory: "Lehrer=teacher (lehren=teach)", gender: "m" },
{ de: "die Lehrerin", en: "teacher (f)", ex: "", ch: 2, high: true, level: 6, category: "professions", memory: "Lehrerin=female teacher", gender: "f" },
{ de: "der Student", en: "student (m)", ex: "", ch: 2, high: true, level: 6, category: "professions", memory: "Student=student (studying)", gender: "m" },
{ de: "die Studentin", en: "student (f)", ex: "", ch: 2, high: true, level: 6, category: "professions", memory: "Studentin=female student", gender: "f" },
{ de: "der Architekt", en: "architect", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Architekt=architect (design)", gender: "m" },
{ de: "der Ingenieur", en: "engineer", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Ingenieur=engineer (ingeniería)", gender: "m" },
{ de: "der Techniker", en: "technician", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Techniker=technician (tech)", gender: "m" },
{ de: "der Taxifahrer", en: "taxi driver", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Taxi-Fahrer=taxi driver", gender: "m" },
{ de: "der Boxer", en: "boxer", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Boxer=boxer (sport)", gender: "m" },
{ de: "der DJ", en: "DJ", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "DJ=disc jockey (English)", gender: "m" },
{ de: "die Journalistin", en: "journalist (f)", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Journalistin=female journalist", gender: "f" },
{ de: "die Professorin", en: "professor (f)", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Professorin=female prof", gender: "f" },
{ de: "die Fotografin", en: "photographer (f)", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Fotografin=female photographer", gender: "f" },
{ de: "der Programmierer", en: "programmer", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Programmierer=coder/programmer", gender: "m" },
{ de: "der Elektriker", en: "electrician", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Elektriker=electrical worker", gender: "m" },
{ de: "der Hausmeister", en: "caretaker, janitor", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Haus-Meister=house master", gender: "m" },
{ de: "die Juristin", en: "attorney (f)", ex: "", ch: 2, high: false, level: 6, category: "professions", memory: "Juristin=female lawyer", gender: "f" },
{ de: "arbeiten", en: "to work", ex: "Ich bin Techniker. Ich arbeite bei BMW.", ch: 2, high: true, level: 6, category: "verbs-work", memory: "arbeiten=to work/labor", gender: "-" },
{ de: "studieren", en: "to study", ex: "Sie studiert Medizin.", ch: 2, high: true, level: 6, category: "verbs-work", memory: "studieren=to study", gender: "-" },
{ de: "machen", en: "to do, to make", ex: "Was machst du?", ch: 2, high: true, level: 6, category: "verbs-action", memory: "machen=make/do", gender: "-" },
{ de: "haben", en: "to have", ex: "Wir haben genug Platz.", ch: 2, high: true, level: 6, category: "verbs-core", memory: "haben=to have", gender: "-" },
{ de: "fahren", en: "to drive", ex: "Er fährt Auto.", ch: 2, high: true, level: 6, category: "verbs-movement", memory: "fahren=to drive/travel", gender: "-" },
{ de: "produzieren", en: "to produce", ex: "", ch: 2, high: false, level: 6, category: "verbs-work", memory: "produzieren=to produce", gender: "-" },
{ de: "antworten", en: "to answer", ex: "", ch: 2, high: false, level: 6, category: "verbs-communication", memory: "antworten=to answer", gender: "-" },
{ de: "berichten", en: "to report", ex: "", ch: 2, high: false, level: 6, category: "verbs-communication", memory: "berichten=to report", gender: "-" },
{ de: "der Beruf", en: "occupation, profession", ex: "Was sind Sie von Beruf?", ch: 2, high: true, level: 6, category: "nouns-work", memory: "Beruf=career/job", gender: "m" },
{ de: "die Firma", en: "company, firm", ex: "Ich arbeite bei einer großen Firma.", ch: 2, high: true, level: 6, category: "nouns-work", memory: "Firma=company/firm", gender: "f" },
{ de: "die Uni", en: "university", ex: "Sie studiert an der Uni.", ch: 2, high: true, level: 6, category: "nouns-education", memory: "Uni=university (short)", gender: "f" },
{ de: "die Klinik", en: "clinic", ex: "Der Arzt arbeitet in der Klinik.", ch: 2, high: false, level: 6, category: "nouns-places", memory: "Klinik=clinic/hospital", gender: "f" },
{ de: "der Patient", en: "patient", ex: "", ch: 2, high: false, level: 6, category: "nouns-people", memory: "Patient=patient (medical)", gender: "m" },
{ de: "der Mensch", en: "person, human", ex: "", ch: 2, high: true, level: 6, category: "nouns-people", memory: "Mensch=person/human", gender: "m" },
{ de: "das Auto", en: "car", ex: "Er fährt Auto.", ch: 2, high: true, level: 6, category: "nouns-transport", memory: "Auto=automobile/car", gender: "n" },
{ de: "die Stunde", en: "hour", ex: "Ich arbeite acht Stunden.", ch: 2, high: true, level: 6, category: "nouns-time", memory: "Stunde=hour (statt=standing)", gender: "f" },
{ de: "bei", en: "at (a company)", ex: "Ich bin Techniker bei BMW.", ch: 2, high: true, level: 6, category: "prepositions", memory: "bei=at/near (location)", gender: "-" },
{ de: "für", en: "for", ex: "", ch: 2, high: true, level: 6, category: "prepositions", memory: "für=for (purpose)", gender: "-" },
{ de: "pro", en: "per", ex: "pro Jahr = per year", ch: 2, high: false, level: 6, category: "prepositions", memory: "Pro=per/for each", gender: "-" },
{ de: "von", en: "of / from", ex: "Der Chef von der Firma.", ch: 2, high: true, level: 6, category: "prepositions", memory: "von=from/of (possession)", gender: "-" },
{ de: "viel", en: "a lot / much", ex: "Ich lese viel.", ch: 2, high: true, level: 6, category: "descriptions", memory: "viel=much/lots (vowel i)", gender: "-" },
{ de: "der Arbeitsplatz", en: "workplace", ex: "Mein Arbeitsplatz ist schön.", ch: 2, high: false, level: 6, category: "nouns-work", memory: "Arbeits-Platz=work place", gender: "m" },
{ de: "die Arbeitszeit", en: "working hours", ex: "Meine Arbeitszeit ist 9-17 Uhr.", ch: 2, high: false, level: 6, category: "nouns-work", memory: "Arbeits-Zeit=work time", gender: "f" },
{ de: "der Kurs", en: "course", ex: "Ich belege einen Deutschkurs.", ch: 2, high: false, level: 6, category: "nouns-education", memory: "Kurs=course (class)", gender: "m" },
{ de: "der Kalender", en: "calendar", ex: "Der Termin ist im Kalender.", ch: 2, high: false, level: 6, category: "nouns-objects", memory: "Kalender=calendar", gender: "m" },
{ de: "das Wörterbuch", en: "dictionary", ex: "Ich nutze ein Wörterbuch.", ch: 2, high: false, level: 6, category: "nouns-objects", memory: "Wörter-Buch=word book", gender: "n" },
{ de: "das Plakat", en: "poster, placard", ex: "Das Plakat ist schön.", ch: 2, high: false, level: 6, category: "nouns-objects", memory: "Plakat=poster/announcement", gender: "n" },
{ de: "die Schule", en: "school", ex: "Ich gehe zur Schule.", ch: 2, high: true, level: 6, category: "nouns-education", memory: "Schule=school", gender: "f" },
{ de: "der Freund", en: "friend (m)", ex: "Mein Freund heißt Max.", ch: 2, high: true, level: 6, category: "nouns-people", memory: "Freund=friend (male)", gender: "m" },
{ de: "die Freundin", en: "friend / girlfriend (f)", ex: "Meine Freundin heißt Anna.", ch: 2, high: true, level: 6, category: "nouns-people", memory: "Freundin=friend (female)", gender: "f" },
{ de: "der Kollege", en: "colleague (m)", ex: "Mein Kollege ist sehr freundlich.", ch: 2, high: true, level: 6, category: "nouns-people", memory: "Kollege=colleague/coworker", gender: "m" },
{ de: "die Kollegin", en: "colleague (f)", ex: "Meine Kollegin arbeitet hier.", ch: 2, high: true, level: 6, category: "nouns-people", memory: "Kollegin=female colleague", gender: "f" },
{ de: "die Adresse", en: "address", ex: "Meine Adresse ist...", ch: 2, high: true, level: 6, category: "nouns-info", memory: "Adresse=address (cognate)", gender: "f" },
{ de: "der Vorname", en: "first name", ex: "Mein Vorname ist Peter.", ch: 2, high: true, level: 6, category: "nouns-personal", memory: "Vor-Name=first name", gender: "m" },
{ de: "der Nachname", en: "last name", ex: "Mein Nachname ist Schmidt.", ch: 2, high: true, level: 6, category: "nouns-personal", memory: "Nach-Name=last name", gender: "m" },
{ de: "das Geburtsdatum", en: "date of birth", ex: "Mein Geburtsdatum ist 1990.", ch: 2, high: false, level: 6, category: "nouns-personal", memory: "Geburts-Datum=birth date", gender: "n" },
{ de: "der Geburtsort", en: "birthplace", ex: "Mein Geburtsort ist Berlin.", ch: 2, high: false, level: 6, category: "nouns-personal", memory: "Geburts-Ort=birth place", gender: "m" },
{ de: "der Wohnort", en: "hometown / place of residence", ex: "Mein Wohnort ist München.", ch: 2, high: false, level: 6, category: "nouns-personal", memory: "Wohn-Ort=living place", gender: "m" },
{ de: "das Formular", en: "form", ex: "Füllen Sie das Formular aus.", ch: 2, high: false, level: 6, category: "nouns-objects", memory: "Formular=form/questionnaire", gender: "n" },
{ de: "das Profil", en: "profile", ex: "Sein Profil ist öffentlich.", ch: 2, high: false, level: 6, category: "nouns-objects", memory: "Profil=profile (cognate)", gender: "n" },
{ de: "willkommen", en: "welcome", ex: "Willkommen in Deutschland!", ch: 2, high: false, level: 6, category: "greetings", memory: "Willkommen=you are welcome", gender: "-" },
{ de: "männlich", en: "male, masculine", ex: "Ist er männlich oder weiblich?", ch: 2, high: false, level: 6, category: "descriptions", memory: "Mann=man > männlich", gender: "-" },
{ de: "weiblich", en: "female, feminine", ex: "Sie ist weiblich.", ch: 2, high: false, level: 6, category: "descriptions", memory: "Weib=woman > weiblich", gender: "-" },
{ de: "online", en: "online", ex: "Ich bin online.", ch: 2, high: false, level: 6, category: "descriptions", memory: "online=connected (English)", gender: "-" },
{ de: "der Kilometer", en: "kilometer", ex: "Es sind 100 Kilometer.", ch: 2, high: false, level: 6, category: "nouns-measurement", memory: "Kilometer=kilometer (metric)", gender: "m" },
{ de: "der Platz", en: "space, place", ex: "Wir haben viel Platz.", ch: 2, high: false, level: 6, category: "nouns-objects", memory: "Platz=space/place/square", gender: "m" },
// Additional items to reach comprehensive coverage
{ de: "der Anzug", en: "suit", ex: "", ch: 1, high: false, level: 2, category: "clothing", memory: "Anzug=outfit/suit", gender: "m" },
{ de: "die Autobahn", en: "highway", ex: "", ch: 1, high: true, level: 1, category: "nouns-transport", memory: "Auto-Bahn=car highway", gender: "f" },
{ de: "das Frühstück", en: "breakfast", ex: "", ch: 1, high: true, level: 1, category: "food", memory: "Früh=early, Stück=piece", gender: "n" },
{ de: "der Rucksack", en: "backpack", ex: "", ch: 1, high: false, level: 3, category: "nouns-objects", memory: "Rück-Sack=back sack", gender: "m" },
{ de: "das Würstchen", en: "sausage", ex: "", ch: 1, high: false, level: 1, category: "food", memory: "Würst(chen)=sausage", gender: "n" },
{ de: "das Butterbrot", en: "sandwich", ex: "", ch: 1, high: false, level: 1, category: "food", memory: "Butter-Brot=butter bread", gender: "n" },
{ de: "der Walzer", en: "waltz", ex: "", ch: 1, high: false, level: 4, category: "dance", memory: "Walzer=waltz (dance)", gender: "m" },
{ de: "das Restaurant", en: "restaurant", ex: "", ch: 2, high: true, level: 4, category: "nouns-places", memory: "Restaurant=restaurant (cognate)", gender: "n" },
{ de: "das Café", en: "café", ex: "", ch: 2, high: false, level: 4, category: "nouns-places", memory: "Café=café (French loanword)", gender: "n" },
{ de: "das Museum", en: "museum", ex: "", ch: 2, high: false, level: 4, category: "nouns-places", memory: "Museum=museum (cognate)", gender: "n" },
{ de: "das Schwimmbad", en: "swimming pool", ex: "", ch: 2, high: false, level: 4, category: "nouns-places", memory: "Schwimm-Bad=swim bath", gender: "n" },
{ de: "das Theater", en: "theater", ex: "", ch: 2, high: false, level: 4, category: "nouns-places", memory: "Theater=theater (cognate)", gender: "n" },
{ de: "das Fußballstadion", en: "soccer stadium", ex: "", ch: 2, high: false, level: 4, category: "nouns-places", memory: "Fußball-Stadion=soccer arena", gender: "n" },
{ de: "die Lieblingsmusik", en: "favorite music", ex: "", ch: 2, high: false, level: 6, category: "nouns-entertainment", memory: "Lieblings=favorite, Musik=music", gender: "f" },
{ de: "der Lieblingsfilm", en: "favorite film", ex: "", ch: 2, high: false, level: 6, category: "nouns-entertainment", memory: "Lieblings=favorite, Film=movie", gender: "m" },
];
// Fill-in-blank sentences
const fibData = [
{ sentence: "Wie ___ du? Ich heiße Anna.", blank: "heißt", hint: "3rd person of heißen", level: 2 },
{ sentence: "Woher ___ du? Ich komme aus Deutschland.", blank: "kommst", hint: "2nd person of kommen", level: 2 },
{ sentence: "Ich ___ in Berlin.", blank: "wohne", hint: "1st person of wohnen", level: 2 },
{ sentence: "___ du Deutsch? Ja, ich spreche Deutsch.", blank: "Sprichst", hint: "2nd person of sprechen", level: 2 },
{ sentence: "Guten ___! — Guten Morgen!", blank: "Morgen", hint: "morning greeting", level: 1 },
{ sentence: "Auf ___! (Goodbye)", blank: "Wiedersehen", hint: "formal goodbye", level: 1 },
{ sentence: "Wie geht ___? — Danke, gut.", blank: "es", hint: "pronoun 'it'", level: 1 },
{ sentence: "Ich ___ gern Musik.", blank: "höre", hint: "1st person of hören", level: 4 },
{ sentence: "Was machst du am ___? — Samstag. (Saturday)", blank: "Samstag", hint: "day of the week", level: 5 },
{ sentence: "Er ___ bei BMW.", blank: "arbeitet", hint: "3rd person of arbeiten", level: 6 },
{ sentence: "Sie ___ Medizin an der Uni.", blank: "studiert", hint: "3rd person of studieren", level: 6 },
{ sentence: "Ich ___ Bücher sehr gern.", blank: "lese", hint: "1st person of lesen (irreg.)", level: 4 },
{ sentence: "Es gibt 12 ___ im Jahr.", blank: "Monate", hint: "plural of Monat", level: 5 },
{ sentence: "Der ___ ist nach dem Sommer. (autumn)", blank: "Herbst", hint: "season", level: 5 },
{ sentence: "Sie ist ___ von Beruf. (doctor, f)", blank: "Ärztin", hint: "female physician", level: 6 },
{ sentence: "Ich ___ nicht gut Englisch.", blank: "spreche", hint: "1st person of sprechen", level: 2 },
{ sentence: "___ Sie Ihren Namen bitte! (Spell)", blank: "Buchstabieren", hint: "imperative of buchstabieren", level: 3 },
{ sentence: "Wir haben am ___ frei. (Sunday)", blank: "Sonntag", hint: "last day of the week", level: 5 },
{ sentence: "Ich fahre gern ___. (car)", blank: "Auto", hint: "das ___", level: 6 },
{ sentence: "Der ___ kommt nach dem Winter. (spring)", blank: "Frühling", hint: "season", level: 5 },
];
// ═══════════════════════════════════════════════════════════
// STATE
// ═══════════════════════════════════════════════════════════
let score = 0, streak = 0;
// Level state
let unlockedLevels = [1];
let currentLevel = 1;
let levelProgress = {
1: { completed: false, score: 0 },
2: { completed: false, score: 0 },
3: { completed: false, score: 0 },
4: { completed: false, score: 0 },
5: { completed: false, score: 0 },
6: { completed: false, score: 0 }
};
// Load progress from localStorage
function loadProgress() {
const saved = localStorage.getItem('netzwerkProgress');
if (saved) {
const data = JSON.parse(saved);
unlockedLevels = data.unlockedLevels || [1];
currentLevel = data.currentLevel || 1;
levelProgress = data.levelProgress || levelProgress;
score = data.score || 0;
streak = data.streak || 0;
document.getElementById('totalScore').textContent = score;
document.getElementById('streakCount').textContent = streak;
}
}
function saveProgress() {
localStorage.setItem('netzwerkProgress', JSON.stringify({
unlockedLevels, currentLevel, levelProgress, score, streak
}));
}
function selectLevel(levelNum) {
if (!unlockedLevels.includes(levelNum)) return;
currentLevel = levelNum;
updateLevelButtons();
initCards();
initQuiz();
resetMatching();
initType();
initFib();
saveProgress();
}
function updateLevelButtons() {
for (let i = 1; i <= 6; i++) {
const btn = document.getElementById('levelBtn' + i);
if (!btn) continue;
btn.classList.remove('active', 'locked');
if (unlockedLevels.includes(i)) {
if (i === currentLevel) {
btn.classList.add('active');
}
btn.textContent = (i === 1 ? '🎯' : '⭐') + ' Level ' + i;
} else {
btn.classList.add('locked');
btn.textContent = '🔒 Level ' + i;
}
}
}
function unlockLevel(levelNum) {
if (!unlockedLevels.includes(levelNum)) {
unlockedLevels.push(levelNum);
updateLevelButtons();
saveProgress();
}
}
// Flashcard state
let cardFilter = 'all';
let filteredCards = [], cardIdx = 0, cardFlipped = false;
// Quiz state
let quizPool = [], quizIdx = 0, quizCorrect = 0, quizAnswered = false;
// Mistaken words tracking (for Practice mode)
let mistakenWords = [];
// Practice state
let practicePool = [], practiceIdx = 0, practiceCorrect = 0, practiceMistakeCount = 0, practiceAttempted = 0;
let practiceAnswered = false, practiceTimerInterval = null, practiceStartTime = null;
let practiceNewMistakes = [];
let practiceRound = 1;
// Hardcore state
let hardcorePool = [], hardcoreIdx = 0, hardcoreCorrect = 0, hardcoreMistakeCount = 0, hardcoreAttempted = 0;
let hardcoreAnswered = false, hardcoreTimerInterval = null, hardcoreStartTime = null;
let hardcoreWrongWords = [];
// All Hardcore state (ALL levels)
let ahPool = [], ahIdx = 0, ahCorrect = 0, ahMistakeCount = 0, ahAttempted = 0;
let ahAnswered = false, ahTimerInterval = null, ahStartTime = null;
let ahWrongWords = [];
// Match state
let matchLeft = [], matchRight = [], matchSelectedLeft = null, matchSelectedRight = null, matchedCount = 0;
// Type state
let typePool = [], typeIdx = 0;
// Fib state
let fibPool = [], fibIdx = 0;
// Vocab browser state
let vocabFilterVal = 'all';
// ═══════════════════════════════════════════════════════════
// UTILITY
// ═══════════════════════════════════════════════════════════
function shuffle(arr) {
const a = [...arr];
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function addScore(pts) {
score += pts;
streak++;
document.getElementById('totalScore').textContent = score;
document.getElementById('streakCount').textContent = streak;
if (streak % 5 === 0) spawnConfetti();
}
function resetStreak() { streak = 0; document.getElementById('streakCount').textContent = 0; }
function spawnConfetti() {
const wrap = document.getElementById('confettiWrap');
const colors = ['#e8c547', '#7c6fff', '#ff6b6b', '#4ecdc4', '#fff'];
for (let i = 0; i < 30; i++) {
const el = document.createElement('div');
el.className = 'confetti-piece';
el.style.left = Math.random() * 100 + 'vw';
el.style.top = '-20px';
el.style.background = colors[Math.floor(Math.random() * colors.length)];
el.style.animationDelay = Math.random() * 0.5 + 's';
el.style.animationDuration = (1 + Math.random() * 0.6) + 's';
wrap.appendChild(el);
setTimeout(() => el.remove(), 2000);
}
}
function showSection(id) {
document.querySelectorAll('.practice-section').forEach(s => s.classList.add('hidden'));
document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
document.getElementById('sec-' + id).classList.remove('hidden');
event.target.classList.add('active');
if (id === 'flashcards') initCards();
if (id === 'quiz') initQuiz();
if (id === 'matching') resetMatching();
if (id === 'typeit') initType();
if (id === 'fillin') initFib();
if (id === 'practice') initPractice();
if (id === 'hardcore') initHardcore();
if (id === 'allhardcore') initAllHardcore();
if (id === 'reference') renderVocabBrowser();
}
// ═══════════════════════════════════════════════════════════
// FLASHCARDS
// ═══════════════════════════════════════════════════════════
function getFilteredVocab() {
return vocab.filter(v => {
const correctLevel = v.level === currentLevel;
if (cardFilter === '1') return v.ch === 1 && correctLevel;
if (cardFilter === '2') return v.ch === 2 && correctLevel;
if (cardFilter === 'high') return v.high && correctLevel;
return correctLevel;
});
}
function setCardFilter(f, btn) {
cardFilter = f;
document.querySelectorAll('#sec-flashcards .filter-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
initCards();
}
function initCards() {
filteredCards = shuffle(getFilteredVocab());
cardIdx = 0;
cardFlipped = false;
showCard();
}
function showCard() {
const card = document.getElementById('mainCard');
card.classList.remove('flipped');
cardFlipped = false;
const v = filteredCards[cardIdx];
document.getElementById('cardTag').textContent = 'LEVEL ' + v.level + ' • ' + (v.category || 'vocab').toUpperCase();
document.getElementById('cardWord').textContent = v.de;
document.getElementById('cardExample').textContent = v.ex || '';
document.getElementById('cardTranslation').textContent = v.en;
document.getElementById('cardHint').innerHTML = (v.memory ? '💡 ' + v.memory + '<br>' : '') + (v.high ? '⭐ High-Frequency' : '');
const pct = ((cardIdx + 1) / filteredCards.length * 100).toFixed(0);
document.getElementById('cardProgress').style.width = pct + '%';
document.getElementById('cardCounter').textContent = `Card ${cardIdx + 1} of ${filteredCards.length}`;
}
function flipCard() {
const card = document.getElementById('mainCard');
cardFlipped = !cardFlipped;
card.classList.toggle('flipped', cardFlipped);
}
function nextCard() {
cardIdx = (cardIdx + 1) % filteredCards.length;
showCard();
}
function prevCard() {
cardIdx = (cardIdx - 1 + filteredCards.length) % filteredCards.length;
showCard();
}
function shuffleCards() {
filteredCards = shuffle(filteredCards);
cardIdx = 0;
showCard();
}
function cardKnew(knew) {
if (!cardFlipped) flipCard();
setTimeout(() => {
if (knew) { addScore(2); }
else { resetStreak(); }
nextCard();
}, 400);
}
// ═══════════════════════════════════════════════════════════
// QUIZ
// ═══════════════════════════════════════════════════════════
function initQuiz() {
quizPool = shuffle(vocab.filter(v => v.level === currentLevel)).slice(0, 20);
quizIdx = 0;
quizCorrect = 0;
quizAnswered = false;
document.getElementById('quizResult').classList.add('hidden');
document.getElementById('quizCard').style.display = '';
document.getElementById('quizNextBtn').style.display = 'none';
showQuiz();
}
function showQuiz() {
if (quizIdx >= quizPool.length) {
showQuizResult(); return;
}
quizAnswered = false;
const v = quizPool[quizIdx];
document.getElementById('quizQ').textContent = v.de;
document.getElementById('quizCtx').textContent = v.ex ? `e.g. "${v.ex}"` : (v.ch === 1 ? 'Kapitel 1' : 'Kapitel 2');
// 4 options: 1 correct + 3 wrong
let wrong = shuffle(vocab.filter(w => w.en !== v.en && w.level === currentLevel)).slice(0, 3).map(w => w.en);
let options = shuffle([v.en, ...wrong]);
const optDiv = document.getElementById('quizOpts');
optDiv.innerHTML = '';
options.forEach(opt => {
const btn = document.createElement('button');
btn.className = 'quiz-option';
btn.textContent = opt;
btn.onclick = () => selectQuizOpt(btn, opt, v.en);
optDiv.appendChild(btn);
});
const fb = document.getElementById('quizFeedback');
fb.className = 'feedback-msg';
fb.textContent = '';
document.getElementById('quizNextBtn').style.display = 'none';
const pct = (quizIdx / quizPool.length * 100).toFixed(0);
document.getElementById('quizProgress').style.width = pct + '%';
document.getElementById('quizCounter').textContent = `Question ${quizIdx + 1} of ${quizPool.length}`;
}
function selectQuizOpt(btn, chosen, correct) {
if (quizAnswered) return;
quizAnswered = true;
document.querySelectorAll('.quiz-option').forEach(b => { b.disabled = true; });
const fb = document.getElementById('quizFeedback');
if (chosen === correct) {
btn.classList.add('correct');
fb.textContent = '✓ Richtig! Correct!';
fb.className = 'feedback-msg show ok';
quizCorrect++;
addScore(5);
} else {
btn.classList.add('wrong');
fb.textContent = `✗ The correct answer is: "${correct}"`;
fb.className = 'feedback-msg show bad';
resetStreak();
// Track mistaken word for Practice mode
const mistakeWord = quizPool[quizIdx];
if (mistakeWord && !mistakenWords.find(w => w.de === mistakeWord.de)) {
mistakenWords.push(mistakeWord);
}
// RE-INSERT mistaken word 3 more times (shuffled into remaining pool)
if (mistakeWord) {
const remaining = quizPool.slice(quizIdx + 1);
for (let r = 0; r < 3; r++) {
const pos = Math.floor(Math.random() * (remaining.length + 1));
remaining.splice(pos, 0, { ...mistakeWord });
}
quizPool = [...quizPool.slice(0, quizIdx + 1), ...remaining];
}
// highlight correct
document.querySelectorAll('.quiz-option').forEach(b => {
if (b.textContent === correct) b.classList.add('correct');
});
}
document.getElementById('quizNextBtn').style.display = '';
// Update counter to reflect new pool size
document.getElementById('quizCounter').textContent = `Question ${quizIdx + 1} of ${quizPool.length}`;
quizIdx++;
}
function nextQuiz() { showQuiz(); }
function resetQuiz() { initQuiz(); }
function showQuizResult() {
document.getElementById('quizCard').style.display = 'none';
document.getElementById('quizNextBtn').style.display = 'none';
const res = document.getElementById('quizResult');
res.classList.remove('hidden');
const pct = Math.round(quizCorrect / quizPool.length * 100);
document.getElementById('quizScore').textContent = `${quizCorrect} / ${quizPool.length} (${pct}%)`;
// Check for level unlock
if (pct >= 70 && currentLevel < 6) {
unlockLevel(currentLevel + 1);
document.getElementById('quizMsg').textContent = '🎉 Level ' + (currentLevel + 1) + ' Unlocked! Great job!';
} else {
document.getElementById('quizMsg').textContent = pct >= 80 ? 'Ausgezeichnet! Excellent work!' : pct >= 50 ? 'Gut gemacht! Keep practicing!' : 'Weiter üben! Keep studying!';
}
document.getElementById('quizEmoji').textContent = pct >= 80 ? '🎉' : pct >= 70 ? '⭐' : pct >= 50 ? '💪' : '📚';
if (pct >= 70) spawnConfetti();
levelProgress[currentLevel].completed = true;
levelProgress[currentLevel].score = pct;
saveProgress();
}
// ═══════════════════════════════════════════════════════════
// MATCHING
// ═══════════════════════════════════════════════════════════
function resetMatching() {
matchSelectedLeft = null; matchSelectedRight = null; matchedCount = 0;
document.getElementById('matchDone').classList.add('hidden');
document.getElementById('matchLeft').style.display = '';
document.getElementById('matchRight').style.display = '';
const pool = shuffle(vocab.filter(v => v.level === currentLevel)).slice(0, 8);
matchLeft = pool.map(v => ({ ...v, id: v.de }));
matchRight = shuffle(pool.map(v => ({ ...v, id: v.de })));
renderMatchCol('matchLeft', matchLeft, 'left');
renderMatchCol('matchRight', matchRight, 'right');
document.getElementById('matchScore').textContent = 'Matched: 0/8';
}
function renderMatchCol(elId, items, side) {
const el = document.getElementById(elId);
el.innerHTML = '';
items.forEach(item => {
const div = document.createElement('div');
div.className = 'match-item';
div.textContent = side === 'left' ? item.de : item.en;
div.dataset.id = item.id;
div.onclick = () => selectMatch(div, side, item.id);
el.appendChild(div);
});
}
function selectMatch(el, side, id) {
if (el.classList.contains('matched')) return;
if (side === 'left') {
document.querySelectorAll('#matchLeft .match-item').forEach(b => b.classList.remove('selected'));
el.classList.add('selected');
matchSelectedLeft = id;
} else {
document.querySelectorAll('#matchRight .match-item').forEach(b => b.classList.remove('selected'));
el.classList.add('selected');
matchSelectedRight = id;
}
if (matchSelectedLeft && matchSelectedRight) {
if (matchSelectedLeft === matchSelectedRight) {
// correct
document.querySelectorAll(`.match-item[data-id="${matchSelectedLeft}"]`).forEach(el => {
el.classList.remove('selected');
el.classList.add('matched');
});
matchedCount++;
addScore(3);
document.getElementById('matchScore').textContent = `Matched: ${matchedCount}/8`;
if (matchedCount === 8) {
setTimeout(() => {
document.getElementById('matchLeft').style.display = 'none';
document.getElementById('matchRight').style.display = 'none';
document.getElementById('matchDone').classList.remove('hidden');
spawnConfetti();
}, 400);
}
} else {
// wrong flash
document.querySelectorAll('.match-item.selected').forEach(el => {
el.classList.add('wrong-flash');
setTimeout(() => { el.classList.remove('wrong-flash', 'selected'); }, 600);
});
resetStreak();
}
matchSelectedLeft = null; matchSelectedRight = null;
}
}
// ═══════════════════════════════════════════════════════════
// TYPE IT
// ═══════════════════════════════════════════════════════════
function initType() {
typePool = shuffle(vocab.filter(v => v.level === currentLevel));
typeIdx = 0;
showType();
}
function showType() {
if (typeIdx >= typePool.length) { typeIdx = 0; typePool = shuffle(vocab.filter(v => v.level === currentLevel)); }
const v = typePool[typeIdx];
document.getElementById('typeQ').textContent = v.en;
document.getElementById('typeCtx').textContent = v.ex ? `Context: "${v.ex}"` : (v.ch === 1 ? 'Kapitel 1' : 'Kapitel 2');
const inp = document.getElementById('typeInput');
inp.value = '';
inp.className = 'type-input';
inp.focus();
const fb = document.getElementById('typeFeedback');
fb.className = 'feedback-msg';
fb.textContent = '';
const pct = (typeIdx / typePool.length * 100).toFixed(0);
document.getElementById('typeProgress').style.width = pct + '%';
document.getElementById('typeCounter').textContent = `Word ${typeIdx + 1} of ${typePool.length}`;
}
function normalize(s) { return s.trim().toLowerCase().replace(/[äàá]/g, 'a').replace(/[öò]/g, 'o').replace(/[üù]/g, 'u').replace(/ß/g, 'ss'); }
function checkType() {
const v = typePool[typeIdx];
const inp = document.getElementById('typeInput');
const val = inp.value.trim();
const fb = document.getElementById('typeFeedback');
// Accept any part of the de field that matches
const correct = normalize(v.de) === normalize(val) || v.de.toLowerCase().includes(val.toLowerCase()) && val.length > 2;
// Strict: the user's answer must match the core word
const coreDE = v.de.replace(/^(der|die|das|den|dem)\s+/i, '').trim();
const isOK = normalize(val) === normalize(v.de) || normalize(val) === normalize(coreDE);
if (isOK) {
inp.className = 'type-input correct-input';
fb.textContent = `✓ Richtig! "${v.de}" = "${v.en}"`;
fb.className = 'feedback-msg show ok';
addScore(4);
setTimeout(() => { typeIdx++; showType(); }, 1200);
} else {
inp.className = 'type-input wrong-input';
fb.textContent = `✗ Answer: "${v.de}"`;
fb.className = 'feedback-msg show bad';
resetStreak();
setTimeout(() => { typeIdx++; showType(); }, 1800);
}
}
function skipType() { typeIdx++; showType(); }
function resetType() { initType(); }
// ═══════════════════════════════════════════════════════════
// FILL IN THE BLANK
// ═══════════════════════════════════════════════════════════
function initFib() {
fibPool = shuffle(fibData.filter(f => f.level === currentLevel));
fibIdx = 0;
showFib();
}
function showFib() {
if (fibIdx >= fibPool.length) { fibIdx = 0; fibPool = shuffle(fibData.filter(f => f.level === currentLevel)); }
const item = fibPool[fibIdx];
const sentenceHtml = item.sentence.replace('___', '<input class="blank-input" id="fibInput" autocomplete="off">');
document.getElementById('fibSentence').innerHTML = sentenceHtml;
const inp = document.getElementById('fibInput');
inp.addEventListener('keydown', e => { if (e.key === 'Enter') checkFib(); });
inp.focus();
const fb = document.getElementById('fibFeedback');
fb.className = 'feedback-msg';
fb.textContent = '';
const pct = (fibIdx / fibPool.length * 100).toFixed(0);
document.getElementById('fibProgress').style.width = pct + '%';
document.getElementById('fibCounter').textContent = `Sentence ${fibIdx + 1} of ${fibPool.length}`;
}
function checkFib() {
const item = fibPool[fibIdx];
const inp = document.getElementById('fibInput');
const val = inp.value.trim();
const fb = document.getElementById('fibFeedback');
if (normalize(val) === normalize(item.blank)) {
inp.className = 'blank-input correct-b';
fb.textContent = `✓ Richtig! "${item.blank}"`;
fb.className = 'feedback-msg show ok';
addScore(6);
setTimeout(() => { fibIdx++; showFib(); }, 1200);
} else {
inp.className = 'blank-input wrong-b';
fb.textContent = `✗ Answer: "${item.blank}"`;
fb.className = 'feedback-msg show bad';
resetStreak();
setTimeout(() => { fibIdx++; showFib(); }, 1800);
}
}
function skipFib() { fibIdx++; showFib(); }
function resetFib() { initFib(); }
function hintFib() {
const item = fibPool[fibIdx];
const fb = document.getElementById('fibFeedback');
fb.textContent = `💡 Hint: ${item.hint}`;
fb.className = 'feedback-msg show';
fb.style.color = 'var(--accent)';
fb.style.background = 'rgba(232,197,71,0.08)';
}
// ═══════════════════════════════════════════════════════════
// VOCAB BROWSER
// ═══════════════════════════════════════════════════════════
function filterVocab(f, btn) {
vocabFilterVal = f;
document.querySelectorAll('#sec-reference .filter-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
renderVocabBrowser();
}
function searchVocab() { renderVocabBrowser(); }
function renderVocabBrowser() {
const search = (document.getElementById('vocabSearch')?.value || '').toLowerCase();
const filtered = vocab.filter(v => {
const matchSearch = !search || v.de.toLowerCase().includes(search) || v.en.toLowerCase().includes(search);
const matchFilter = vocabFilterVal === 'all' ? true : vocabFilterVal === '1' ? v.ch === 1 : vocabFilterVal === '2' ? v.ch === 2 : v.high;
return matchSearch && matchFilter;
});
const el = document.getElementById('vocabBrowser');
if (!el) return;
el.innerHTML = filtered.length === 0 ? '<div style="color:var(--muted);font-family:DM Mono,monospace;padding:20px">No results found.</div>' :
filtered.map(v => `
<div class="vocab-item">
<span class="vocab-de">${v.de}</span>
<span class="vocab-en">${v.en}${v.ex ? `<span style="color:var(--accent2);font-style:italic"> — ${v.ex}</span>` : ''}</span>
<span class="vocab-badge ${v.high ? 'high' : v.ch === 1 ? 'kap1' : 'kap2'}">${v.high ? '⭐ High' : 'Kap ' + v.ch}</span>
</div>`).join('');
}
// ═══════════════════════════════════════════════════════════
// PRACTICE MODE (Repeat Mistakes 3×)
// ═══════════════════════════════════════════════════════════
function formatTime(seconds) {
const m = Math.floor(seconds / 60).toString().padStart(2, '0');
const s = (seconds % 60).toString().padStart(2, '0');
return m + ':' + s;
}
function startPracticeTimer() {
if (practiceTimerInterval) clearInterval(practiceTimerInterval);
practiceStartTime = Date.now();
practiceTimerInterval = setInterval(() => {
const elapsed = Math.floor((Date.now() - practiceStartTime) / 1000);
document.getElementById('practiceTimer').textContent = formatTime(elapsed);
}, 1000);
}
function stopPracticeTimer() {
if (practiceTimerInterval) { clearInterval(practiceTimerInterval); practiceTimerInterval = null; }
}
function initPractice() {
// Check if there are mistaken words
if (mistakenWords.length === 0) {
document.getElementById('practiceEmpty').classList.remove('hidden');
document.getElementById('practiceActive').classList.add('hidden');
document.getElementById('practiceResult').classList.add('hidden');
return;
}
resetPractice();
}
function resetPractice() {
if (mistakenWords.length === 0) {
initPractice();
return;
}
document.getElementById('practiceEmpty').classList.add('hidden');
document.getElementById('practiceActive').classList.remove('hidden');
document.getElementById('practiceResult').classList.add('hidden');
// Each mistaken word appears 3 times
practicePool = [];
for (let i = 0; i < 3; i++) {
practicePool = practicePool.concat([...mistakenWords]);
}
practicePool = shuffle(practicePool);
practiceIdx = 0;
practiceCorrect = 0;
practiceMistakeCount = 0;
practiceAttempted = 0;
practiceAnswered = false;
practiceNewMistakes = [];
practiceRound = 1;
updatePracticeStatus();
startPracticeTimer();
showPracticeQuestion();
}
function updatePracticeStatus() {
document.getElementById('practiceRemaining').textContent = practicePool.length - practiceIdx;
document.getElementById('practiceAttempted').textContent = practiceAttempted;
document.getElementById('practiceMistakes').textContent = practiceMistakeCount;
document.getElementById('practiceCorrectCount').textContent = practiceCorrect;
}
function showPracticeQuestion() {
if (practiceIdx >= practicePool.length) {
// Check if there were new mistakes this round
if (practiceNewMistakes.length > 0) {
// Re-queue new mistakes × 3
practicePool = [];
for (let i = 0; i < 3; i++) {
practicePool = practicePool.concat([...practiceNewMistakes]);
}
practicePool = shuffle(practicePool);
practiceNewMistakes = [];
practiceIdx = 0;
practiceRound++;
updatePracticeStatus();
showPracticeQuestion();
return;
}
showPracticeResult();
return;
}
practiceAnswered = false;
const v = practicePool[practiceIdx];
document.getElementById('practiceQ').textContent = v.de;
document.getElementById('practiceCtx').textContent = v.ex ? `e.g. "${v.ex}"` : (v.category || '').replace(/-/g, ' ');
let wrong = shuffle(vocab.filter(w => w.en !== v.en && w.level === currentLevel)).slice(0, 3).map(w => w.en);
let options = shuffle([v.en, ...wrong]);
const optDiv = document.getElementById('practiceOpts');
optDiv.innerHTML = '';
options.forEach(opt => {
const btn = document.createElement('button');
btn.className = 'quiz-option';
btn.textContent = opt;
btn.onclick = () => selectPracticeOpt(btn, opt, v.en, v);
optDiv.appendChild(btn);
});
const fb = document.getElementById('practiceFeedback');
fb.className = 'feedback-msg';
fb.textContent = '';
document.getElementById('practiceMemoryTip').className = 'memory-tip';
document.getElementById('practiceNextBtn').style.display = 'none';
const pct = (practiceIdx / practicePool.length * 100).toFixed(0);
document.getElementById('practiceProgress').style.width = pct + '%';
document.getElementById('practiceCounter').textContent = `Round ${practiceRound} — Question ${practiceIdx + 1} of ${practicePool.length}`;
updatePracticeStatus();
}
function selectPracticeOpt(btn, chosen, correct, wordObj) {
if (practiceAnswered) return;
practiceAnswered = true;
practiceAttempted++;
document.querySelectorAll('#practiceOpts .quiz-option').forEach(b => { b.disabled = true; });
const fb = document.getElementById('practiceFeedback');
if (chosen === correct) {
btn.classList.add('correct');
fb.textContent = '✓ Richtig! You remembered it!';
fb.className = 'feedback-msg show ok';
practiceCorrect++;
addScore(3);
} else {
btn.classList.add('wrong');
fb.textContent = `✗ Correct answer: "${correct}"`;
fb.className = 'feedback-msg show bad';
practiceMistakeCount++;
resetStreak();
// Add to new mistakes for next round
if (!practiceNewMistakes.find(w => w.de === wordObj.de)) {
practiceNewMistakes.push(wordObj);
}
// RE-INSERT mistaken word 3 more times into remaining pool
const remaining = practicePool.slice(practiceIdx + 1);
for (let r = 0; r < 3; r++) {
const pos = Math.floor(Math.random() * (remaining.length + 1));
remaining.splice(pos, 0, { ...wordObj });
}
practicePool = [...practicePool.slice(0, practiceIdx + 1), ...remaining];
document.querySelectorAll('#practiceOpts .quiz-option').forEach(b => {
if (b.textContent === correct) b.classList.add('correct');
});
}
// Show memory tip
if (wordObj.memory) {
const tip = document.getElementById('practiceMemoryTip');
tip.textContent = '💡 Memory Tip: ' + wordObj.memory;
tip.className = 'memory-tip show';
}
document.getElementById('practiceNextBtn').style.display = '';
document.getElementById('practiceCounter').textContent = `Round ${practiceRound} — Question ${practiceIdx + 1} of ${practicePool.length}`;
practiceIdx++;
updatePracticeStatus();
}
function nextPractice() { showPracticeQuestion(); }
function showPracticeResult() {
stopPracticeTimer();
const elapsed = Math.floor((Date.now() - practiceStartTime) / 1000);
document.getElementById('practiceActive').classList.add('hidden');
const res = document.getElementById('practiceResult');
res.classList.remove('hidden');
const accuracy = practiceAttempted > 0 ? Math.round(practiceCorrect / practiceAttempted * 100) : 100;
if (practiceMistakeCount === 0) {
document.getElementById('practiceResEmoji').textContent = '🏆';
document.getElementById('practiceResTitle').textContent = 'Perfect Practice!';
document.getElementById('practiceResMsg').textContent = 'Zero mistakes! You\'ve mastered these words!';
spawnConfetti();
} else {
document.getElementById('practiceResEmoji').textContent = '💪';
document.getElementById('practiceResTitle').textContent = 'Practice Complete!';
document.getElementById('practiceResMsg').textContent = `You had ${practiceMistakeCount} mistake(s). Try again to get a perfect score!`;
}
document.getElementById('practiceResultStats').innerHTML = `
<div class="result-stat-card success-stat"><div class="stat-label">Correct</div><div class="stat-value">${practiceCorrect}</div></div>
<div class="result-stat-card error-stat"><div class="stat-label">Mistakes</div><div class="stat-value">${practiceMistakeCount}</div></div>
<div class="result-stat-card"><div class="stat-label">Accuracy</div><div class="stat-value">${accuracy}%</div></div>
<div class="result-stat-card"><div class="stat-label">Time Spent</div><div class="stat-value">${formatTime(elapsed)}</div></div>
`;
// Show wrong words if any
if (practiceNewMistakes.length > 0) {
document.getElementById('practiceWrongList').innerHTML = `
<div class="ww-title">Words to Review:</div>
${practiceNewMistakes.map(w => `
<div class="wrong-word-item">
<span class="ww-de">${w.de}</span>
<span class="ww-en">${w.en}</span>
<span class="ww-tip">${w.memory || ''}</span>
</div>
`).join('')}
`;
} else {
document.getElementById('practiceWrongList').innerHTML = '';
}
}
// ═══════════════════════════════════════════════════════════
// HARDCORE MODE (Full Vocabulary Final Test)
// ═══════════════════════════════════════════════════════════
function startHardcoreTimer() {
if (hardcoreTimerInterval) clearInterval(hardcoreTimerInterval);
hardcoreStartTime = Date.now();
hardcoreTimerInterval = setInterval(() => {
const elapsed = Math.floor((Date.now() - hardcoreStartTime) / 1000);
document.getElementById('hardcoreTimer').textContent = formatTime(elapsed);
}, 1000);
}
function stopHardcoreTimer() {
if (hardcoreTimerInterval) { clearInterval(hardcoreTimerInterval); hardcoreTimerInterval = null; }
}
// Confirm modal helpers
let _confirmCallback = null;
function showConfirm(emoji, title, msg, onYes) {
document.getElementById('confirmEmoji').textContent = emoji;
document.getElementById('confirmTitle').textContent = title;
document.getElementById('confirmMsg').textContent = msg;
_confirmCallback = onYes;
document.getElementById('confirmOverlay').classList.remove('hidden');
}
function confirmYes() {
document.getElementById('confirmOverlay').classList.add('hidden');
if (_confirmCallback) { _confirmCallback(); _confirmCallback = null; }
}
function confirmNo() {
document.getElementById('confirmOverlay').classList.add('hidden');
_confirmCallback = null;
}
// Hardcore level selection (all levels unlocked)
let hardcoreSelectedLevel = 1;
function selectHCLevel(level, btn) {
hardcoreSelectedLevel = level;
document.querySelectorAll('.hc-level-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
// Only auto-restart if quiz is not in progress
if (hardcoreAttempted === 0) resetHardcore();
}
function confirmHCStop() {
showConfirm('⏹', 'Stop the test?',
`You have attempted ${hardcoreAttempted} question(s). Your progress will be saved as partial stats.`,
() => showHardcoreResult(true)
);
}
function confirmHCRestart() {
showConfirm('↺', 'Restart the test?',
'All your current progress will be lost and the test will restart from the beginning.',
() => resetHardcore()
);
}
function initHardcore() {
resetHardcore();
}
function resetHardcore() {
const levelWords = vocab.filter(v => v.level === hardcoreSelectedLevel);
hardcorePool = shuffle(levelWords);
hardcoreIdx = 0;
hardcoreCorrect = 0;
hardcoreMistakeCount = 0;
hardcoreAttempted = 0;
hardcoreAnswered = false;
hardcoreWrongWords = [];
document.getElementById('hardcoreActive').classList.remove('hidden');
document.getElementById('hardcoreResult').classList.add('hidden');
document.getElementById('hcMistakePractice').classList.add('hidden');
document.getElementById('hcMistakePracticeResult').classList.add('hidden');
document.getElementById('hcLevelPicker').style.display = '';
updateHardcoreStatus();
startHardcoreTimer();
showHardcoreQuestion();
}
function updateHardcoreStatus() {
document.getElementById('hardcoreRemaining').textContent = hardcorePool.length - hardcoreIdx;
document.getElementById('hardcoreAttempted').textContent = hardcoreAttempted;
document.getElementById('hardcoreMistakeCount').textContent = hardcoreMistakeCount;
document.getElementById('hardcoreCorrectCount').textContent = hardcoreCorrect;
}
function showHardcoreQuestion() {
if (hardcoreIdx >= hardcorePool.length) {
showHardcoreResult();
return;
}
hardcoreAnswered = false;
const v = hardcorePool[hardcoreIdx];
document.getElementById('hardcoreQ').textContent = v.de;
document.getElementById('hardcoreCtx').textContent = v.ex ? `e.g. "${v.ex}"` : `Level ${v.level}${(v.category || '').replace(/-/g, ' ')}`;
// Harder options: prefer same category
let sameCategory = vocab.filter(w => w.en !== v.en && w.category === v.category && w.level === currentLevel);
let otherWords = vocab.filter(w => w.en !== v.en && w.level === currentLevel);
let wrongPool = sameCategory.length >= 3 ? sameCategory : otherWords;
let wrong = shuffle(wrongPool).slice(0, 3).map(w => w.en);
// Ensure we have 3 unique wrong answers
while (wrong.length < 3) {
const extra = shuffle(otherWords).find(w => !wrong.includes(w.en) && w.en !== v.en);
if (extra) wrong.push(extra.en);
else break;
}
let options = shuffle([v.en, ...wrong]);
const optDiv = document.getElementById('hardcoreOpts');
optDiv.innerHTML = '';
options.forEach(opt => {
const btn = document.createElement('button');
btn.className = 'quiz-option';
btn.textContent = opt;
btn.onclick = () => selectHardcoreOpt(btn, opt, v.en, v);
optDiv.appendChild(btn);
});
const fb = document.getElementById('hardcoreFeedback');
fb.className = 'feedback-msg';
fb.textContent = '';
document.getElementById('hardcoreMemoryTip').className = 'memory-tip';
document.getElementById('hardcoreNextBtn').style.display = 'none';
const pct = (hardcoreIdx / hardcorePool.length * 100).toFixed(0);
document.getElementById('hardcoreProgress').style.width = pct + '%';
document.getElementById('hardcoreCounter').textContent = `Question ${hardcoreIdx + 1} of ${hardcorePool.length}`;
updateHardcoreStatus();
}
function selectHardcoreOpt(btn, chosen, correct, wordObj) {
if (hardcoreAnswered) return;
hardcoreAnswered = true;
hardcoreAttempted++;
document.querySelectorAll('#hardcoreOpts .quiz-option').forEach(b => { b.disabled = true; });
const fb = document.getElementById('hardcoreFeedback');
if (chosen === correct) {
btn.classList.add('correct');
fb.textContent = '✓ Richtig! Excellent!';
fb.className = 'feedback-msg show ok';
hardcoreCorrect++;
addScore(5);
} else {
btn.classList.add('wrong');
fb.textContent = `✗ Correct answer: "${correct}"`;
fb.className = 'feedback-msg show bad';
hardcoreMistakeCount++;
resetStreak();
if (!hardcoreWrongWords.find(w => w.de === wordObj.de)) {
hardcoreWrongWords.push(wordObj);
}
// Also add to global mistakenWords for Practice mode
if (!mistakenWords.find(w => w.de === wordObj.de)) {
mistakenWords.push(wordObj);
}
document.querySelectorAll('#hardcoreOpts .quiz-option').forEach(b => {
if (b.textContent === correct) b.classList.add('correct');
});
}
// Always show memory tip in Hardcore mode
if (wordObj.memory) {
const tip = document.getElementById('hardcoreMemoryTip');
tip.textContent = '💡 Remember: ' + wordObj.memory;
tip.className = 'memory-tip show';
}
document.getElementById('hardcoreNextBtn').style.display = '';
document.getElementById('hardcoreCounter').textContent = `Question ${hardcoreIdx + 1} of ${hardcorePool.length}`;
hardcoreIdx++;
updateHardcoreStatus();
}
function nextHardcore() { showHardcoreQuestion(); }
function showHardcoreResult(partial = false) {
stopHardcoreTimer();
const elapsed = Math.floor((Date.now() - hardcoreStartTime) / 1000);
const attempted = hardcoreAttempted;
document.getElementById('hardcoreActive').classList.add('hidden');
document.getElementById('hcLevelPicker').style.display = 'none';
const res = document.getElementById('hardcoreResult');
res.classList.remove('hidden');
const accuracy = attempted > 0 ? Math.round(hardcoreCorrect / attempted * 100) : 100;
if (partial) {
document.getElementById('hardcoreResEmoji').textContent = '⏹';
document.getElementById('hardcoreResTitle').textContent = 'Test Stopped';
document.getElementById('hardcoreResMsg').textContent = `You answered ${attempted} of ${hardcorePool.length} questions before stopping.`;
} else if (accuracy === 100) {
document.getElementById('hardcoreResEmoji').textContent = '🏆';
document.getElementById('hardcoreResTitle').textContent = 'PERFECT SCORE!';
document.getElementById('hardcoreResMsg').textContent = 'You are a vocabulary master! Unbeatable! 🔥';
spawnConfetti();
} else if (accuracy >= 80) {
document.getElementById('hardcoreResEmoji').textContent = '🌟';
document.getElementById('hardcoreResTitle').textContent = 'Ausgezeichnet!';
document.getElementById('hardcoreResMsg').textContent = 'Excellent performance! Almost perfect!';
spawnConfetti();
} else if (accuracy >= 60) {
document.getElementById('hardcoreResEmoji').textContent = '💪';
document.getElementById('hardcoreResTitle').textContent = 'Gut gemacht!';
document.getElementById('hardcoreResMsg').textContent = 'Good job! Keep practicing the words below.';
} else {
document.getElementById('hardcoreResEmoji').textContent = '📚';
document.getElementById('hardcoreResTitle').textContent = 'Keep Studying!';
document.getElementById('hardcoreResMsg').textContent = 'Review the words below and try Practice Mistakes!';
}
document.getElementById('hardcoreResultStats').innerHTML = `
<div class="result-stat-card success-stat"><div class="stat-label">Correct</div><div class="stat-value">${hardcoreCorrect}</div></div>
<div class="result-stat-card error-stat"><div class="stat-label">Mistakes</div><div class="stat-value">${hardcoreMistakeCount}</div></div>
<div class="result-stat-card"><div class="stat-label">Accuracy</div><div class="stat-value">${accuracy}%</div></div>
<div class="result-stat-card"><div class="stat-label">Time Spent</div><div class="stat-value">${formatTime(elapsed)}</div></div>
<div class="result-stat-card"><div class="stat-label">${partial ? 'Answered' : 'Total Qs'}</div><div class="stat-value">${partial ? attempted + ' / ' + hardcorePool.length : hardcorePool.length}</div></div>
<div class="result-stat-card"><div class="stat-label">Level</div><div class="stat-value">Lv ${hardcoreSelectedLevel}</div></div>
`;
if (hardcoreWrongWords.length > 0) {
document.getElementById('hcPracticeMistakesBtn').style.display = '';
document.getElementById('hardcoreWrongList').innerHTML = `
<div class="ww-title">❌ Words to Review (${hardcoreWrongWords.length}):</div>
${hardcoreWrongWords.map(w => `
<div class="wrong-word-item">
<span class="ww-de">${w.de}</span>
<span class="ww-en">${w.en}</span>
<span class="ww-tip">💡 ${w.memory || 'No tip'}</span>
</div>
`).join('')}
`;
} else {
document.getElementById('hcPracticeMistakesBtn').style.display = 'none';
document.getElementById('hardcoreWrongList').innerHTML = '<div style="text-align:center;color:var(--success);font-family:DM Mono,monospace;font-size:0.85rem;padding:16px">🎉 No wrong answers! Perfect!</div>';
}
}
// ═══════════════════════════════════════════════════════════
// ALL HARDCORE CONFIRMATIONS
function confirmAHStop() {
showConfirm('⏹', 'Stop the test?',
`You have attempted ${ahAttempted} question(s). Partial stats will be shown.`,
() => showAHResult(true)
);
}
function confirmAHRestart() {
showConfirm('↺', 'Restart the test?',
'All your current progress will be lost and the full test will restart.',
() => resetAllHardcore()
);
}
// ALL HARDCORE MODE (Full Vocabulary ALL Levels)
// ═══════════════════════════════════════════════════════════
function startAHTimer() {
if (ahTimerInterval) clearInterval(ahTimerInterval);
ahStartTime = Date.now();
ahTimerInterval = setInterval(() => {
const elapsed = Math.floor((Date.now() - ahStartTime) / 1000);
document.getElementById('allhardcoreTimer').textContent = formatTime(elapsed);
}, 1000);
}
function stopAHTimer() {
if (ahTimerInterval) { clearInterval(ahTimerInterval); ahTimerInterval = null; }
}
function initAllHardcore() {
resetAllHardcore();
}
function resetAllHardcore() {
// ALL vocab from ALL levels — the ultimate test
ahPool = shuffle([...vocab]);
ahIdx = 0;
ahCorrect = 0;
ahMistakeCount = 0;
ahAttempted = 0;
ahAnswered = false;
ahWrongWords = [];
document.getElementById('allhardcoreActive').classList.remove('hidden');
document.getElementById('allhardcoreResult').classList.add('hidden');
document.getElementById('ahMistakePractice').classList.add('hidden');
document.getElementById('ahMistakePracticeResult').classList.add('hidden');
updateAHStatus();
startAHTimer();
showAHQuestion();
}
function updateAHStatus() {
document.getElementById('allhardcoreRemaining').textContent = ahPool.length - ahIdx;
document.getElementById('allhardcoreAttempted').textContent = ahAttempted;
document.getElementById('allhardcoreMistakeCount').textContent = ahMistakeCount;
document.getElementById('allhardcoreCorrectCount').textContent = ahCorrect;
}
function showAHQuestion() {
if (ahIdx >= ahPool.length) {
showAHResult();
return;
}
ahAnswered = false;
const v = ahPool[ahIdx];
document.getElementById('allhardcoreQ').textContent = v.de;
document.getElementById('allhardcoreCtx').textContent = v.ex ? `e.g. "${v.ex}"` : `Level ${v.level}${(v.category || '').replace(/-/g, ' ')}`;
// Harder options: prefer same category across all levels
let sameCategory = vocab.filter(w => w.en !== v.en && w.category === v.category);
let otherWords = vocab.filter(w => w.en !== v.en);
let wrongPool = sameCategory.length >= 3 ? sameCategory : otherWords;
let wrong = shuffle(wrongPool).slice(0, 3).map(w => w.en);
while (wrong.length < 3) {
const extra = shuffle(otherWords).find(w => !wrong.includes(w.en) && w.en !== v.en);
if (extra) wrong.push(extra.en);
else break;
}
let options = shuffle([v.en, ...wrong]);
const optDiv = document.getElementById('allhardcoreOpts');
optDiv.innerHTML = '';
options.forEach(opt => {
const btn = document.createElement('button');
btn.className = 'quiz-option';
btn.textContent = opt;
btn.onclick = () => selectAHOpt(btn, opt, v.en, v);
optDiv.appendChild(btn);
});
const fb = document.getElementById('allhardcoreFeedback');
fb.className = 'feedback-msg';
fb.textContent = '';
document.getElementById('allhardcoreMemoryTip').className = 'memory-tip';
document.getElementById('allhardcoreNextBtn').style.display = 'none';
const pct = (ahIdx / ahPool.length * 100).toFixed(0);
document.getElementById('allhardcoreProgress').style.width = pct + '%';
document.getElementById('allhardcoreCounter').textContent = `Question ${ahIdx + 1} of ${ahPool.length}`;
updateAHStatus();
}
function selectAHOpt(btn, chosen, correct, wordObj) {
if (ahAnswered) return;
ahAnswered = true;
ahAttempted++;
document.querySelectorAll('#allhardcoreOpts .quiz-option').forEach(b => { b.disabled = true; });
const fb = document.getElementById('allhardcoreFeedback');
if (chosen === correct) {
btn.classList.add('correct');
fb.textContent = '✓ Richtig! Excellent!';
fb.className = 'feedback-msg show ok';
ahCorrect++;
addScore(5);
} else {
btn.classList.add('wrong');
fb.textContent = `✗ Correct answer: "${correct}"`;
fb.className = 'feedback-msg show bad';
ahMistakeCount++;
resetStreak();
if (!ahWrongWords.find(w => w.de === wordObj.de)) {
ahWrongWords.push(wordObj);
}
// Also add to global mistakenWords for Practice mode
if (!mistakenWords.find(w => w.de === wordObj.de)) {
mistakenWords.push(wordObj);
}
document.querySelectorAll('#allhardcoreOpts .quiz-option').forEach(b => {
if (b.textContent === correct) b.classList.add('correct');
});
}
// Always show memory tip
if (wordObj.memory) {
const tip = document.getElementById('allhardcoreMemoryTip');
tip.textContent = '💡 Remember: ' + wordObj.memory;
tip.className = 'memory-tip show';
}
document.getElementById('allhardcoreNextBtn').style.display = '';
document.getElementById('allhardcoreCounter').textContent = `Question ${ahIdx + 1} of ${ahPool.length}`;
ahIdx++;
updateAHStatus();
}
function nextAllHardcore() { showAHQuestion(); }
function showAHResult(partial = false) {
stopAHTimer();
const elapsed = Math.floor((Date.now() - ahStartTime) / 1000);
const attempted = ahAttempted;
document.getElementById('allhardcoreActive').classList.add('hidden');
const res = document.getElementById('allhardcoreResult');
res.classList.remove('hidden');
const accuracy = ahAttempted > 0 ? Math.round(ahCorrect / ahAttempted * 100) : 100;
if (partial) {
document.getElementById('allhardcoreResEmoji').textContent = '⏹';
document.getElementById('allhardcoreResTitle').textContent = 'Test Stopped';
document.getElementById('allhardcoreResMsg').textContent = `You answered ${attempted} of ${ahPool.length} questions before stopping.`;
} else if (accuracy === 100) {
document.getElementById('allhardcoreResEmoji').textContent = '👑';
document.getElementById('allhardcoreResTitle').textContent = 'LEGENDARY!';
document.getElementById('allhardcoreResMsg').textContent = 'You mastered EVERY word! You are unstoppable! 💀🔥';
spawnConfetti();
} else if (accuracy >= 80) {
document.getElementById('allhardcoreResEmoji').textContent = '🏆';
document.getElementById('allhardcoreResTitle').textContent = 'Ausgezeichnet!';
document.getElementById('allhardcoreResMsg').textContent = 'Incredible performance across all levels!';
spawnConfetti();
} else if (accuracy >= 60) {
document.getElementById('allhardcoreResEmoji').textContent = '💪';
document.getElementById('allhardcoreResTitle').textContent = 'Gut gemacht!';
document.getElementById('allhardcoreResMsg').textContent = 'Good effort! Review the words below.';
} else {
document.getElementById('allhardcoreResEmoji').textContent = '📚';
document.getElementById('allhardcoreResTitle').textContent = 'Keep Studying!';
document.getElementById('allhardcoreResMsg').textContent = 'Go back to flashcards and practice more!';
}
document.getElementById('allhardcoreResultStats').innerHTML = `
<div class="result-stat-card success-stat"><div class="stat-label">Correct</div><div class="stat-value">${ahCorrect}</div></div>
<div class="result-stat-card error-stat"><div class="stat-label">Mistakes</div><div class="stat-value">${ahMistakeCount}</div></div>
<div class="result-stat-card"><div class="stat-label">Accuracy</div><div class="stat-value">${accuracy}%</div></div>
<div class="result-stat-card"><div class="stat-label">Time Spent</div><div class="stat-value">${formatTime(elapsed)}</div></div>
<div class="result-stat-card"><div class="stat-label">${partial ? 'Answered' : 'Total Qs'}</div><div class="stat-value">${partial ? attempted + ' / ' + ahPool.length : ahPool.length}</div></div>
<div class="result-stat-card"><div class="stat-label">All Levels</div><div class="stat-value">1-6</div></div>
`;
if (ahWrongWords.length > 0) {
// Show Practice Mistakes button
document.getElementById('ahPracticeMistakesBtn').style.display = '';
document.getElementById('allhardcoreWrongList').innerHTML = `
<div class="ww-title">❌ Words to Review (${ahWrongWords.length}):</div>
${ahWrongWords.map(w => `
<div class="wrong-word-item">
<span class="ww-de">${w.de}</span>
<span class="ww-en">${w.en}</span>
<span class="ww-tip">💡 ${w.memory || 'No tip'}</span>
</div>
`).join('')}
`;
} else {
document.getElementById('ahPracticeMistakesBtn').style.display = 'none';
document.getElementById('allhardcoreWrongList').innerHTML = '<div style="text-align:center;color:var(--success);font-family:DM Mono,monospace;font-size:0.85rem;padding:16px">🎉 No wrong answers! Legendary!</div>';
}
}
// ═══════════════════════════════════════════════════════════
// ALL HARDCORE — PRACTICE MISTAKES (post-quiz loop)
// ═══════════════════════════════════════════════════════════
let ahmpPool = [], ahmpIdx = 0, ahmpCorrectCount = 0, ahmpMistakeCount = 0, ahmpAttemptedCount = 0;
let ahmpAnswered = false, ahmpTimerInterval = null, ahmpStartTime = null;
let ahmpNewMistakes = [], ahmpRound = 1;
function startAHMistakePractice() {
if (ahWrongWords.length === 0) return;
// Hide result & active quiz, show mistake practice
document.getElementById('allhardcoreResult').classList.add('hidden');
document.getElementById('allhardcoreActive').classList.add('hidden');
document.getElementById('ahMistakePracticeResult').classList.add('hidden');
document.getElementById('ahMistakePractice').classList.remove('hidden');
// Build pool: each wrong word ×3, shuffled
ahmpPool = [];
for (let i = 0; i < 3; i++) {
ahmpPool = ahmpPool.concat([...ahWrongWords]);
}
ahmpPool = shuffle(ahmpPool);
ahmpIdx = 0;
ahmpCorrectCount = 0;
ahmpMistakeCount = 0;
ahmpAttemptedCount = 0;
ahmpAnswered = false;
ahmpNewMistakes = [];
ahmpRound = 1;
// Start timer
if (ahmpTimerInterval) clearInterval(ahmpTimerInterval);
ahmpStartTime = Date.now();
ahmpTimerInterval = setInterval(() => {
const elapsed = Math.floor((Date.now() - ahmpStartTime) / 1000);
document.getElementById('ahmpTimer').textContent = formatTime(elapsed);
}, 1000);
updateAHMPStatus();
showAHMPQuestion();
}
function updateAHMPStatus() {
document.getElementById('ahmpRemaining').textContent = ahmpPool.length - ahmpIdx;
document.getElementById('ahmpAttempted').textContent = ahmpAttemptedCount;
document.getElementById('ahmpMistakes').textContent = ahmpMistakeCount;
document.getElementById('ahmpCorrect').textContent = ahmpCorrectCount;
}
function showAHMPQuestion() {
if (ahmpIdx >= ahmpPool.length) {
// End of round: check if there are new mistakes
if (ahmpNewMistakes.length > 0) {
// Re-queue new mistakes ×3
ahmpPool = [];
for (let i = 0; i < 3; i++) {
ahmpPool = ahmpPool.concat([...ahmpNewMistakes]);
}
ahmpPool = shuffle(ahmpPool);
ahmpNewMistakes = [];
ahmpIdx = 0;
ahmpRound++;
updateAHMPStatus();
showAHMPQuestion();
return;
}
// All clear!
showAHMPResult();
return;
}
ahmpAnswered = false;
const v = ahmpPool[ahmpIdx];
document.getElementById('ahmpQ').textContent = v.de;
document.getElementById('ahmpCtx').textContent = v.ex ? `e.g. "${v.ex}"` : `Level ${v.level}${(v.category || '').replace(/-/g, ' ')}`;
let wrong = shuffle(vocab.filter(w => w.en !== v.en)).slice(0, 3).map(w => w.en);
let options = shuffle([v.en, ...wrong]);
const optDiv = document.getElementById('ahmpOpts');
optDiv.innerHTML = '';
options.forEach(opt => {
const btn = document.createElement('button');
btn.className = 'quiz-option';
btn.textContent = opt;
btn.onclick = () => selectAHMPOpt(btn, opt, v.en, v);
optDiv.appendChild(btn);
});
document.getElementById('ahmpFeedback').className = 'feedback-msg';
document.getElementById('ahmpFeedback').textContent = '';
document.getElementById('ahmpMemoryTip').className = 'memory-tip';
document.getElementById('ahmpNextBtn').style.display = 'none';
const pct = (ahmpIdx / ahmpPool.length * 100).toFixed(0);
document.getElementById('ahmpProgress').style.width = pct + '%';
document.getElementById('ahmpCounter').textContent = `Round ${ahmpRound} — Question ${ahmpIdx + 1} of ${ahmpPool.length}`;
updateAHMPStatus();
}
function selectAHMPOpt(btn, chosen, correct, wordObj) {
if (ahmpAnswered) return;
ahmpAnswered = true;
ahmpAttemptedCount++;
document.querySelectorAll('#ahmpOpts .quiz-option').forEach(b => { b.disabled = true; });
const fb = document.getElementById('ahmpFeedback');
if (chosen === correct) {
btn.classList.add('correct');
fb.textContent = '✓ Richtig! You remembered it!';
fb.className = 'feedback-msg show ok';
ahmpCorrectCount++;
addScore(3);
} else {
btn.classList.add('wrong');
fb.textContent = `✗ Correct answer: "${correct}"`;
fb.className = 'feedback-msg show bad';
ahmpMistakeCount++;
resetStreak();
if (!ahmpNewMistakes.find(w => w.de === wordObj.de)) {
ahmpNewMistakes.push(wordObj);
}
document.querySelectorAll('#ahmpOpts .quiz-option').forEach(b => {
if (b.textContent === correct) b.classList.add('correct');
});
}
if (wordObj.memory) {
const tip = document.getElementById('ahmpMemoryTip');
tip.textContent = '💡 Remember: ' + wordObj.memory;
tip.className = 'memory-tip show';
}
document.getElementById('ahmpNextBtn').style.display = '';
ahmpIdx++;
updateAHMPStatus();
}
function nextAHMP() { showAHMPQuestion(); }
function showAHMPResult() {
if (ahmpTimerInterval) { clearInterval(ahmpTimerInterval); ahmpTimerInterval = null; }
const elapsed = Math.floor((Date.now() - ahmpStartTime) / 1000);
document.getElementById('ahMistakePractice').classList.add('hidden');
const res = document.getElementById('ahMistakePracticeResult');
res.classList.remove('hidden');
const accuracy = ahmpAttemptedCount > 0 ? Math.round(ahmpCorrectCount / ahmpAttemptedCount * 100) : 100;
if (ahmpMistakeCount === 0) {
document.getElementById('ahmpResEmoji').textContent = '🏆';
document.getElementById('ahmpResTitle').textContent = 'All Mistakes Fixed!';
document.getElementById('ahmpResMsg').textContent = 'Zero mistakes! You\'ve mastered every word! 🔥';
spawnConfetti();
} else {
document.getElementById('ahmpResEmoji').textContent = '💪';
document.getElementById('ahmpResTitle').textContent = 'Practice Complete!';
document.getElementById('ahmpResMsg').textContent = `${ahmpMistakeCount} mistake(s) remaining. Try again!`;
}
document.getElementById('ahmpResultStats').innerHTML = `
<div class="result-stat-card success-stat"><div class="stat-label">Correct</div><div class="stat-value">${ahmpCorrectCount}</div></div>
<div class="result-stat-card error-stat"><div class="stat-label">Mistakes</div><div class="stat-value">${ahmpMistakeCount}</div></div>
<div class="result-stat-card"><div class="stat-label">Accuracy</div><div class="stat-value">${accuracy}%</div></div>
<div class="result-stat-card"><div class="stat-label">Time Spent</div><div class="stat-value">${formatTime(elapsed)}</div></div>
<div class="result-stat-card"><div class="stat-label">Rounds</div><div class="stat-value">${ahmpRound}</div></div>
<div class="result-stat-card"><div class="stat-label">Total Qs</div><div class="stat-value">${ahmpAttemptedCount}</div></div>
`;
if (ahmpNewMistakes.length > 0) {
document.getElementById('ahmpWrongList').innerHTML = `
<div class="ww-title">Still struggling with:</div>
${ahmpNewMistakes.map(w => `
<div class="wrong-word-item">
<span class="ww-de">${w.de}</span>
<span class="ww-en">${w.en}</span>
<span class="ww-tip">💡 ${w.memory || ''}</span>
</div>
`).join('')}
`;
} else {
document.getElementById('ahmpWrongList').innerHTML = '';
}
}
// ═══════════════════════════════════════════════════════════
// INIT
// ═══════════════════════════════════════════════════════════
loadProgress();
updateLevelButtons();
initCards();
</script>
</body>
</html>