Enhance: Conceal closed letter sheet, enrich Hogwarts lore story, and allow calm reading before boarding Hogwarts Express

This commit is contained in:
kobold-ai
2026-09-21 16:53:45 +02:00
parent 611a94ab43
commit 1cbea5e84f
4 changed files with 279 additions and 52 deletions
+141 -24
View File
@@ -245,11 +245,17 @@ body {
.antique-envelope-scene {
position: relative;
width: min(440px, 92vw);
height: min(260px, 58vw);
width: min(480px, 94vw);
height: min(280px, 60vw);
margin: 25px auto 35px;
perspective: 1200px;
perspective-origin: center top;
transition: margin-top 0.8s cubic-bezier(0.25, 1, 0.5, 1);
}
.antique-envelope-scene.envelope-opened {
margin-top: 155px;
margin-bottom: 25px;
}
.envelope-base {
@@ -280,26 +286,43 @@ body {
/* The Letter inside the envelope (slides up when opened) */
.letter-sheet {
position: absolute;
bottom: 8px;
top: 15px;
left: 4%;
width: 92%;
height: 92%;
height: 440px;
max-height: 75vh;
background-color: #f7f0df;
background-image:
radial-gradient(ellipse at 50% 25%, rgba(255, 255, 255, 0.5) 0%, rgba(140, 100, 50, 0.15) 100%),
radial-gradient(ellipse at 50% 25%, rgba(255, 255, 255, 0.6) 0%, rgba(140, 100, 50, 0.15) 100%),
url('/static/img/parchment_texture.jpg');
background-size: cover;
background-position: center;
border-radius: 4px;
border: 1.5px solid #8e6c43;
padding: 16px 18px;
padding: 16px 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
z-index: 2;
transition: transform 1.1s cubic-bezier(0.2, 0.8, 0.25, 1) 0.35s, box-shadow 0.8s ease, z-index 0.2s 0.35s;
overflow: hidden;
opacity: 0;
transform: translateY(40px) scale(0.92);
pointer-events: none;
transition: transform 0.9s cubic-bezier(0.2, 0.85, 0.25, 1) 0.35s,
opacity 0.6s ease 0.35s,
box-shadow 0.8s ease;
overflow-y: auto;
text-align: center;
outline: 1px dashed rgba(130, 95, 55, 0.45);
outline-offset: -5px;
scrollbar-width: thin;
scrollbar-color: #8c6838 transparent;
}
.letter-sheet::-webkit-scrollbar {
width: 6px;
}
.letter-sheet::-webkit-scrollbar-thumb {
background-color: #8c6838;
border-radius: 4px;
}
/* Front Pocket of envelope (covers the lower portion of the letter) */
@@ -312,14 +335,14 @@ body {
radial-gradient(ellipse at 50% 65%, rgba(255, 255, 255, 0.25) 0%, rgba(130, 95, 45, 0.2) 100%),
url('/static/img/parchment_texture.jpg');
background-size: cover;
clip-path: polygon(0 32%, 50% 54%, 100% 32%, 100% 100%, 0 100%);
clip-path: polygon(0 16%, 50% 50%, 100% 16%, 100% 100%, 0 100%);
z-index: 4;
border: 2px solid #8e6c43;
filter: drop-shadow(0 -3px 6px rgba(60, 40, 15, 0.3));
display: flex;
align-items: flex-end;
justify-content: center;
padding-bottom: 20px;
padding-bottom: 22px;
pointer-events: none;
}
@@ -397,11 +420,11 @@ body {
/* Sketched Wax Seal placed right over the flap tip */
.sketched-wax-seal {
position: absolute;
top: 56%;
top: 55%;
left: 50%;
transform: translate(-50%, -50%) rotate(-3deg);
width: 72px;
height: 72px;
width: 74px;
height: 74px;
border-radius: 50%;
cursor: pointer;
z-index: 7;
@@ -454,6 +477,7 @@ body {
/* Open state: Flap opens, seal dissolves, letter slides out */
.envelope-opened .sketched-wax-seal {
animation: breakSealAnim 0.7s forwards;
pointer-events: none;
}
.envelope-opened .envelope-closing-flap {
@@ -463,11 +487,13 @@ body {
}
.envelope-opened .letter-sheet {
transform: translateY(-135px) scale(1.06);
z-index: 10;
opacity: 1;
pointer-events: auto;
transform: translateY(-145px) scale(1.02);
z-index: 25;
box-shadow:
0 20px 48px rgba(0, 0, 0, 0.85),
0 0 25px rgba(201, 168, 106, 0.35);
0 24px 60px rgba(0, 0, 0, 0.85),
0 0 35px rgba(201, 168, 106, 0.4);
}
@keyframes breakSealAnim {
@@ -561,9 +587,10 @@ body {
}
.letter-paragraph {
font-size: clamp(1.02rem, 3.0vw, 1.2rem);
font-size: clamp(1.0rem, 2.9vw, 1.16rem);
margin-bottom: 8px;
text-indent: 1.2em;
line-height: 1.48;
}
.letter-paragraph strong {
@@ -615,6 +642,30 @@ body {
color: var(--text-muted);
line-height: 1.5;
margin-bottom: 14px;
transition: all 0.3s ease;
}
.intro-action-container {
margin-top: 14px;
display: flex;
justify-content: center;
}
.letter-actions {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
width: 100%;
}
.fade-in-action {
animation: fadeInActionAnim 0.5s ease forwards;
}
@keyframes fadeInActionAnim {
0% { opacity: 0; transform: translateY(12px); }
100% { opacity: 1; transform: translateY(0); }
}
.btn-break-seal {
@@ -629,23 +680,89 @@ body {
box-shadow: 0 10px 28px rgba(201, 168, 106, 0.6);
}
.btn-launch-quiz {
font-size: clamp(1.05rem, 3.4vw, 1.22rem);
padding: 14px 28px;
font-weight: 700;
letter-spacing: 0.5px;
border-radius: 10px;
cursor: pointer;
background: linear-gradient(135deg, #e6c564 0%, #c9a86a 50%, #8c6838 100%);
color: #1a0f05;
border: 1px solid #ffd700;
box-shadow: 0 6px 25px rgba(201, 168, 106, 0.5), 0 0 15px rgba(255, 215, 0, 0.35);
transition: all 0.3s ease;
}
.btn-launch-quiz:hover {
transform: translateY(-2px) scale(1.02);
box-shadow: 0 10px 32px rgba(201, 168, 106, 0.75), 0 0 25px rgba(255, 215, 0, 0.6);
}
.glow-pulse {
animation: glowPulseAnim 2.6s infinite alternate ease-in-out;
}
@keyframes glowPulseAnim {
0% { box-shadow: 0 6px 20px rgba(201, 168, 106, 0.45); }
100% { box-shadow: 0 10px 32px rgba(201, 168, 106, 0.8), 0 0 25px rgba(255, 215, 0, 0.6); }
}
.btn-close-letter {
font-size: 0.92rem;
color: var(--text-muted);
background: transparent;
border: 1px solid rgba(201, 168, 106, 0.25);
padding: 8px 18px;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
}
.btn-close-letter:hover {
color: var(--gold-bright);
border-color: var(--gold);
background: rgba(201, 168, 106, 0.1);
}
@media (max-width: 480px) {
.antique-envelope-scene {
width: min(320px, 92vw);
height: min(205px, 58vw);
width: min(340px, 94vw);
height: min(215px, 60vw);
margin: 15px auto 25px;
}
.antique-envelope-scene.envelope-opened {
margin-top: 135px;
margin-bottom: 20px;
}
.sketched-wax-seal {
width: 60px;
height: 60px;
top: 56%;
width: 62px;
height: 62px;
top: 55%;
}
.letter-sheet {
top: 10px;
left: 2%;
width: 96%;
height: 400px;
max-height: 72vh;
padding: 12px 14px;
}
.envelope-opened .letter-sheet {
transform: translateY(-100px) scale(1.04);
transform: translateY(-120px) scale(1.01);
}
.letter-paragraph {
font-size: 0.96rem;
line-height: 1.42;
margin-bottom: 6px;
}
.envelope-front-pocket {
padding-bottom: 14px;
}
.btn-launch-quiz {
padding: 12px 20px;
font-size: 1.0rem;
}
}
.difficulty-selection {
+116 -19
View File
@@ -20,19 +20,25 @@ const UI_TEXT = {
letterSchoolTitle: "Hogwarts-Schule für Hexerei und Zauberei",
letterHeadmaster: "Schulleiter: Albus Dumbledore<br><span>(Großhexenmeister, Träger des Merlin-Ordens Erster Klasse)</span>",
letterSalutation: "Liebe Hexe, lieber Zauberer,",
letterParagraph1: "wir freuen uns, dir mitteilen zu dürfen, dass du zu den <strong>Magischen Z.A.G.-Prüfungen</strong> des Zaubereiministeriums zugelassen bist.",
letterParagraph2: "Zehn anspruchsvolle Prüfungsfragen entscheiden über deinen Rang in den Chroniken von Hogwarts. Zeige deinen Scharfsinn und sichere bis zu <strong>1.000 Hauspunkte</strong> für dein Haus!",
letterParagraph1: "die schwebenden Kerzen der Großen Halle flackern in unruhigem Schein, und der Sprechende Hut hat nach Jahrhunderten des Schweigens ein wohlbehütetes Geheimnis preisgegeben: Tief in den uralten Gewölben des Schlosses – verborgen hinter den wachsamen Wasserspeiern und den staubigen Bänden der Verbotenen Abteilung – wurde eine magische Prüfungsurkunde versiegelt.",
letterParagraph2: "Zehn arkanische Prüfungen des Scharfsinns, der Zauberkunde und der Entschlossenheit liegen vor dir. Nur wer wahren Mut beweist und die uralten Rätsel meistert, sichert sich seinen ehrenvollen Platz in den Chroniken von Hogwarts und trägt bis zu <strong>1.000 Hauspunkte</strong> für sein Haus davon!",
letterParagraph3: "Halte deinen Zauberstab bereit, besinne dich auf deine Flüche und Gegenzauber und nimm dich vor verhexten Trugschlüssen in Acht. Wir erwarten deine Eule nicht später als mit dem nächsten Glockenschlag des Uhrenturms.",
letterClosing: "Mit vorzüglicher Hochachtung,",
letterSignature: "Minerva McGonagall",
letterSignTitle: "Stellvertretende Schulleiterin",
introDescClosed: "Ein verzauberter Pergamentbrief aus Hogwarts liegt vor dir.<br>Wähle deinen Schwierigkeitsgrad und brich das rote Zaubersiegel:",
introDescOpened: "Lies die Botschaft der Schulleitung aufmerksam.<br>Sobald du bereit bist, besteige den Hogwarts-Express zur Prüfung:",
introDesc: "Ein verzauberter Pergamentbrief aus Hogwarts liegt vor dir.<br>Wähle deinen Schwierigkeitsgrad und brich das rote Zaubersiegel:",
diffLabel: "Schwierigkeitsgrad wählen:",
diffAll: "Alle gemischt",
diffEasy: "Leicht",
diffMed: "Mittel",
diffHard: "Schwer",
breakSealBtn: "📜 Siegel brechen & Brief öffnen",
startQuizBtn: "🚂 Hogwarts-Express besteigen & Prüfung beginnen ➔",
closeLetterBtn: "↩ Brief wieder schließen",
startBtn: "📜 Siegel brechen & Brief öffnen",
startBtnCasting: "✨ Zauber wird gewirkt...",
startBtnCasting: "✨ Zauberstab wird vorbereitet...",
pointsLabel: "🏆 Hauspunkte",
questionLabel: "Frage",
ofLabel: "von",
@@ -59,19 +65,25 @@ const UI_TEXT = {
letterSchoolTitle: "Hogwarts School of Witchcraft and Wizardry",
letterHeadmaster: "Headmaster: Albus Dumbledore<br><span>(Order of Merlin, First Class, Grand Sorc.)</span>",
letterSalutation: "Dear Witch or Wizard,",
letterParagraph1: "we are pleased to inform you that you have been admitted to the <strong>Magical O.W.L. Examinations</strong> of the Ministry of Magic.",
letterParagraph2: "Ten demanding examination questions will determine your rank in the Hogwarts archives. Show your wisdom and claim up to <strong>1,000 House Points</strong> for your house!",
letterParagraph1: "The floating candles of the Great Hall flicker with restless anticipation, and the Sorting Hat has stirred after centuries of silence to unveil a closely guarded secret: Deep within the ancient castle vaults—past the watchful gargoyles and the dust of the Restricted Section—a legendary trial has been unsealed.",
letterParagraph2: "Ten arcane trials of wit, magical lore, and courage lie ahead of you. Only those who prove true valor and decipher the hidden mysteries shall claim their place in the eternal Hogwarts archives and earn up to <strong>1,000 House Points</strong> for their house!",
letterParagraph3: "Ready your wand, recall your spells and counter-curses, and beware of enchanted trickery. We await your owl by no later than the next chime of the clock tower.",
letterClosing: "Yours sincerely,",
letterSignature: "Minerva McGonagall",
letterSignTitle: "Deputy Headmistress",
introDescClosed: "An enchanted Hogwarts parchment letter lies before you.<br>Choose your difficulty and break the crimson magical seal:",
introDescOpened: "Read the message from Hogwarts leadership carefully.<br>When you are ready, board the Hogwarts Express for your trial:",
introDesc: "An enchanted Hogwarts parchment letter lies before you.<br>Choose your difficulty and break the crimson magical seal:",
diffLabel: "Choose difficulty level:",
diffAll: "All mixed",
diffEasy: "Easy",
diffMed: "Medium",
diffHard: "Hard",
breakSealBtn: "📜 Break Seal & Open Letter",
startQuizBtn: "🚂 Board Hogwarts Express & Begin Trials ➔",
closeLetterBtn: "↩ Close letter again",
startBtn: "📜 Break Seal & Open Letter",
startBtnCasting: "✨ Casting spell...",
startBtnCasting: "✨ Preparing wand...",
pointsLabel: "🏆 House Points",
questionLabel: "Question",
ofLabel: "of",
@@ -163,12 +175,16 @@ function applyQuizTranslations(lang) {
const letterSalutation = document.getElementById("letterSalutation");
const letterParagraph1 = document.getElementById("letterParagraph1");
const letterParagraph2 = document.getElementById("letterParagraph2");
const letterParagraph3 = document.getElementById("letterParagraph3");
const letterClosing = document.getElementById("letterClosing");
const letterSignature = document.getElementById("letterSignature");
const letterSignTitle = document.getElementById("letterSignTitle");
const envelopeRecipient = document.getElementById("envelopeRecipient");
const envelopeLocation = document.getElementById("envelopeLocation");
const breakSealBtn = document.getElementById("breakSealBtn");
const startQuizBtn = document.getElementById("startQuizBtn");
const closeLetterBtn = document.getElementById("closeLetterBtn");
if (pageTitle) pageTitle.innerText = t.pageTitle;
if (pageSubtitle) pageSubtitle.innerText = t.pageSubtitle;
@@ -180,15 +196,29 @@ function applyQuizTranslations(lang) {
if (letterSalutation && t.letterSalutation) letterSalutation.innerText = t.letterSalutation;
if (letterParagraph1 && t.letterParagraph1) letterParagraph1.innerHTML = t.letterParagraph1;
if (letterParagraph2 && t.letterParagraph2) letterParagraph2.innerHTML = t.letterParagraph2;
if (letterParagraph3 && t.letterParagraph3) letterParagraph3.innerHTML = t.letterParagraph3;
if (letterClosing && t.letterClosing) letterClosing.innerText = t.letterClosing;
if (letterSignature && t.letterSignature) letterSignature.innerText = t.letterSignature;
if (letterSignTitle && t.letterSignTitle) letterSignTitle.innerText = t.letterSignTitle;
if (introDesc) introDesc.innerHTML = t.introDesc;
const envelope = document.getElementById("envelope");
if (introDesc) {
if (envelope && envelope.classList.contains("envelope-opened") && t.introDescOpened) {
introDesc.innerHTML = t.introDescOpened;
} else if (t.introDescClosed) {
introDesc.innerHTML = t.introDescClosed;
} else if (t.introDesc) {
introDesc.innerHTML = t.introDesc;
}
}
if (diffLabel) diffLabel.innerText = t.diffLabel;
if (diffAllText) diffAllText.innerText = t.diffAll;
if (diffEasyText) diffEasyText.innerText = t.diffEasy;
if (diffMedText) diffMedText.innerText = t.diffMed;
if (diffHardText) diffHardText.innerText = t.diffHard;
if (breakSealBtn && !breakSealBtn.disabled) breakSealBtn.innerText = t.breakSealBtn || t.startBtn;
if (startQuizBtn && !startQuizBtn.disabled) startQuizBtn.innerText = t.startQuizBtn;
if (closeLetterBtn) closeLetterBtn.innerText = t.closeLetterBtn;
if (startBtn && !startBtn.disabled) startBtn.innerText = t.startBtn;
if (checkBtn) checkBtn.innerText = t.checkBtn;
if (hintBtn) hintBtn.innerText = t.hintBtn;
@@ -253,16 +283,12 @@ function saveRecentQuestions(ids) {
} catch(e) {}
}
async function startGame() {
function breakSealAndOpenLetter() {
const envelope = document.getElementById("envelope");
const startBtn = document.getElementById("startBtn");
const t = UI_TEXT[currentLang] || UI_TEXT.de;
if (!envelope || envelope.classList.contains("envelope-opened")) return;
startBtn.disabled = true;
startBtn.innerText = t.startBtnCasting;
envelope.classList.add("envelope-opened");
playSound('magic');
sessionQuestionIds = [];
envelope.classList.add("envelope-opened");
// Start background music if enabled or first click
const bgm = document.getElementById("bgm");
@@ -278,6 +304,55 @@ async function startGame() {
}).catch(() => {});
}
const breakSealBtn = document.getElementById("breakSealBtn");
const startBtn = document.getElementById("startBtn");
const letterActions = document.getElementById("letterActions");
const introDesc = document.getElementById("introDesc");
const t = UI_TEXT[currentLang] || UI_TEXT.de;
if (breakSealBtn) breakSealBtn.classList.add("hidden");
if (startBtn) startBtn.classList.add("hidden");
if (letterActions) {
letterActions.classList.remove("hidden");
letterActions.classList.add("fade-in-action");
}
if (introDesc && t.introDescOpened) {
introDesc.innerHTML = t.introDescOpened;
}
}
function closeLetter() {
const envelope = document.getElementById("envelope");
if (!envelope) return;
envelope.classList.remove("envelope-opened");
const breakSealBtn = document.getElementById("breakSealBtn");
const startBtn = document.getElementById("startBtn");
const letterActions = document.getElementById("letterActions");
const introDesc = document.getElementById("introDesc");
const t = UI_TEXT[currentLang] || UI_TEXT.de;
if (letterActions) letterActions.classList.add("hidden");
if (breakSealBtn) breakSealBtn.classList.remove("hidden");
if (startBtn) startBtn.classList.remove("hidden");
if (introDesc && t.introDescClosed) {
introDesc.innerHTML = t.introDescClosed;
}
}
async function startQuizFromLetter() {
const startQuizBtn = document.getElementById("startQuizBtn");
const t = UI_TEXT[currentLang] || UI_TEXT.de;
if (startQuizBtn) {
startQuizBtn.disabled = true;
startQuizBtn.innerText = t.startBtnCasting;
}
playSound('magic');
sessionQuestionIds = [];
try {
const recentIds = getRecentQuestionIds();
const wrongIds = getWrongQuestionIds();
@@ -304,18 +379,40 @@ async function startGame() {
currentScore = data.score;
attemptsUsed = data.attempts_used;
const introCard = document.getElementById("introCard");
if (introCard) {
introCard.style.transition = "opacity 0.4s ease, transform 0.4s ease";
introCard.style.opacity = "0";
introCard.style.transform = "scale(0.97)";
}
setTimeout(() => {
document.getElementById("introCard").classList.add("hidden");
if (introCard) {
introCard.classList.add("hidden");
introCard.style.opacity = "";
introCard.style.transform = "";
}
document.getElementById("gameCard").classList.remove("hidden");
startTimer();
renderQuestion(data);
}, 1800);
}, 400);
} catch (err) {
alert(t.startError + err.message);
startBtn.disabled = false;
startBtn.innerText = t.startBtn;
envelope.classList.remove("envelope-opened");
if (startQuizBtn) {
startQuizBtn.disabled = false;
startQuizBtn.innerText = t.startQuizBtn;
}
}
}
// Fallback alias for startGame
function startGame() {
const envelope = document.getElementById("envelope");
if (envelope && !envelope.classList.contains("envelope-opened")) {
breakSealAndOpenLetter();
} else {
startQuizFromLetter();
}
}
+1 -1
View File
@@ -8,7 +8,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@500;700&family=Marck+Script&family=MedievalSharp&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/css/style.css?v=2.5">
<link rel="stylesheet" href="/static/css/style.css?v=2.6">
{% block head %}{% endblock %}
</head>
<body>
+21 -8
View File
@@ -38,10 +38,13 @@
<div class="letter-body-handwritten">
<p class="letter-salutation" id="letterSalutation">Liebe Hexe, lieber Zauberer,</p>
<p class="letter-paragraph" id="letterParagraph1">
wir freuen uns, dir mitteilen zu dürfen, dass du zu den <strong>Magischen Z.A.G.-Prüfungen</strong> an der Hogwarts-Schule zugelassen bist.
die schwebenden Kerzen der Großen Halle flackern in unruhigem Schein, und der Sprechende Hut hat nach Jahrhunderten des Schweigens ein wohlbehütetes Geheimnis preisgegeben: Tief in den uralten Gewölben des Schlosses – verborgen hinter den wachsamen Wasserspeiern und den staubigen Bänden der Verbotenen Abteilung – wurde eine magische Prüfungsurkunde versiegelt.
</p>
<p class="letter-paragraph" id="letterParagraph2">
Zehn anspruchsvolle Prüfungsfragen entscheiden über deinen Rang in den Chroniken. Zeige deinen Scharfsinn und sichere bis zu <strong>1.000 Hauspunkte</strong> für dein Haus!
Zehn arkanische Prüfungen des Scharfsinns, der Zauberkunde und der Entschlossenheit liegen vor dir. Nur wer wahren Mut beweist und die uralten Rätsel meistert, sichert sich seinen ehrenvollen Platz in den Chroniken von Hogwarts und trägt bis zu <strong>1.000 Hauspunkte</strong> für sein Haus davon!
</p>
<p class="letter-paragraph" id="letterParagraph3">
Halte deinen Zauberstab bereit, besinne dich auf deine Flüche und Gegenzauber und nimm dich vor verhexten Trugschlüssen in Acht. Wir erwarten deine Eule nicht später als mit dem nächsten Glockenschlag des Uhrenturms.
</p>
</div>
@@ -68,7 +71,7 @@
</div>
<!-- Handgezeichnetes Wachssiegel über der Klappenspitze -->
<div class="sketched-wax-seal" id="waxSeal" onclick="startGame()" title="Siegel brechen & Brief öffnen">
<div class="sketched-wax-seal" id="waxSeal" onclick="breakSealAndOpenLetter()" title="Siegel brechen & Brief öffnen">
<img src="/static/img/hogwarts_seal.jpg" alt="Hogwarts-Wappen Siegel" class="seal-crest-img">
<div class="seal-emboss-ring"></div>
<div class="seal-glow"></div>
@@ -77,7 +80,7 @@
</div>
<!-- Auswahlbereich unter dem Pergament -->
<div class="intro-content intro-controls-panel">
<div class="intro-content intro-controls-panel" id="introControls">
<p class="intro-subnote" id="introDesc">
Ein verzauberter Pergamentbrief aus Hogwarts liegt vor dir.<br>
Wähle deinen Schwierigkeitsgrad und brich das rote Zaubersiegel:
@@ -102,9 +105,19 @@
</div>
</div>
<button id="startBtn" class="btn btn-primary btn-large btn-break-seal" onclick="startGame()">
📜 Siegel brechen & Brief öffnen
</button>
<div class="intro-action-container">
<button id="breakSealBtn" class="btn btn-primary btn-large btn-break-seal" onclick="breakSealAndOpenLetter()">
📜 Siegel brechen & Brief öffnen
</button>
<div id="letterActions" class="letter-actions hidden">
<button id="startQuizBtn" class="btn btn-primary btn-large btn-launch-quiz glow-pulse" onclick="startQuizFromLetter()">
🚂 Hogwarts-Express besteigen & Prüfung beginnen ➔
</button>
<button id="closeLetterBtn" class="btn btn-secondary btn-close-letter" onclick="closeLetter()">
↩ Brief wieder schließen
</button>
</div>
</div>
</div>
</div>
@@ -170,5 +183,5 @@
{% endblock %}
{% block scripts %}
<script src="/static/js/quiz.js?v=2.5"></script>
<script src="/static/js/quiz.js?v=2.6"></script>
{% endblock %}