7915512195
Die Umstellung auf echte Menge hatte die EU-Priorität eingeebnet. Jetzt Stufen-
folge: EU-Nachbar (Hauptweg zu UNSEREM Markt, kurzer Weg) VOR fernem Welt-
Lieferant (nur wo es hier nicht wächst, z.B. Pfeffer→Vietnam+Brasilien) VOR
"Big Player, kommt kaum an" (world-far). Innerhalb einer Stufe zählt die Menge.
- MARKET_TIER {eu:0, global:1, world-far:2}
- explainChoice: EU-Antwort erklärt "aus EU-Binnenmarkt, kurzer Weg, kein Zoll";
world-far-Fehlklick erklärt "weltweit groß, aber kaum im EU-Handel"
- "in dieser Runde"-Texte entfernt (Auswahl ist jetzt deterministisch)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1626 lines
100 KiB
HTML
1626 lines
100 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Weltküche – Woher kommt mein Essen?</title>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css"/>
|
||
<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=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,700;12..96,800&family=Manrope:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root{
|
||
--bg:#EFEBE0;--bg2:#E6E0D1;--card:#FBF9F3;--ink:#2B2E27;--muted:#797B6E;--line:#D9D3C4;
|
||
--green:#5A7A52;--green-d:#3F5A39;--green-soft:#CBD9C0;
|
||
--clay:#C97B5A;--clay-soft:#EFD6CB;--gold:#D2A24C;--gold-soft:#F0E2C3;
|
||
--radius:20px;--shadow:0 6px 18px rgba(43,46,39,.07);font-size:16px;
|
||
}
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
html,body{height:100%}
|
||
body{font-family:'Manrope',sans-serif;background:var(--bg);color:var(--ink);line-height:1.5;-webkit-font-smoothing:antialiased}
|
||
h1,h2,h3,.display{font-family:'Bricolage Grotesque',sans-serif;letter-spacing:-.01em;line-height:1.1}
|
||
button{font-family:inherit;cursor:pointer;border:none}
|
||
.wrap{max-width:1180px;margin:0 auto;padding:0 18px}
|
||
|
||
header.top{position:sticky;top:0;z-index:600;background:rgba(239,235,224,.88);backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
|
||
.top-in{display:flex;align-items:center;gap:14px;padding:12px 0}
|
||
.logo{display:flex;align-items:center;gap:10px;font-family:'Bricolage Grotesque';font-weight:800;font-size:1.15rem}
|
||
.logo .globe{width:34px;height:34px;border-radius:11px;background:var(--green);color:#fff;display:grid;place-items:center;font-size:1.1rem}
|
||
.top-stats{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}
|
||
.pill{background:var(--card);border:1px solid var(--line);border-radius:999px;padding:7px 14px;font-weight:700;font-size:.85rem;display:flex;align-items:center;gap:7px}
|
||
.pill .v{color:var(--green-d)}
|
||
.ghostbtn{background:var(--card);border:1px solid var(--line);border-radius:999px;padding:7px 14px;font-weight:600;font-size:.85rem;color:var(--ink)}
|
||
.ghostbtn:hover{background:var(--bg2)}
|
||
|
||
.screen{display:none;animation:fade .4s ease}
|
||
.screen.active{display:block}
|
||
@keyframes fade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
||
|
||
.hero{padding:54px 0 30px;text-align:center}
|
||
.hero .kicker{display:inline-block;background:var(--green-soft);color:var(--green-d);font-weight:700;font-size:.8rem;padding:6px 14px;border-radius:999px;letter-spacing:.04em;text-transform:uppercase}
|
||
.hero h1{font-size:clamp(2.1rem,6vw,3.6rem);margin:18px 0 10px;font-weight:800}
|
||
.hero p.lead{max-width:640px;margin:0 auto;color:var(--muted);font-size:1.08rem}
|
||
.startcard{max-width:480px;margin:30px auto 0;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:26px;text-align:left}
|
||
.startcard label{font-weight:700;font-size:.9rem;display:block;margin-bottom:8px}
|
||
.startcard input{width:100%;padding:13px 15px;border-radius:13px;border:1px solid var(--line);background:#fff;font-family:inherit;font-size:1rem;color:var(--ink)}
|
||
.startcard input:focus{outline:none;border-color:var(--green)}
|
||
.btn{background:var(--green);color:#fff;font-weight:700;border-radius:13px;padding:14px 22px;font-size:1rem;transition:transform .12s,background .2s;width:100%;margin-top:16px}
|
||
.btn:hover{background:var(--green-d);transform:translateY(-1px)}
|
||
.btn.secondary{background:transparent;color:var(--green-d);border:1px solid var(--green);margin-top:10px}
|
||
.btn.secondary:hover{background:var(--green-soft);transform:none}
|
||
.howto{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;max-width:860px;margin:34px auto 0}
|
||
.howto .step{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:18px;text-align:center}
|
||
.howto .num{width:30px;height:30px;border-radius:9px;background:var(--gold-soft);color:#9b7320;font-weight:800;display:grid;place-items:center;margin:0 auto 10px}
|
||
.howto h4{font-family:'Bricolage Grotesque';font-size:1rem;margin-bottom:4px}
|
||
.howto p{font-size:.85rem;color:var(--muted)}
|
||
|
||
.section-head{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;margin:30px 0 18px;flex-wrap:wrap}
|
||
.section-head h2{font-size:1.8rem;font-weight:800}
|
||
.section-head p{color:var(--muted);font-size:.95rem}
|
||
.dishgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:16px;padding-bottom:40px}
|
||
.dishcard{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:20px;box-shadow:var(--shadow);text-align:left;transition:transform .14s,border-color .2s;position:relative;overflow:hidden}
|
||
.dishcard:hover{transform:translateY(-3px);border-color:var(--green)}
|
||
.dishcard .emoji{font-size:2.6rem;line-height:1}
|
||
.dishcard h3{font-size:1.25rem;margin:10px 0 2px}
|
||
.dishcard .origin{color:var(--muted);font-size:.85rem;font-weight:600}
|
||
.dishcard .tags{display:flex;gap:6px;margin-top:12px;flex-wrap:wrap}
|
||
.tag{background:var(--bg2);border-radius:999px;padding:4px 10px;font-size:.74rem;font-weight:600;color:var(--ink)}
|
||
.diff{position:absolute;top:16px;right:16px;font-size:.72rem;font-weight:700;color:var(--green-d);background:var(--green-soft);padding:4px 9px;border-radius:999px}
|
||
.done-flag{position:absolute;top:16px;right:16px;background:var(--gold);color:#fff;font-size:.72rem;font-weight:700;padding:4px 9px;border-radius:999px}
|
||
|
||
.game-grid{display:grid;grid-template-columns:320px 1fr;gap:14px;padding:14px 0 40px;align-items:start}
|
||
/* iPad-Landscape (1180×820): kompaktere linke Spalte, alles auf einen Blick */
|
||
@media(min-width:881px) and (max-width:1280px){
|
||
.game-grid{grid-template-columns:280px 1fr;gap:12px}
|
||
}
|
||
@media(max-width:880px){.game-grid{grid-template-columns:1fr}.howto{grid-template-columns:1fr}}
|
||
.panel{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}
|
||
.panel-pad{padding:20px}
|
||
.recipe-top{display:flex;align-items:center;gap:14px;border-bottom:1px solid var(--line);padding:18px 20px}
|
||
.recipe-top .emoji{font-size:2.4rem}
|
||
.recipe-top h2{font-size:1.35rem;font-weight:800}
|
||
.recipe-top .origin{color:var(--muted);font-size:.85rem;font-weight:600}
|
||
.ing-list{list-style:none}
|
||
.ing-item{display:flex;align-items:center;gap:10px;padding:6px 10px;border-radius:10px;margin-bottom:4px;border:1px solid transparent;background:var(--bg);transition:.2s;min-height:36px}
|
||
.ing-item .ic{font-size:1.25rem;width:22px;text-align:center;line-height:1}
|
||
.ing-item .nm{font-weight:700;font-size:.92rem;line-height:1.15}
|
||
.ing-item .sub{font-size:.72rem;color:var(--muted);display:none}
|
||
.ing-item.local{opacity:.7}
|
||
.ing-item.active{background:var(--green-soft);border-color:var(--green)}
|
||
.ing-item.solved{background:#fff;border-color:var(--line)}
|
||
.ing-item .nm{flex:1}
|
||
.ing-item .chk{margin-left:auto;font-weight:800;color:var(--green);font-size:.95rem}
|
||
.ing-item .cnt{margin-left:auto;font-size:.72rem;font-weight:700;color:var(--green-d);background:var(--green-soft);border-radius:999px;padding:2px 8px}
|
||
.ing-item .pantry-badge{margin-left:auto;font-size:.7rem;color:var(--muted);font-weight:600}
|
||
.task-banner{background:var(--gold-soft);border:1px solid #e6cf95;border-radius:14px;padding:13px 15px;margin:0 0 14px;font-size:.92rem}
|
||
.task-banner b{color:#8a6516}
|
||
.map-wrap{position:relative}
|
||
#map{height:540px;width:100%;border-radius:var(--radius);z-index:1;transition:height .55s cubic-bezier(.4,0,.2,1)}
|
||
/* Rundenabschluss: Karte schrumpft, damit Result-Card mehr Platz bekommt */
|
||
#map.solved{height:280px}
|
||
/* iPad-Landscape: niedrigere Karte, damit Ergebnis-Card direkt drunter sichtbar bleibt */
|
||
@media(min-width:881px) and (max-width:1280px){#map{height:420px} #map.solved{height:220px}}
|
||
@media(max-width:880px){#map{height:400px} #map.solved{height:200px}}
|
||
.map-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:0 0 12px}
|
||
.map-head h3{font-size:1.1rem}
|
||
.map-head .hint{font-size:.82rem;color:var(--muted)}
|
||
|
||
.cm-wrap{background:transparent;border:none}
|
||
.country-btn{position:relative;transform:translate(-50%,-50%);display:flex;align-items:center;gap:9px;background:#fff;border:2.5px solid var(--green);color:var(--ink);font-family:'Manrope';font-weight:700;font-size:.98rem;padding:11px 17px;border-radius:14px;white-space:nowrap;cursor:pointer;box-shadow:0 6px 16px rgba(43,46,39,.25);transition:background .15s,border-color .15s,transform .12s}
|
||
.country-btn .flag{font-size:1.35rem;line-height:1}
|
||
.country-btn:hover:not(:disabled){background:var(--green-soft);transform:translate(-50%,-50%) scale(1.07)}
|
||
.country-btn:active:not(:disabled){transform:translate(-50%,-50%) scale(.98)}
|
||
.country-btn.correct{background:var(--green);border-color:var(--green-d);color:#fff}
|
||
.country-btn.wrong{background:var(--clay);border-color:#a85a3c;color:#fff;opacity:.9}
|
||
.country-btn:disabled{cursor:default}
|
||
.leaflet-div-icon{background:transparent;border:none}
|
||
|
||
.result-card{margin-top:14px;border-radius:16px;padding:16px;border:1px solid var(--line);display:none}
|
||
.result-card.show{display:block;animation:fade .3s}
|
||
.result-card.ok{background:#fff;border-color:var(--green)}
|
||
.result-card.mid{background:#fff;border-color:var(--gold)}
|
||
.result-card.no{background:var(--clay-soft);border-color:var(--clay)}
|
||
.result-card .rc-head{font-family:'Bricolage Grotesque';font-weight:800;font-size:1.05rem;display:flex;align-items:flex-start;gap:8px;margin-bottom:6px;line-height:1.25}
|
||
.result-card.ok .rc-head{color:var(--green-d)}
|
||
.result-card.mid .rc-head{color:#8a6516}
|
||
.result-card.no .rc-head{color:#9c4d2e}
|
||
.econ{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:12px 0}
|
||
.econ .box{background:var(--bg);border-radius:12px;padding:11px 13px}
|
||
.econ .box .k{font-size:.72rem;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:.03em}
|
||
.econ .box .val{font-weight:700;font-size:.95rem;margin-top:2px}
|
||
.econ .box.price{background:var(--gold-soft)}
|
||
.econ .box.price .val{color:#8a6516}
|
||
.note{font-size:.88rem;background:var(--green-soft);border-radius:12px;padding:11px 13px;color:var(--green-d);margin-bottom:10px}
|
||
.result-card.no .note{background:#fff;color:var(--ink);margin-bottom:0}
|
||
.more-line{font-size:.86rem;color:var(--ink);background:var(--bg);border-radius:12px;padding:11px 13px;margin-bottom:10px}
|
||
.more-line b{color:var(--green-d)}
|
||
.regions{margin-bottom:10px}
|
||
.regions .rlabel,.econ-list .rlabel{font-size:.72rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:6px}
|
||
.region-item{display:flex;gap:9px;align-items:flex-start;font-size:.85rem;background:var(--bg);border-radius:12px;padding:9px 12px;margin-bottom:6px}
|
||
.econ-list{margin:10px 0}
|
||
.prod-row{display:flex;align-items:center;gap:10px;background:var(--bg);border-radius:10px;padding:6px 10px;margin-bottom:4px;font-size:.82rem}
|
||
.prod-row.match{background:var(--green-soft);font-weight:700;color:var(--green-d)}
|
||
.prod-row .prod-nm{flex:1;font-weight:600}
|
||
.prod-row .prod-val{font-variant-numeric:tabular-nums;font-size:.78rem;color:var(--muted);text-align:right}
|
||
.prod-row.match .prod-val{color:var(--green-d)}
|
||
|
||
/* Alle-Länder-dieser-Runde — am Ende jeder Zutat sehen Schüler:innen jede Karten-Option erklärt */
|
||
.all-choices{margin:10px 0 12px;padding:10px 12px;background:rgba(255,255,255,.55);border:1px solid var(--line);border-radius:12px}
|
||
.all-choices .rlabel{font-size:.72rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:8px}
|
||
.all-choice{display:grid;grid-template-columns:auto 1fr auto;gap:4px 10px;align-items:center;padding:8px 10px;margin-bottom:6px;border-radius:10px;background:var(--bg)}
|
||
.all-choice:last-child{margin-bottom:0}
|
||
.all-choice .cf{font-size:1.25rem;line-height:1}
|
||
.all-choice .cnm{font-weight:700;font-size:.88rem}
|
||
.all-choice .cb{font-weight:800;font-size:.95rem;justify-self:end}
|
||
.all-choice .ctxt{grid-column:2/-1;font-size:.78rem;color:#3a4a35;line-height:1.45;margin-top:1px}
|
||
.all-choice .clicked-tag{grid-column:2/-1;justify-self:start;font-size:.62rem;color:#a85a3c;font-weight:800;letter-spacing:.05em;text-transform:uppercase;background:rgba(168,90,60,.10);padding:2px 7px;border-radius:999px;margin-top:4px}
|
||
.all-choice.ok{background:rgba(76,124,78,.13)}
|
||
.all-choice.ok .cnm{color:var(--green-d)}
|
||
.all-choice.mid{background:rgba(210,162,76,.13)}
|
||
.all-choice.no{background:rgba(168,90,60,.10)}
|
||
.prod-more{font-size:.78rem;color:var(--muted);text-align:center;padding:4px}
|
||
.region-item .rf{font-size:1.2rem;line-height:1.2}
|
||
.region-item .rn{font-weight:700;display:block;margin-bottom:1px}
|
||
.next-row{display:flex;gap:10px;margin-top:4px}
|
||
.next-row .btn{margin-top:0}
|
||
|
||
.overlay{position:fixed;inset:0;background:rgba(43,46,39,.45);backdrop-filter:blur(3px);display:none;place-items:center;z-index:900;padding:18px}
|
||
.overlay.show{display:grid;animation:fade .25s}
|
||
.modal{background:var(--card);border-radius:24px;max-width:470px;width:100%;padding:30px;text-align:center;box-shadow:0 24px 60px rgba(0,0,0,.25);max-height:90vh;overflow:auto}
|
||
.modal .big-emoji{font-size:3.4rem}
|
||
.modal h2{font-size:1.6rem;font-weight:800;margin:8px 0}
|
||
.modal .scoreline{font-size:1.05rem;color:var(--green-d);font-weight:700;margin:4px 0 14px}
|
||
.fact{background:var(--gold-soft);border-radius:14px;padding:14px;font-size:.92rem;text-align:left;margin-bottom:14px}
|
||
.fact b{color:#8a6516}
|
||
.earned-badges{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-bottom:14px}
|
||
.bdg{width:66px;text-align:center}
|
||
.bdg .ring{width:54px;height:54px;border-radius:50%;background:var(--gold-soft);border:2px solid var(--gold);display:grid;place-items:center;font-size:1.5rem;margin:0 auto 5px}
|
||
.bdg .lbl{font-size:.66rem;font-weight:700;line-height:1.2}
|
||
|
||
.board{max-width:640px;margin:0 auto}
|
||
.rank-row{display:flex;align-items:center;gap:14px;background:var(--card);border:1px solid var(--line);border-radius:14px;padding:13px 16px;margin-bottom:9px}
|
||
.rank-row .pos{font-family:'Bricolage Grotesque';font-weight:800;font-size:1.2rem;width:34px;color:var(--muted)}
|
||
.rank-row.me{border-color:var(--green);background:#fff}
|
||
.rank-row .nm{font-weight:700}
|
||
.rank-row .sc{margin-left:auto;font-weight:800;color:var(--green-d)}
|
||
.rank-row .medal{font-size:1.3rem;width:34px;text-align:center}
|
||
.badge-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:14px;max-width:820px;margin:0 auto}
|
||
.badge-card{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:18px;text-align:center;transition:.2s}
|
||
.badge-card.locked{opacity:.45;filter:grayscale(.6)}
|
||
.badge-card .ring{width:60px;height:60px;border-radius:50%;display:grid;place-items:center;font-size:1.7rem;margin:0 auto 10px;background:var(--gold-soft);border:2px solid var(--gold)}
|
||
.badge-card.locked .ring{background:var(--bg2);border-color:var(--line)}
|
||
.badge-card h4{font-family:'Bricolage Grotesque';font-size:.98rem;margin-bottom:4px}
|
||
.badge-card p{font-size:.78rem;color:var(--muted)}
|
||
.center{text-align:center;padding:30px 0}
|
||
.footer-note{text-align:center;color:var(--muted);font-size:.78rem;padding:24px 0 40px;max-width:700px;margin:0 auto}
|
||
.progress-dots{display:flex;gap:6px;margin-top:10px}
|
||
.progress-dots .d{width:9px;height:9px;border-radius:50%;background:var(--line)}
|
||
.progress-dots .d.on{background:var(--green)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="top">
|
||
<div class="wrap top-in">
|
||
<a href="../../schueler.html" title="Zurück zum Cockpit" style="text-decoration:none;color:inherit;font-size:1.3rem;margin-right:6px">🏠</a>
|
||
<div class="logo"><span class="globe">🌍</span> Weltküche</div>
|
||
<div class="top-stats">
|
||
<span class="pill" id="pillName">👤 <span class="v" id="statName">–</span></span>
|
||
<span class="pill">⭐ <span class="v" id="statScore">0</span> Pkt</span>
|
||
<span class="pill">🏅 <span class="v" id="statBadges">0</span></span>
|
||
<button class="ghostbtn" onclick="show('badges')">Abzeichen</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<section class="screen active" id="screen-start">
|
||
<div class="wrap">
|
||
<div class="hero" style="padding:40px 0 20px">
|
||
<img id="wkSplashImg" alt="" style="max-width:360px;width:80%;border-radius:24px;box-shadow:var(--shadow)">
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Tutorial-Overlay: standardisierte Einführung wie in den anderen Sims -->
|
||
<div id="tutorial-overlay" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.55);z-index:9999;align-items:center;justify-content:center">
|
||
<div style="background:var(--card);max-width:560px;border-radius:20px;padding:28px 30px;margin:20px;box-shadow:0 10px 40px rgba(0,0,0,.3);max-height:88vh;overflow-y:auto">
|
||
<img id="tutorial-step-img" alt="" style="display:block;width:160px;height:160px;margin:0 auto 10px;border-radius:18px;background:var(--bg)">
|
||
<h2 id="tutorial-step-title" style="font-size:1.4rem;font-family:'Bricolage Grotesque';margin-bottom:14px;text-align:center;color:var(--green-d)">Willkommen!</h2>
|
||
<div id="tutorial-step-body" style="font-size:.96rem;line-height:1.55;color:var(--ink);margin-bottom:18px"></div>
|
||
<div style="display:flex;gap:10px;justify-content:space-between;align-items:center;margin-top:10px">
|
||
<button class="ghostbtn" onclick="skipTutorial()" style="font-size:.85rem">Überspringen</button>
|
||
<div id="tutorial-dots" style="display:flex;gap:6px"></div>
|
||
<button class="btn" id="tutorial-next" onclick="nextTutorialStep()" style="font-size:.92rem;width:auto;margin:0;padding:10px 18px">Weiter →</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<section class="screen" id="screen-dishes">
|
||
<div class="wrap">
|
||
<div class="section-head">
|
||
<div><h2>Wähle dein Gericht</h2><p id="dishCount">30 Gerichte aus aller Welt.</p></div>
|
||
<button class="ghostbtn" onclick="startTutorial()">ℹ️ Information</button>
|
||
</div>
|
||
<div id="dishFilter" style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:18px">
|
||
<button class="ghostbtn active" data-cat="all" onclick="setDishFilter('all')" style="background:var(--green);color:#fff;border-color:var(--green)">Alle</button>
|
||
<button class="ghostbtn" data-cat="regional-at" onclick="setDishFilter('regional-at')">🇦🇹 AT-regional</button>
|
||
<button class="ghostbtn" data-cat="dach" onclick="setDishFilter('dach')">🇦🇹🇩🇪🇨🇭 DACH</button>
|
||
<button class="ghostbtn" data-cat="europa" onclick="setDishFilter('europa')">🇪🇺 Europa</button>
|
||
<button class="ghostbtn" data-cat="exotisch" onclick="setDishFilter('exotisch')">🌍 Exotisch</button>
|
||
</div>
|
||
<div class="dishgrid" id="dishGrid"></div>
|
||
<p class="footer-note">Tipp: Die „falschen“ Länder sind kein Zufall. Frag dich: zu kalt? zu trocken? am Meer oder Binnenland? Manche Zutaten (z. B. Äpfel) kommen sogar von uns aus der Nähe.</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="screen" id="screen-recipe">
|
||
<div class="wrap" style="max-width:760px">
|
||
<div class="section-head" style="margin-bottom:6px">
|
||
<button class="ghostbtn" onclick="backToDishes()">← Gerichte</button>
|
||
</div>
|
||
<div style="text-align:center;margin:0 0 18px">
|
||
<button class="btn" style="max-width:420px;margin:0 auto" onclick="startResearch()">🌍 Jetzt Welt-Zutaten erforschen →</button>
|
||
</div>
|
||
<div class="panel panel-pad" id="recipe-body" style="background:var(--card)"></div>
|
||
<div style="text-align:center;margin:18px 0 40px">
|
||
<button class="btn" style="max-width:420px;margin:0 auto" onclick="startResearch()">🌍 Jetzt Welt-Zutaten erforschen →</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="screen" id="screen-game">
|
||
<div class="wrap">
|
||
<div class="section-head" style="margin-bottom:6px">
|
||
<button class="ghostbtn" onclick="backToDishes()">← Gerichte</button>
|
||
<div class="progress-dots" id="progressDots"></div>
|
||
</div>
|
||
<div class="game-grid">
|
||
<div class="panel">
|
||
<div class="recipe-top">
|
||
<span class="emoji" id="gEmoji">🍕</span>
|
||
<div><h2 id="gName">Gericht</h2><div class="origin" id="gOrigin">Herkunft</div></div>
|
||
</div>
|
||
<div class="panel-pad"><ul class="ing-list" id="ingList"></ul></div>
|
||
</div>
|
||
<div class="panel panel-pad">
|
||
<div class="task-banner" id="taskBanner">Suche: …</div>
|
||
<div class="map-head"><h3>Weltkarte</h3><span class="hint">tippe das/die Herkunftsland/-länder an</span></div>
|
||
<div class="map-wrap"><div id="map"></div></div>
|
||
<div class="result-card" id="resultCard"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="screen" id="screen-recipe-full">
|
||
<div class="wrap" style="max-width:760px">
|
||
<div class="section-head" style="margin-bottom:6px">
|
||
<button class="ghostbtn" onclick="show('dishes')">← Gerichte</button>
|
||
</div>
|
||
<div class="panel panel-pad" id="recipe-full-body" style="background:var(--card)"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="screen" id="screen-badges">
|
||
<div class="wrap">
|
||
<div class="section-head"><div><h2>Abzeichen</h2><p>Sammle alle durch Entdecken, Genauigkeit und Wissen.</p></div>
|
||
<button class="ghostbtn" onclick="backFromSub()">← Zurück</button></div>
|
||
<div class="badge-grid" id="badgeGrid"></div>
|
||
<div class="center"><button class="btn secondary" style="max-width:260px;margin:20px auto 0" onclick="backFromSub()">Weiter forschen</button></div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="overlay" id="overlay"><div class="modal" id="modalBox"></div></div>
|
||
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js"></script>
|
||
<script>
|
||
/* ====================== LÄNDER (mit Regionen-Info) ====================== */
|
||
let C = {
|
||
vietnam:{n:"Vietnam",f:"🇻🇳",lat:14.06,lng:108.28,info:"Südostasien, tropisches Monsunklima. Heute weltgrößter Pfeffer-Exporteur und Nr. 2 bei Kaffee."},
|
||
indien:{n:"Indien",f:"🇮🇳",lat:21.0,lng:78.0,info:"Riesiges Land mit vielen Klimazonen – vom tropischen Süden bis zum Himalaya. Einer der größten Agrarproduzenten der Welt."},
|
||
china:{n:"China",f:"🇨🇳",lat:35.0,lng:104.0,info:"Bevölkerungsreich, mit vielen Klimazonen. Weltgrößter Produzent vieler Lebensmittel: Reis, Erdnüsse, Äpfel, Chili."},
|
||
island:{n:"Island",f:"🇮🇸",lat:64.96,lng:-19.02,info:"Vulkaninsel knapp unter dem Polarkreis. Zu kalt für Ackerbau – Gemüse wächst nur in beheizten Gewächshäusern."},
|
||
kanada:{n:"Kanada",f:"🇨🇦",lat:56.0,lng:-106.0,info:"Riesig mit kalten Wintern. Im Süden (Prärie) wachsen aber Weizen und Raps auf gewaltigen Feldern."},
|
||
spanien:{n:"Spanien",f:"🇪🇸",lat:40.0,lng:-3.7,info:"Mittelmeerklima mit heißen, trockenen Sommern – perfekt für Oliven, Mandeln und Zitrusfrüchte."},
|
||
italien:{n:"Italien",f:"🇮🇹",lat:42.5,lng:12.5,info:"Vom Alpenraum bis zum heißen Süden. Bekannt für Oliven, Tomaten, Hartweizen und Wein."},
|
||
schweden:{n:"Schweden",f:"🇸🇪",lat:62.0,lng:16.0,info:"Nordeuropa, lange kalte Winter. Getreide und Beeren ja – tropische Gewürze unmöglich."},
|
||
polen:{n:"Polen",f:"🇵🇱",lat:52.0,lng:19.1,info:"Mitteleuropa, kühles Klima. Großer Produzent von Äpfeln, Getreide und Kartoffeln."},
|
||
norwegen:{n:"Norwegen",f:"🇳🇴",lat:62.0,lng:9.0,info:"Lange Küste, kühles Klima, viel Gebirge. Stark in der Fischerei – kaum Ackerbau."},
|
||
saudi:{n:"Saudi-Arabien",f:"🇸🇦",lat:24.0,lng:45.0,info:"Wüstenland mit extremer Hitze und kaum Regen. Landwirtschaft nur mit künstlicher Bewässerung."},
|
||
elfenbein:{n:"Elfenbeinküste",f:"🇨🇮",lat:7.5,lng:-5.5,info:"Westafrika, nahe am Äquator: heiß und feucht. Weltgrößter Kakao-Produzent."},
|
||
ghana:{n:"Ghana",f:"🇬🇭",lat:7.9,lng:-1.0,info:"Westafrika, tropisch. Nach der Elfenbeinküste der zweitgrößte Kakao-Produzent der Welt."},
|
||
deutschland:{n:"Deutschland",f:"🇩🇪",lat:51.0,lng:10.4,info:"Gemäßigtes Klima. Getreide, Zuckerrüben, Kartoffeln – aber keine tropischen Pflanzen."},
|
||
brasilien:{n:"Brasilien",f:"🇧🇷",lat:-10.0,lng:-52.0,info:"Größtes Land Südamerikas, viel Tropen. Nummer 1 bei Kaffee, Zucker, Orangen und Soja."},
|
||
finnland:{n:"Finnland",f:"🇫🇮",lat:63.0,lng:26.0,info:"Hoher Norden mit langen, kalten Wintern. Wald und Beeren statt Gewürzanbau."},
|
||
madagaskar:{n:"Madagaskar",f:"🇲🇬",lat:-19.0,lng:46.8,info:"Tropische Insel vor Afrika. Liefert rund drei Viertel der Welt-Vanille."},
|
||
indonesien:{n:"Indonesien",f:"🇮🇩",lat:-2.0,lng:117.0,info:"Tausende tropische Inseln am Äquator. Groß bei Kokos, Gewürzen, Palmöl und Kaffee."},
|
||
frankreich:{n:"Frankreich",f:"🇫🇷",lat:46.5,lng:2.2,info:"Vielfältiges Klima. Europas größter Agrarproduzent – Getreide, Wein, Milch."},
|
||
japan:{n:"Japan",f:"🇯🇵",lat:36.2,lng:138.2,info:"Inselstaat mit langen Küsten. Reis und Meeresprodukte wie Nori-Algen sind zentral."},
|
||
thailand:{n:"Thailand",f:"🇹🇭",lat:15.0,lng:101.0,info:"Südostasien, tropisch-feucht. Einer der größten Reis-Exporteure der Welt."},
|
||
philippinen:{n:"Philippinen",f:"🇵🇭",lat:12.9,lng:121.8,info:"Tropischer Inselstaat. Weltweit führend bei Kokosnüssen und Bananen."},
|
||
russland:{n:"Russland",f:"🇷🇺",lat:60.0,lng:90.0,info:"Riesig und überwiegend kalt. Im Süden Weizen, im Norden nur Wald und Tundra."},
|
||
srilanka:{n:"Sri Lanka",f:"🇱🇰",lat:7.9,lng:80.8,info:"Tropische Insel südlich von Indien. Heimat des echten Ceylon-Zimts und großer Tee-Produzent."},
|
||
guatemala:{n:"Guatemala",f:"🇬🇹",lat:15.5,lng:-90.2,info:"Mittelamerika, bergig und tropisch. Weltgrößter Exporteur von Kardamom."},
|
||
ecuador:{n:"Ecuador",f:"🇪🇨",lat:-1.8,lng:-78.2,info:"Liegt direkt am Äquator (daher der Name). Weltgrößter Bananen-Exporteur."},
|
||
usa:{n:"USA",f:"🇺🇸",lat:39.0,lng:-98.0,info:"Sehr groß, viele Klimazonen. Kalifornien liefert fast alle Mandeln der Welt; riesige Mais- und Sojafelder im Mittleren Westen."},
|
||
uk:{n:"Großbritannien",f:"🇬🇧",lat:54.0,lng:-2.5,info:"Kühles, feuchtes Inselklima. Tee wird viel getrunken, aber nicht angebaut."},
|
||
sudan:{n:"Sudan",f:"🇸🇩",lat:14.0,lng:30.0,info:"Heißes, trockenes Land in Nordostafrika. Einer der größten Sesam-Produzenten."},
|
||
suedkorea:{n:"Südkorea",f:"🇰🇷",lat:36.5,lng:127.8,info:"Halbinsel mit langer Küste. Wichtiger Produzent von Seetang (Nori) und Reis."},
|
||
oesterreich:{n:"Österreich",f:"🇦🇹",lat:47.6,lng:14.2,info:"Binnenland in den Alpen – kein Meereszugang. Milch, Getreide, Äpfel, aber kein Seetang."},
|
||
schweiz:{n:"Schweiz",f:"🇨🇭",lat:46.8,lng:8.2,info:"Alpines Binnenland ohne Meer. Bekannt für Milch und Käse, nicht für Meeresalgen."},
|
||
mexiko:{n:"Mexiko",f:"🇲🇽",lat:23.6,lng:-102.5,info:"Mittelamerika, viel Sonne und Wärme. Heimat von Avocado, Mais, Chili und Limette."},
|
||
};
|
||
|
||
/* ====================== ZUTATEN-BAUSTEINE ====================== */
|
||
let ING = {
|
||
pfeffer:{nm:"Schwarzer Pfeffer",ic:"🧂",correct:["vietnam","indien"],choices:["vietnam","indien","island","kanada"],
|
||
econ:{leader:"Vietnam",share:"~40 % des Welt-Pfefferexports",world:"~550.000 t / Jahr weltweit",price:"ca. 2–4 € / 100 g"},
|
||
note:"Pfeffer ist eine tropische Kletterpflanze und wächst nur nahe am Äquator. In Island oder Kanada ist es viel zu kalt.",
|
||
more:"Pfeffer war im Mittelalter so wertvoll, dass man ihn „schwarzes Gold“ nannte und damit sogar bezahlte."},
|
||
olivenoel:{nm:"Olivenöl",ic:"🫒",correct:["spanien","italien"],choices:["spanien","italien","schweden","polen"],
|
||
econ:{leader:"Spanien",share:"~45 % der Weltproduktion",world:"~3 Mio t Olivenöl / Jahr",price:"ca. 1 € / 100 ml"},
|
||
note:"Olivenbäume lieben das milde Mittelmeerklima. In Schweden oder Polen sind die Winter zu kalt.",
|
||
more:"Ein Olivenbaum kann über 1000 Jahre alt werden und trägt jedes Jahr aufs Neue Früchte."},
|
||
hartweizen:{nm:"Hartweizen (Teig)",ic:"🌾",correct:["kanada","italien"],choices:["kanada","italien","norwegen","saudi"],
|
||
econ:{leader:"Kanada",share:"weltgrößter Hartweizen-Exporteur (~50 % Welthandel)",world:"~770 Mio t Weizen / Jahr",price:"ca. 0,10–0,20 € / 100 g (Mehl)"},
|
||
note:"Weizen braucht gemäßigtes Klima mit genug Regen – nicht zu kalt wie Norwegen, nicht zu trocken wie Saudi-Arabien.",
|
||
more:"Hartweizen (Durum) ist besonders eiweißreich – darum hält Pasta beim Kochen ihre Form."},
|
||
tomaten:{nm:"Tomaten (Sauce)",ic:"🍅",correct:["italien","spanien"],choices:["italien","spanien","norwegen","island"],
|
||
econ:{leader:"Italien & Spanien (EU)",share:"China ist weltweit Nr. 1, in Europa führen Italien & Spanien",world:"~190 Mio t Tomaten / Jahr",price:"ca. 0,15–0,25 € / 100 g (Dose)"},
|
||
note:"Tomaten lieben Sonne und Wärme. Im hohen Norden wachsen sie nur im beheizten Gewächshaus.",
|
||
more:"Die berühmten Pizza- und Pastasaucen kommen meist aus süditalienischen Tomaten, etwa aus der Gegend um Neapel."},
|
||
kakao:{nm:"Kakao",ic:"🍫",correct:["elfenbein","ghana"],choices:["elfenbein","ghana","deutschland","kanada"],
|
||
econ:{leader:"Elfenbeinküste",share:"~40 % der Weltproduktion (mit Ghana ~60 %)",world:"~5 Mio t Kakaobohnen / Jahr",price:"ca. 1–2 € / 100 g (Kakaopulver)"},
|
||
note:"Der Kakaobaum wächst nur in heißen, feuchten Gebieten nahe am Äquator – Deutschland und Kanada sind zu kalt.",
|
||
more:"Aus einer Kakaofrucht kommen ~30–40 Bohnen; für 100 g Schokolade braucht man rund 100 Bohnen."},
|
||
rohrzucker:{nm:"Rohrzucker",ic:"🧊",correct:["brasilien","indien"],choices:["brasilien","indien","norwegen","finnland"],
|
||
econ:{leader:"Brasilien",share:"weltgrößter Zucker-Exporteur",world:"~180 Mio t Zucker / Jahr",price:"ca. 0,10 € / 100 g"},
|
||
note:"Zuckerrohr ist eine tropische Pflanze. (In Europa gewinnt man Zucker stattdessen aus Zuckerrüben.)",
|
||
more:"Aus Zuckerrohr macht Brasilien auch Biosprit (Ethanol) für Autos."},
|
||
vanille:{nm:"Vanille",ic:"🌼",correct:["madagaskar","indonesien"],choices:["madagaskar","indonesien","frankreich","japan"],
|
||
econ:{leader:"Madagaskar",share:"~75–80 % der Weltproduktion",world:"weltweit nur ~7.000 t / Jahr",price:"ca. 30–60 € / 100 g (echte Schoten)"},
|
||
note:"Vanille ist nach Safran das teuerste Gewürz der Welt – jede Blüte muss von Hand bestäubt werden.",
|
||
more:"Die Vanille-Orchidee blüht nur einen einzigen Tag im Jahr – darum ist echte Vanille so selten."},
|
||
reis:{nm:"Reis",ic:"🍚",correct:["indien","thailand"],choices:["indien","thailand","island","norwegen"],
|
||
econ:{leader:"Indien",share:"weltgrößter Reis-Exporteur (~40 % Welthandel)",world:"~520 Mio t / Jahr",price:"ca. 0,15–0,30 € / 100 g"},
|
||
note:"Reis wächst auf warmen, überfluteten Feldern. Island und Norwegen sind dafür viel zu kalt.",
|
||
more:"Für mehr als die Hälfte der Menschheit ist Reis das wichtigste Grundnahrungsmittel."},
|
||
kurkuma:{nm:"Kurkuma",ic:"🟡",correct:["indien"],choices:["indien","schweden","kanada","russland"],
|
||
econ:{leader:"Indien",share:"~75–80 % der Weltproduktion",world:"fast die ganze Welternte aus Südasien",price:"ca. 1–2 € / 100 g"},
|
||
note:"Kurkuma gibt dem Curry die gelbe Farbe und ist eine tropische Wurzel aus Südasien.",
|
||
more:"Kurkuma färbt so stark, dass es auch als natürlicher Lebensmittelfarbstoff (E100) verwendet wird."},
|
||
kokos:{nm:"Kokosmilch",ic:"🥥",correct:["indonesien","philippinen"],choices:["indonesien","philippinen","deutschland","russland"],
|
||
econ:{leader:"Indonesien",share:"Indonesien & Philippinen ~halbe Weltproduktion",world:"~60 Mio t Kokosnüsse / Jahr",price:"ca. 0,30 € / 100 ml"},
|
||
note:"Kokospalmen brauchen tropische Küsten: Sonne, Wärme und sandigen Boden.",
|
||
more:"Eine Kokospalme trägt bis zu 100 Nüsse pro Jahr – über viele Jahrzehnte hinweg."},
|
||
ingwer:{nm:"Ingwer",ic:"🫚",correct:["indien","china"],choices:["indien","china","norwegen","island"],
|
||
econ:{leader:"Indien",share:"Indien & China sind die größten Produzenten",world:"~4 Mio t / Jahr",price:"ca. 0,60–1,00 € / 100 g"},
|
||
note:"Ingwer ist eine tropische Wurzel und braucht Wärme und Feuchtigkeit zum Wachsen.",
|
||
more:"Ingwer wird seit über 3000 Jahren als Gewürz und Heilmittel verwendet."},
|
||
zimt:{nm:"Zimt",ic:"🪵",correct:["srilanka","indonesien"],choices:["srilanka","indonesien","schweden","deutschland"],
|
||
econ:{leader:"Sri Lanka",share:"Heimat des echten Ceylon-Zimts",world:"~250.000 t Zimt / Jahr",price:"ca. 2–4 € / 100 g"},
|
||
note:"Überraschung: Im schwedischen Klassiker steckt KEIN schwedischer Zimt – er kommt aus Sri Lanka, vom anderen Ende der Welt!",
|
||
more:"Zimt ist die getrocknete, gerollte Rinde eines Baumes – die typischen „Stangen“ sind eingerollte Rindenstücke."},
|
||
kardamom:{nm:"Kardamom",ic:"🫛",correct:["guatemala","indien"],choices:["guatemala","indien","norwegen","italien"],
|
||
econ:{leader:"Guatemala",share:"weltgrößter Kardamom-Exporteur",world:"~40.000 t / Jahr",price:"ca. 5–10 € / 100 g"},
|
||
note:"Kardamom ist eines der teuersten Gewürze der Welt (Platz 3 nach Safran und Vanille).",
|
||
more:"Kardamom kam erst um 1900 nach Guatemala – heute exportiert das Land mehr davon als die Ursprungsregion Indien."},
|
||
banane:{nm:"Banane",ic:"🍌",correct:["ecuador","philippinen"],choices:["ecuador","philippinen","deutschland","kanada"],
|
||
econ:{leader:"Ecuador",share:"weltgrößter Bananen-Exporteur (~25–30 %)",world:"~135 Mio t Bananen / Jahr",price:"ca. 0,15–0,25 € / 100 g"},
|
||
note:"Bananen brauchen ganzjährig tropische Wärme – deshalb reisen sie tausende Kilometer zu uns.",
|
||
more:"Bananen werden grün geerntet und reifen auf dem langen Schiffsweg nach Europa nach."},
|
||
mandeln:{nm:"Mandeln",ic:"🌰",correct:["usa","spanien"],choices:["usa","spanien","norwegen","russland"],
|
||
econ:{leader:"USA",share:"Kalifornien: ~75–80 % der Weltproduktion",world:"~1,4 Mio t Mandeln / Jahr",price:"ca. 1,50–2,50 € / 100 g"},
|
||
note:"Fast alle Mandeln wachsen im sonnigen Kalifornien und rund ums Mittelmeer.",
|
||
more:"Mandelbäume brauchen Bienen zur Bestäubung – in Kalifornien werden dafür Milliarden Bienen herangekarrt."},
|
||
kaffee:{nm:"Kaffee",ic:"☕",correct:["brasilien","vietnam"],choices:["brasilien","vietnam","uk","schweden"],
|
||
econ:{leader:"Brasilien",share:"~35 % der Weltproduktion (Vietnam Nr. 2)",world:"~10 Mio t Rohkaffee / Jahr",price:"ca. 1–2 € / 100 g (Bohnen)"},
|
||
note:"Lustig: Schweden trinkt am meisten Kaffee pro Kopf – angebaut wird dort aber keine einzige Bohne.",
|
||
more:"Kaffee ist nach Erdöl eine der meistgehandelten Rohstoff-Waren der Welt."},
|
||
orange:{nm:"Orangensaft",ic:"🍊",correct:["brasilien","spanien"],choices:["brasilien","spanien","norwegen","island"],
|
||
econ:{leader:"Brasilien",share:"weltgrößter Orangen-Produzent; in Europa führt Spanien",world:"~75 Mio t Orangen / Jahr",price:"ca. 0,15–0,30 € / 100 ml (Saft)"},
|
||
note:"Orangen sind Zitrusfrüchte – sie brauchen warmes, frostfreies Klima.",
|
||
more:"Fast die Hälfte aller Orangen wird zu Saft verarbeitet; Brasilien ist der größte Saft-Exporteur."},
|
||
sesam:{nm:"Sesam",ic:"⚪",correct:["indien","sudan"],choices:["indien","sudan","island","norwegen"],
|
||
econ:{leader:"Sudan / Indien",share:"gehören zu den größten Produzenten weltweit",world:"~6,5 Mio t Sesam / Jahr",price:"ca. 0,80–1,50 € / 100 g"},
|
||
note:"Sesam liebt Hitze und Trockenheit – deshalb wächst er in Afrika und Asien, nicht im kalten Norden.",
|
||
more:"Aus Sesam wird die Paste Tahini gemacht – Grundzutat von Hummus."},
|
||
soja:{nm:"Sojabohnen (Sojasauce)",ic:"🫘",correct:["usa","brasilien"],choices:["usa","brasilien","island","saudi"],
|
||
econ:{leader:"Brasilien",share:"Brasilien & USA ~zwei Drittel der Weltproduktion",world:"~390 Mio t Soja / Jahr",price:"ca. 0,40 € / 100 ml (Sojasauce)"},
|
||
note:"Soja braucht viel Fläche und mildes bis warmes Klima – Island ist zu kalt, Saudi-Arabien zu trocken.",
|
||
more:"Der größte Teil der Welt-Sojaernte wird gar nicht von Menschen gegessen, sondern als Tierfutter verwendet."},
|
||
nori:{nm:"Nori (Seetang)",ic:"🟢",correct:["japan","suedkorea"],choices:["japan","suedkorea","oesterreich","schweiz"],
|
||
econ:{leader:"Japan / Südkorea",share:"fast die gesamte Welt-Noriproduktion",world:"Millionen Tonnen Algen / Jahr",price:"ca. 5–10 € / 100 g (Noriblätter)"},
|
||
note:"Nori ist eine Meeresalge. Österreich und die Schweiz sind Binnenländer ohne Meer – dort kann gar kein Seetang wachsen!",
|
||
more:"Nori wird auf Netzen im flachen Küstenwasser „angebaut“ und wie Gemüse geerntet."},
|
||
avocado:{nm:"Avocado",ic:"🥑",correct:["mexiko"],choices:["mexiko","norwegen","island","polen"],
|
||
econ:{leader:"Mexiko",share:"~30 % der Weltproduktion, weltgrößter Exporteur",world:"~9 Mio t / Jahr",price:"ca. 0,80–1,20 € / 100 g"},
|
||
note:"Avocados wachsen in warmen, frostfreien Regionen – Mexiko ist das Avocado-Land Nr. 1.",
|
||
more:"Avocadobäume brauchen sehr viel Wasser – in trockenen Anbaugebieten ist das ein Umweltproblem."},
|
||
limette:{nm:"Limette",ic:"🟢",correct:["mexiko","indien"],choices:["mexiko","indien","norwegen","finnland"],
|
||
econ:{leader:"Mexiko / Indien",share:"größte Produzenten von Limetten & Zitronen",world:"~20 Mio t Limetten & Zitronen / Jahr",price:"ca. 0,30–0,50 € / 100 g"},
|
||
note:"Wie alle Zitrusfrüchte braucht die Limette Wärme und verträgt keinen Frost.",
|
||
more:"Limettensaft hält angeschnittene Avocado grün – die Säure bremst das Braunwerden."},
|
||
mais:{nm:"Mais (Tortilla)",ic:"🌽",correct:["usa","brasilien"],choices:["usa","brasilien","island","saudi"],
|
||
econ:{leader:"USA",share:"USA & Brasilien sind große Mais-Exporteure",world:"~1,2 Mrd t Mais / Jahr",price:"ca. 0,10–0,20 € / 100 g (Mehl)"},
|
||
note:"Mais stammt ursprünglich aus Mexiko, wächst heute aber weltweit – nur nicht im ewigen Eis oder in der reinen Wüste.",
|
||
more:"Mais ist mengenmäßig das meistangebaute Getreide der Welt – noch vor Weizen und Reis."},
|
||
erdnuss:{nm:"Erdnüsse",ic:"🥜",correct:["china","indien"],choices:["china","indien","norwegen","island"],
|
||
econ:{leader:"China",share:"China & Indien sind die größten Produzenten",world:"~50 Mio t / Jahr",price:"ca. 0,40–0,80 € / 100 g"},
|
||
note:"Erdnüsse reifen UNTER der Erde und brauchen warmen, lockeren Boden.",
|
||
more:"Botanisch sind Erdnüsse keine Nüsse, sondern Hülsenfrüchte – verwandt mit Erbsen und Bohnen."},
|
||
chili:{nm:"Chili",ic:"🌶️",correct:["indien","china"],choices:["indien","china","norwegen","finnland"],
|
||
econ:{leader:"Indien",share:"Indien ist weltgrößter Chili-Produzent und -Exporteur",world:"~40 Mio t Chilis & Paprika / Jahr",price:"ca. 1–3 € / 100 g (getrocknet)"},
|
||
note:"Chilis lieben Hitze – im kühlen Norden reifen sie kaum aus.",
|
||
more:"Die Schärfe kommt vom Stoff Capsaicin; gemessen wird sie in Scoville-Einheiten."},
|
||
kichererbsen:{nm:"Kichererbsen",ic:"🟤",correct:["indien"],choices:["indien","norwegen","island","schweden"],
|
||
econ:{leader:"Indien",share:"~70 % der Weltproduktion",world:"~15 Mio t / Jahr",price:"ca. 0,20–0,40 € / 100 g"},
|
||
note:"Kichererbsen mögen Wärme und Trockenheit – im nassen, kalten Norden gedeihen sie schlecht.",
|
||
more:"Aus Kichererbsen werden Hummus und Falafel gemacht – Grundnahrung im Nahen Osten."},
|
||
kumin:{nm:"Kreuzkümmel",ic:"🌿",correct:["indien"],choices:["indien","norwegen","kanada","polen"],
|
||
econ:{leader:"Indien",share:"größter Produzent und Exporteur",world:"Hauptanbau in Indien & Vorderasien",price:"ca. 1–2 € / 100 g"},
|
||
note:"Kreuzkümmel braucht ein heißes, trockenes Klima zum Reifen.",
|
||
more:"Vorsicht: Kreuzkümmel wird oft mit Kümmel verwechselt – schmeckt aber ganz anders."},
|
||
apfel:{nm:"Äpfel",ic:"🍎",correct:["oesterreich","polen"],choices:["oesterreich","polen","saudi","island"],
|
||
econ:{leader:"Polen (EU) / China (Welt)",share:"China ist weltweit Nr. 1, Polen größter EU-Produzent",world:"~95 Mio t Äpfel / Jahr",price:"ca. 0,15–0,30 € / 100 g"},
|
||
note:"Äpfel mögen es gemäßigt, mit kühlem Winter. In der Wüste oder Arktis wachsen sie nicht.",
|
||
more:"Ein heimischer Apfel reist oft nur wenige Kilometer – ganz anders als Vanille oder Kakao vom anderen Ende der Welt."},
|
||
};
|
||
|
||
/* ====================== GERICHTE ======================
|
||
* WICHTIG: Die 30 Gerichte werden aus assets/data/dishes.json geladen.
|
||
* Vorher 10 Inline-Gerichte → ersetzt durch async-Load (siehe loadWeltkuecheData).
|
||
* Zusätzliche Länder + Zutaten werden aus countries-extras.json + ingredients-extras.json gemerged.
|
||
*/
|
||
let DISHES = [];
|
||
let _basePath = ''; // wird vom Wrapper-PHP gesetzt (BASE_PATH/sims/weltkueche/)
|
||
|
||
/* ====================== SFX (ElevenLabs-Sounds) ======================
|
||
* Lazy-load, gecached pro Name, defensive — fällt still bei Fehlern.
|
||
* Lautstärke aus localStorage (Default 0.55); Schnellklicks via cloneNode. */
|
||
const _sfxCache = {};
|
||
function _sfxVolume() {
|
||
try {
|
||
const v = parseFloat(localStorage.getItem('wk-sfx-vol'));
|
||
return isNaN(v) ? 0.55 : Math.max(0, Math.min(1, v));
|
||
} catch (_) { return 0.55; }
|
||
}
|
||
function playSfx(name) {
|
||
try {
|
||
if (!_basePath) return;
|
||
let a = _sfxCache[name];
|
||
if (!a) {
|
||
a = new Audio(_basePath + 'sounds/' + name + '.mp3');
|
||
a.preload = 'auto';
|
||
_sfxCache[name] = a;
|
||
}
|
||
// Wenn schon läuft (z. B. schneller Doppelklick): Clone für überlappende Wiedergabe
|
||
if (!a.paused && a.currentTime > 0.05) {
|
||
const clone = a.cloneNode();
|
||
clone.volume = _sfxVolume();
|
||
clone.play().catch(()=>{});
|
||
return;
|
||
}
|
||
a.currentTime = 0;
|
||
a.volume = _sfxVolume();
|
||
a.play().catch(()=>{});
|
||
} catch (_) {}
|
||
}
|
||
window.playSfx = playSfx;
|
||
|
||
async function loadWeltkuecheData() {
|
||
const base = (window.WK_BASE || './');
|
||
const [dishesJ, countriesFull, prodMatrix, transportJ] = await Promise.all([
|
||
fetch(base + 'assets/data/dishes.json').then(r => r.json()),
|
||
fetch(base + 'assets/data/countries-full.json').then(r => r.json()),
|
||
fetch(base + 'assets/data/production-matrix.json').then(r => r.json()),
|
||
fetch(base + 'assets/data/transport.json').then(r => r.json()).catch(() => ({})),
|
||
]);
|
||
// Länder-Map komplett aus countries-full ersetzen (überschreibt frühere inline-Defaults)
|
||
Object.entries(countriesFull).forEach(([k, v]) => { C[k] = v; });
|
||
// ING-Map aus production-matrix bauen
|
||
Object.entries(prodMatrix).forEach(([k, v]) => {
|
||
ING[k] = {
|
||
nm: v.nm, ic: v.ic,
|
||
producers: v.producers || {},
|
||
climate_possible: v.climate_possible || [],
|
||
fact: v.fact || '',
|
||
more: v.more || ''
|
||
};
|
||
});
|
||
DISHES = Object.entries(dishesJ)
|
||
.filter(([k]) => !k.startsWith('_'))
|
||
.map(([id, d]) => Object.assign({id}, d));
|
||
window.WK_TRANSPORT = transportJ;
|
||
_basePath = base;
|
||
}
|
||
|
||
/* ========== KLIMA-BILANZ: pro Gericht CO₂-Berechnung der Welt-Zutaten ========== */
|
||
function calculateClimateImpact(dish) {
|
||
const T = window.WK_TRANSPORT || {};
|
||
const factors = (T._meta && T._meta.co2_factors_g_per_tkm) || {schiff:8, bahn:30, lkw:80, flugzeug:500};
|
||
let total = 0;
|
||
const details = [];
|
||
(dish.ings || []).forEach(ingKey => {
|
||
const ing = ING[ingKey];
|
||
if (!ing || !ing.correct || !ing.correct[0]) return;
|
||
const country = ing.correct[0];
|
||
const t = T[country];
|
||
if (!t) return;
|
||
const mass_kg = 0.05; // 50 g pro Welt-Zutat (didaktischer Mittelwert)
|
||
const factor = factors[t.typical] || 80;
|
||
const co2 = t.distance_km * mass_kg * factor / 1000; // → g CO₂
|
||
total += co2;
|
||
details.push({
|
||
ing: ing.nm,
|
||
country: (C[country] && C[country].n) || country,
|
||
flag: (C[country] && C[country].f) || '',
|
||
mode: t.typical,
|
||
dist: t.distance_km,
|
||
co2: Math.round(co2)
|
||
});
|
||
});
|
||
details.sort((a, b) => b.co2 - a.co2);
|
||
return { total: Math.round(total), details };
|
||
}
|
||
|
||
function climateImpactBlock(dish) {
|
||
const imp = calculateClimateImpact(dish);
|
||
if (!imp.details.length) return '';
|
||
const modeIcon = { schiff: '🚢', bahn: '🚂', lkw: '🚚', flugzeug: '✈️' };
|
||
let categoryRef = '';
|
||
if (dish.category === 'regional-at') categoryRef = '🏔 typischer AT-regionaler Wert: <b>30–80 g</b>';
|
||
else if (dish.category === 'dach') categoryRef = '🏘 typischer DACH-Wert: <b>80–200 g</b>';
|
||
else if (dish.category === 'europa') categoryRef = '🇪🇺 typischer Europa-Wert: <b>200–500 g</b>';
|
||
else if (dish.category === 'exotisch') categoryRef = '🌍 typischer Exotik-Wert: <b>500–1500 g</b>';
|
||
// Bewertung
|
||
let badge = '🌱', verdict = 'sehr klimafreundlich';
|
||
if (imp.total >= 500) { badge = '🔥'; verdict = 'klimaintensiv — viel Welthandel'; }
|
||
else if (imp.total >= 200) { badge = '⚠️'; verdict = 'mittlerer Klima-Footprint'; }
|
||
else if (imp.total >= 80) { badge = '🌤'; verdict = 'noch okay, einige Welt-Zutaten'; }
|
||
return `<div style="background:var(--green-soft);border-radius:14px;padding:14px;margin:14px 0">
|
||
<h3 style="font-size:.96rem;margin-bottom:8px">🌍 Klimabilanz der Welt-Zutaten</h3>
|
||
<div style="font-size:1.6rem;font-weight:800;color:var(--green-d);margin-bottom:4px">${badge} ${imp.total} g CO₂ pro Portion</div>
|
||
<div style="font-size:.84rem;color:var(--muted);margin-bottom:10px">${verdict}${categoryRef?' · '+categoryRef:''}</div>
|
||
<table style="width:100%;font-size:.82rem;border-collapse:collapse">
|
||
<thead><tr style="text-align:left;color:var(--muted)"><th style="padding:4px 0">Zutat</th><th>Land</th><th>Weg</th><th style="text-align:right">CO₂</th></tr></thead>
|
||
<tbody>
|
||
${imp.details.map(r => `<tr style="border-top:1px solid rgba(0,0,0,.08)">
|
||
<td style="padding:4px 0">${esc(r.ing)}</td>
|
||
<td>${r.flag} ${esc(r.country)}</td>
|
||
<td>${modeIcon[r.mode]||''} ${r.dist} km</td>
|
||
<td style="text-align:right;font-variant-numeric:tabular-nums"><b>${r.co2} g</b></td>
|
||
</tr>`).join('')}
|
||
</tbody>
|
||
</table>
|
||
<div style="margin-top:10px;font-size:.78rem;color:var(--muted)">
|
||
<b>So gerechnet:</b> 50 g pro Welt-Zutat × Strecke × Transport-Faktor (Schiff 8, Bahn 30, Lkw 80, Flugzeug 500 g CO₂ pro Tonnen-Kilometer).
|
||
</div>
|
||
${dish.category === 'exotisch' || imp.total > 300 ? `<div style="margin-top:10px;background:#fff;border-radius:10px;padding:10px 12px;font-size:.84rem">
|
||
<b>💭 Was meinst du?</b> Lokale Küche hat winzigen CO₂-Fußabdruck, aber begrenzte Vielfalt.
|
||
Globale Küche bringt Vitamin-/Geschmacksvielfalt — kostet aber 10–30× mehr Klima.
|
||
Wo ist deine Balance?
|
||
</div>` : ''}
|
||
</div>`;
|
||
}
|
||
|
||
const BADGES = [
|
||
{id:"first", icon:"🌱",name:"Erste Zutat", desc:"Dein erstes richtiges Herkunftsland gefunden."},
|
||
{id:"pair", icon:"🔗",name:"Doppel-Forscher", desc:"Beide Herkunftsländer einer Zutat entdeckt."},
|
||
{id:"spice", icon:"🧂",name:"Gewürz-Kenner", desc:"8 Herkunftsländer korrekt verortet."},
|
||
{id:"climate",icon:"🌡️",name:"Klima-Detektiv", desc:"3 Zutaten komplett ohne Fehler gelöst."},
|
||
{id:"perfect",icon:"🎯",name:"Volltreffer-Koch", desc:"Ein Gericht ganz ohne Fehlklick gelöst."},
|
||
{id:"dish", icon:"🍽️",name:"Weltkoch", desc:"Dein erstes Gericht abgeschlossen."},
|
||
{id:"landlk", icon:"🗺️",name:"Geografie-Genie", desc:"Erkannt: Binnenland hat kein Meer (Nori)."},
|
||
{id:"trade", icon:"📈",name:"Welthandels-Profi",desc:"Über 3000 Punkte gesammelt."},
|
||
{id:"explorer",icon:"🌐",name:"Kontinent-Hopper",desc:"5 Gerichte abgeschlossen."},
|
||
{id:"star", icon:"⭐",name:"Sterne-Koch", desc:"Alle Gerichte abgeschlossen."},
|
||
];
|
||
|
||
/* ====================== STATE ====================== */
|
||
let state={name:"",score:0,badges:[],completed:[],totalCorrect:0,cleanIngredients:0};
|
||
let map,markerLayer,current;
|
||
const store={get(k,d){try{const v=localStorage.getItem(k);return v?JSON.parse(v):d;}catch(e){return d;}},set(k,v){try{localStorage.setItem(k,JSON.stringify(v));}catch(e){}}};
|
||
|
||
/* ====================== NAVIGATION ====================== */
|
||
let lastScreen="dishes";
|
||
function show(id){
|
||
if(id!=="badges") lastScreen=id;
|
||
document.querySelectorAll('.screen').forEach(s=>s.classList.remove('active'));
|
||
document.getElementById('screen-'+id).classList.add('active');
|
||
if(id==='badges') renderBadges();
|
||
window.scrollTo({top:0,behavior:'smooth'});
|
||
}
|
||
function backFromSub(){show(lastScreen);}
|
||
function backToDishes(){show('dishes');}
|
||
async function startGame(){
|
||
// Erst die JSON-Daten async laden (30 Gerichte + Country/Ingredient-Extras + Transport)
|
||
if (!DISHES.length) {
|
||
try { await loadWeltkuecheData(); }
|
||
catch (e) { alert('Daten konnten nicht geladen werden: ' + e.message); return; }
|
||
}
|
||
// Name aus Plattform-Session — kein Eingabefeld mehr
|
||
state.name = (window.WK_STUDENT_NAME && window.WK_STUDENT_NAME !== 'Gast') ? window.WK_STUDENT_NAME : 'Forscher:in';
|
||
document.getElementById('statName').textContent = state.name;
|
||
document.getElementById('dishCount').textContent = DISHES.length + ' Gerichte aus aller Welt.';
|
||
renderDishes();
|
||
// Onboarding kommt IMMER — auch bei wiederholtem Aufruf
|
||
startTutorial();
|
||
}
|
||
// Auto-Start: kein "Losforschen"-Button mehr nötig.
|
||
window.addEventListener('DOMContentLoaded', () => {
|
||
// Splash-Bild aus Plattform-Assets (existiert als WebP, fällt sonst still aus)
|
||
const splash = document.getElementById('wkSplashImg');
|
||
if (splash && window.WK_API_BASE) {
|
||
const apiBase = window.WK_API_BASE.replace(/\/api$/,'');
|
||
splash.src = apiBase + '/assets/img/card-weltkueche.webp';
|
||
splash.onerror = () => { splash.style.display = 'none'; };
|
||
}
|
||
startGame();
|
||
});
|
||
|
||
/* Glossar-Link-Helfer: erzeugt einen Inline-Tooltip-Trigger (Plattform-Standard).
|
||
* Wird beim Klick durch GlossarTooltip.js zum kleinen Popup mit Kurztext + Link
|
||
* auf den vollen Glossar-Eintrag. Fallback: externer Tab auf Glossar-Page, falls
|
||
* GlossarTooltip nicht geladen wurde. */
|
||
function glossarLink(label, slug) {
|
||
return `<span class="ggs-g" data-glossar="${slug}" style="font-weight:700;color:var(--green-d)">${label}</span>`;
|
||
}
|
||
window.glossarLink = glossarLink;
|
||
|
||
/* ====================== TUTORIAL ====================== */
|
||
/* TUTORIAL_STEPS werden in startTutorial() befüllt, damit die Inline-Glossar-Links
|
||
* (window.glossarLink) zur Laufzeit auf den richtigen Plattform-Pfad gehen. */
|
||
let TUTORIAL_STEPS = [];
|
||
function buildTutorialSteps() {
|
||
const g = (label, slug) => glossarLink(label, slug);
|
||
const imgBase = (window.WK_BASE || './') + 'assets/img/onboarding/';
|
||
TUTORIAL_STEPS = [
|
||
{ img: imgBase + 'woher.webp',
|
||
title: 'Woher kommt mein Essen?',
|
||
html: 'Pizza, Sushi, Apfelstrudel, Curry, Gröstl, Tortilla — viele Mahlzeiten auf deinem Teller haben eine <b>überraschende Reise</b> hinter sich, manche bleiben fast vor der Haustür.<br><br>'
|
||
+ 'In dieser Forschungs-Werkstatt erkundest du <b>30 Gerichte aus aller Welt</b>: ihre Zutaten, ihre Rezepte und ihren ' + g('Klima-Fußabdruck', 'co2-fussabdruck-lebensmittel') + '.' },
|
||
{ img: imgBase + 'forschung.webp',
|
||
title: 'Was mache ich jetzt?',
|
||
html: '<div style="text-align:left;display:inline-block;line-height:1.55;font-size:.95em">'
|
||
+ '<b>1. Gericht wählen</b> — du bekommst zuerst das Rezept mit Schritten, Nährwerten und Zubereitungs-Zeit.<br><br>'
|
||
+ '<b>2. Welt-Zutaten erforschen</b> — pro Welt-Zutat (z. B. Pfeffer, Kakao, Vanille) wählst du auf der Weltkarte das richtige Herkunftsland an. Hast du das erste Land gefunden, kannst du das zweite wählen.</div>' },
|
||
{ img: imgBase + 'forschung.webp',
|
||
title: 'Was lerne ich dabei?',
|
||
html: '<div style="text-align:left;display:inline-block;line-height:1.55;font-size:.95em">'
|
||
+ '<b>Wirtschaft & Geografie</b> — pro Treffer erfährst du, wer Weltmarktführer im ' + g('Welthandel', 'welthandel') + ' ist, wie groß die ' + g('Welternte', 'welternte') + ' ist und was es bei uns kostet.<br><br>'
|
||
+ '<b>' + g('Klimabilanz', 'co2-fussabdruck-lebensmittel') + '</b> — am Ende siehst du, wie weit die Zutaten gereist sind.</div>' },
|
||
{ img: imgBase + 'markt.webp',
|
||
title: 'Was genau ist gesucht?',
|
||
html: '<b>Gesucht ist nicht der größte Produzent der Welt</b> — sondern woher die Zutaten kommen, die wir hier bei uns auf dem Markt finden.<br><br>'
|
||
+ 'Vier Kategorien helfen dir:<br>'
|
||
+ '<div style="text-align:left;display:inline-block;margin:8px 0;line-height:1.55;font-size:.95em">'
|
||
+ '<b>🇪🇺 Hauptlieferant</b> — kommt typisch zu uns auf den Markt<br>'
|
||
+ '<b>🌐 Welt-Hauptproduzent</b> — Weltmarkt-Big-Player, kommt bei uns aber selten an<br>'
|
||
+ '<b>🌍 Off-Season-Lieferant</b> — füllt nur unsere Winter-Saison<br>'
|
||
+ '<b>🏠 Eigenkonsum / regional</b> — bleibt im eigenen Land</div><br>'
|
||
+ 'Manche Länder produzieren <i>viel</i>, sind bei uns aber nicht im Regal — etwa weil sie alles selbst verbrauchen oder weil der Transport zu lang ist.' },
|
||
{ img: imgBase + 'punkte.webp',
|
||
title: 'Punkte & Abzeichen',
|
||
html: 'Jedes richtig zugeordnete Land bringt <b>Punkte</b>. Doppel-Treffer (zweites Herkunftsland) zählen extra.<br><br>'
|
||
+ 'Es gibt 10 <b>Abzeichen</b> zum Sammeln — z. B. <b>Volltreffer-Koch</b> (Gericht ohne Fehler), <b>Geografie-Genie</b> (erkennen, dass ein Binnenland kein Meer hat) oder <b>Sterne-Koch</b> (alle 30 Gerichte).' },
|
||
{ img: imgBase + 'klima.webp',
|
||
title: 'Klimabilanz im Blick',
|
||
html: 'Bei jedem Gericht siehst du, <b>wie viel CO₂</b> die Reise der Zutaten verursacht. Transport-Wege sind ein Riesen-Unterschied: per Schiff sehr klein, per Flugzeug sehr groß. Mehr dazu: ' + g('CO₂-Fußabdruck von Lebensmitteln', 'co2-fussabdruck-lebensmittel') + '.<br><br>'
|
||
+ 'Vergleiche: Tiroler Gröstl ≈ 50 g CO₂, Sushi-Rolle ≈ 900 g.<br><br>'
|
||
+ 'Am Schluss denken wir gemeinsam nach: ' + g('Saisonal & Regional', 'saisonal-regional') + ' vs. ' + g('Welthandel', 'welthandel') + ' — wo ist deine Balance?' },
|
||
{ img: imgBase + 'glossar.webp',
|
||
title: 'Begriffe nachschlagen',
|
||
html: 'Du stößt in der Simulation auf Fachbegriffe? Klick einfach drauf — sie öffnen das Glossar:<br><br>'
|
||
+ '<div style="display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin:10px 0">'
|
||
+ g('Welthandel', 'welthandel') + g('Klimazone', 'klimazone') + g('Monokultur', 'monokultur')
|
||
+ g('Fair Trade', 'fair-trade') + g('Saisonal & Regional', 'saisonal-regional') + g('CO₂-Fußabdruck', 'co2-fussabdruck-lebensmittel')
|
||
+ '</div>'
|
||
+ '<div style="text-align:center;margin-top:14px"><a href="' + (window.WK_API_BASE || '').replace(/\/api$/,'') + '/pages/glossar.php?module=weltkueche" target="_blank" rel="noopener" style="display:inline-block;background:var(--green);color:#fff;text-decoration:none;border-radius:10px;padding:10px 16px;font-weight:700">📖 Alle Weltküche-Begriffe öffnen →</a></div>' }
|
||
];
|
||
}
|
||
let _tutorialIdx = 0;
|
||
function startTutorial() {
|
||
_tutorialIdx = 0;
|
||
if (!TUTORIAL_STEPS.length) buildTutorialSteps();
|
||
document.getElementById('tutorial-overlay').style.display = 'flex';
|
||
renderTutorialStep();
|
||
}
|
||
function renderTutorialStep() {
|
||
const step = TUTORIAL_STEPS[_tutorialIdx];
|
||
const imgEl = document.getElementById('tutorial-step-img');
|
||
if (imgEl) { imgEl.src = step.img; imgEl.alt = step.title; }
|
||
document.getElementById('tutorial-step-title').textContent = step.title;
|
||
document.getElementById('tutorial-step-body').innerHTML = step.html;
|
||
// Dots
|
||
const dots = TUTORIAL_STEPS.map((_, i) =>
|
||
'<span style="width:8px;height:8px;border-radius:50%;background:' + (i === _tutorialIdx ? 'var(--green)' : 'rgba(0,0,0,.15)') + '"></span>').join('');
|
||
document.getElementById('tutorial-dots').innerHTML = dots;
|
||
// Last step → "Los geht's!"
|
||
document.getElementById('tutorial-next').textContent = (_tutorialIdx >= TUTORIAL_STEPS.length - 1) ? 'Los geht\'s! →' : 'Weiter →';
|
||
}
|
||
function nextTutorialStep() {
|
||
if (_tutorialIdx >= TUTORIAL_STEPS.length - 1) {
|
||
closeTutorial();
|
||
show('dishes');
|
||
} else {
|
||
_tutorialIdx++;
|
||
renderTutorialStep();
|
||
}
|
||
}
|
||
function skipTutorial() {
|
||
closeTutorial();
|
||
show('dishes');
|
||
}
|
||
function closeTutorial() {
|
||
document.getElementById('tutorial-overlay').style.display = 'none';
|
||
store.set('wk_tutorial_done', true);
|
||
}
|
||
window.startTutorial = startTutorial;
|
||
window.nextTutorialStep = nextTutorialStep;
|
||
window.skipTutorial = skipTutorial;
|
||
// Plattform-Convention: GGS_TUTORIAL (siehe live-client.js).
|
||
window.GGS_TUTORIAL = { simId: 'weltkueche', title: 'Welt-Küche', replay: function() { startTutorial(); } };
|
||
function updateHUD(){
|
||
document.getElementById('statScore').textContent=state.score;
|
||
document.getElementById('statBadges').textContent=state.badges.length;
|
||
}
|
||
|
||
// Live-State für Lehreransicht (alle ~4 s vom Plattform-live-client.js abgefragt).
|
||
window.GGS_LIVE_STATE = function() {
|
||
if (!state) return null;
|
||
const dish = current && current.dish;
|
||
const ingTot = dish ? (dish.ings||[]).length : 0;
|
||
const ingDone = current ? (current.solved||[]).length : 0;
|
||
const completedDish = (state.completed||[]).length;
|
||
const totalDish = DISHES.length;
|
||
// progressPct = Fortschritt im aktuellen Gericht (für generische Vergleichs-Anzeige);
|
||
// wenn kein Gericht offen, Anteil der bereits gelösten Gerichte
|
||
const dishPct = ingTot > 0 ? Math.round(ingDone/ingTot*100) : 0;
|
||
const overallPct = totalDish > 0 ? Math.round(completedDish/totalDish*100) : 0;
|
||
return {
|
||
phase: dish ? (current && current.solved ? 'forschen' : ('Zutat ' + (ingDone+1) + '/' + ingTot)) : ('Auswahl · ' + completedDish + '/' + totalDish),
|
||
progressPct: dish ? dishPct : overallPct,
|
||
orderIndex: ingDone, // für generische Vergleichs-Anzeige (Zutat-Index)
|
||
totalOrders: ingTot,
|
||
dishId: dish ? dish.id : null,
|
||
dishName: dish ? dish.name : null,
|
||
dishCategory: dish ? dish.category : null,
|
||
dishOrigin: dish ? dish.origin : null,
|
||
ingTotal: ingTot,
|
||
ingSolved: ingDone,
|
||
ingCurrent: (current && current.dish && current.idx<current.dish.ings.length) ? current.dish.ings[current.idx] : null,
|
||
mistakesInDish: current ? (current.mistakesInDish||0) : 0,
|
||
score: state.score || 0,
|
||
completed: completedDish,
|
||
completedTotal: totalDish,
|
||
badgesEarned: (state.badges||[]).length,
|
||
badgesTotal: BADGES ? BADGES.length : 10,
|
||
levelWon: completedDish >= totalDish,
|
||
};
|
||
};
|
||
|
||
/* ====================== DISH GRID ====================== */
|
||
let _activeFilter = 'all';
|
||
function renderDishes(){
|
||
const g=document.getElementById('dishGrid'); g.innerHTML="";
|
||
const filtered = DISHES.filter(d => _activeFilter === 'all' || d.category === _activeFilter);
|
||
filtered.forEach(d=>{
|
||
const done=state.completed.includes(d.id);
|
||
const el=document.createElement('button');
|
||
el.className="dishcard"; el.onclick=()=>openDish(d.id);
|
||
const imgPath = d.imagePath ? (_basePath + d.imagePath) : null;
|
||
const catBadge = ({
|
||
'regional-at': '🇦🇹 AT-regional',
|
||
'dach': '🇦🇹🇩🇪🇨🇭 DACH',
|
||
'europa': '🇪🇺 Europa',
|
||
'exotisch': '🌍 Exotisch'
|
||
})[d.category] || '';
|
||
el.innerHTML=`${done?'<span class="done-flag">✓ Gelöst</span>':'<span class="diff">'+d.diff+'</span>'}
|
||
${imgPath ? `<img src="${imgPath}" alt="${d.name}" style="width:100%;height:140px;object-fit:cover;border-radius:14px;margin-bottom:10px;background:var(--bg2)" onerror="this.style.display='none'">` : `<div class="emoji">${d.emoji}</div>`}
|
||
<h3>${d.name}</h3>
|
||
<div class="origin">📍 ${d.origin}</div>
|
||
${d.diff_note?'<div style="font-size:.74rem;color:var(--muted);font-style:italic;margin-top:4px">'+d.diff_note+'</div>':''}
|
||
<div class="tags" style="margin-top:10px">
|
||
${catBadge?'<span class="tag" style="background:var(--green-soft);color:var(--green-d);font-weight:700">'+catBadge+'</span>':''}
|
||
${d.tags.map(t=>'<span class="tag">'+t+'</span>').join('')}
|
||
<span class="tag">${d.ings.length} Welt-Zutaten</span>
|
||
</div>`;
|
||
g.appendChild(el);
|
||
});
|
||
}
|
||
function setDishFilter(cat) {
|
||
_activeFilter = cat;
|
||
document.querySelectorAll('#dishFilter button').forEach(b => b.classList.toggle('active', b.dataset.cat === cat));
|
||
renderDishes();
|
||
}
|
||
window.setDishFilter = setDishFilter;
|
||
|
||
/* ====================== GAME ====================== */
|
||
function openDish(id){
|
||
const dish=DISHES.find(d=>d.id===id);
|
||
current={dish,idx:0,solved:[],dishScore:0,mistakesInDish:0,found:[],wrongHere:0,wrongStreak:0};
|
||
// Aufgabe-Cache reset pro Gericht — neue Korrekt-Auswahl + neue Distraktoren
|
||
_taskCache = {};
|
||
showRecipeScreen(dish);
|
||
}
|
||
|
||
function showRecipeScreen(d) {
|
||
// Vor-Forschungs-Ansicht: NUR Bild + Header + Nährwerte + Rezept-Schritte + Tipp.
|
||
// Lehrplan-Bezug ("Wenn dich das interessiert") + Trivia + Klimabilanz kommen
|
||
// erst NACH dem Forschen auf dem recipe-full-Screen.
|
||
const host = document.getElementById('recipe-body');
|
||
const imgPath = d.imagePath ? (_basePath + d.imagePath) : null;
|
||
const n = d.nutrition || {};
|
||
const r = d.recipe || {};
|
||
let html = '';
|
||
if (imgPath) {
|
||
html += `<img src="${imgPath}" alt="${d.name}" style="width:100%;max-height:280px;object-fit:cover;border-radius:18px;margin-bottom:18px;background:var(--bg2)" onerror="this.style.display='none'">`;
|
||
}
|
||
html += `<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">
|
||
<span style="font-size:2.4rem">${d.emoji}</span>
|
||
<div><h2 style="font-size:1.6rem;margin:0">${d.name}</h2>
|
||
<div style="color:var(--muted);font-size:.92rem">📍 ${d.origin} · <b>${d.diff}</b>${d.diff_note?' · <em>'+d.diff_note+'</em>':''}</div>
|
||
</div>
|
||
</div>`;
|
||
|
||
// Nährwerte
|
||
if (Object.keys(n).length) {
|
||
html += `<div style="background:var(--bg2);border-radius:14px;padding:14px;margin:16px 0">
|
||
<h3 style="font-size:.92rem;margin-bottom:10px">📊 Nährwerte pro Portion (${n.portion_g||'?'} g)</h3>
|
||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;font-size:.86rem">
|
||
<span>kcal: <b>${n.kcal||'–'}</b></span>
|
||
<span>Eiweiß: <b>${n.protein_g||'–'} g</b></span>
|
||
<span>Kohlenhydrate: <b>${n.carbs_g||'–'} g</b></span>
|
||
<span>Fett: <b>${n.fat_g||'–'} g</b></span>
|
||
<span>Ballaststoffe: <b>${n.fiber_g||'–'} g</b></span>
|
||
<span>Salz: <b>${n.salt_g||'–'} g</b></span>
|
||
</div>
|
||
</div>`;
|
||
}
|
||
|
||
// Rezept-Schritte (inkl. Tipp) — Schluss vor dem Forschen.
|
||
if (r.steps && r.steps.length) {
|
||
html += `<div style="margin:16px 0 0">
|
||
<h3 style="font-size:1rem;margin-bottom:8px">👩🍳 So machst du es (${r.portions||'?'} Portionen, ${r.time_min||'?'} min)</h3>
|
||
<ol style="padding-left:1.4rem;line-height:1.55;font-size:.92rem">${r.steps.map(s=>'<li style="margin-bottom:6px">'+esc(s)+'</li>').join('')}</ol>
|
||
${r.tipp?'<div style="background:var(--gold-soft);border-radius:10px;padding:10px 12px;font-size:.86rem;margin-top:10px"><b style="color:#8a6516">💡 Tipp:</b> '+esc(r.tipp)+'</div>':''}
|
||
</div>`;
|
||
}
|
||
|
||
host.innerHTML = html;
|
||
show('recipe');
|
||
}
|
||
|
||
function startResearch() {
|
||
const d = current.dish;
|
||
document.getElementById('gEmoji').textContent = d.emoji;
|
||
document.getElementById('gName').textContent = d.name;
|
||
document.getElementById('gOrigin').textContent = "📍 " + d.origin;
|
||
show('game'); renderIngList();
|
||
setTimeout(()=>{ initMap(); loadIngredient(); }, 80);
|
||
}
|
||
|
||
function esc(s){return String(s||'').replace(/[<>&"]/g,c=>({'<':'<','>':'>','&':'&','"':'"'}[c]));}
|
||
function initMap(){
|
||
if(!map){
|
||
map=L.map('map',{worldCopyJump:true,minZoom:1,maxZoom:6,scrollWheelZoom:true}).setView([25,15],2);
|
||
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',
|
||
{attribution:'© OpenStreetMap-Mitwirkende · © CARTO',subdomains:'abcd',maxZoom:6}).addTo(map);
|
||
markerLayer=L.layerGroup().addTo(map);
|
||
}
|
||
setTimeout(()=>map.invalidateSize(),120);
|
||
}
|
||
function ingOf(i){return ING[current.dish.ings[i]];}
|
||
// Zählt nur die Welt-Zutaten (für Progress-Dots & Beendigungs-Logik)
|
||
function worldIngCount(dish){
|
||
let n = 0;
|
||
dish.ings.forEach(k => { if (ING[k] && isWorldIngredient(k)) n++; });
|
||
return n;
|
||
}
|
||
|
||
/* Eine Zutat zählt als "Welt-Zutat" für das Spiel, wenn sie mindestens
|
||
* 3 Producer mit nennenswerten Tonnen-Werten hat. Alles andere ist eine
|
||
* Spiesekammer-Sache (Hefe, Essig, Schmand, kulturelle Erzeugnisse) —
|
||
* wird im Game als "lokal verfügbar" angezeigt, aber nicht erforscht. */
|
||
function isWorldIngredient(key) {
|
||
const ing = ING[key]; if (!ing) return false;
|
||
const real = Object.values(ing.producers || {}).filter(p => p.t && p.t > 0);
|
||
return real.length >= 3;
|
||
}
|
||
|
||
/* ====================== AUFGABEN-GENERIERUNG ======================
|
||
* Neue Architektur (ab production-matrix.json):
|
||
*
|
||
* Jede Welt-Zutat hat:
|
||
* producers Land → {t, share, note} (nennenswerte Marktproduktion)
|
||
* climate_possible [land, …] (theoretisch möglich, kein Hauptmarkt)
|
||
*
|
||
* Pro Aufgabe wählen wir:
|
||
* - 2 ZUFÄLLIGE producers als "Spiel-Slot-Korrekt" — gewichtet nach Tonnage
|
||
* (damit Hauptproduzenten häufiger drankommen, aber nicht ausschließlich).
|
||
* - 2-4 Distraktoren aus dem Rest: gemischt aus climate_possible (für
|
||
* differenziertes Feedback) und klar-unmöglichen Ländern.
|
||
*
|
||
* Beim Klick gibt es drei Feedback-Stufen:
|
||
* 1) IM SPIEL-SLOT → "Treffer!" mit Mengen-Info
|
||
* 2) PRODUZENT ABER nicht im Slot → "Auch ein echter Produzent — gerade nicht gefragt"
|
||
* 3) climate_possible → "Theoretisch möglich (passende Klimazone), aber kein Hauptmarkt"
|
||
* 4) sonst → "Klimatisch nicht möglich — hier wächst das nicht"
|
||
*/
|
||
let _taskCache = {}; // pro ing-key → {correct:[c1,c2], choices:[…]}
|
||
|
||
function shuffleInPlace(a) {
|
||
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 weightedSample(weighted, k) {
|
||
// weighted: [{key, weight}, …] → liefert k unterschiedliche Keys, weighted-random gezogen
|
||
const items = weighted.filter(w => w.weight > 0);
|
||
const out = [];
|
||
for (let n = 0; n < k && items.length; n++) {
|
||
const total = items.reduce((s, x) => s + x.weight, 0);
|
||
let r = Math.random() * total;
|
||
let i = 0;
|
||
while (r > items[i].weight) { r -= items[i].weight; i++; }
|
||
out.push(items[i].key);
|
||
items.splice(i, 1);
|
||
}
|
||
return out;
|
||
}
|
||
|
||
// Liefert ein Set aller in C{} bekannten Country-Slugs außer correct/excluded.
|
||
function impossibleCountries(corr, possible) {
|
||
const exclude = new Set([...corr, ...possible]);
|
||
const out = [];
|
||
Object.keys(C).forEach(c => { if (!exclude.has(c)) out.push(c); });
|
||
return out;
|
||
}
|
||
|
||
function buildTaskFor(ingKey) {
|
||
if (_taskCache[ingKey]) return _taskCache[ingKey];
|
||
const ing = ING[ingKey];
|
||
if (!ing) return null;
|
||
// HEIMAT-REGEL: In einem regionalen Gericht kommt jede Zutat, die das
|
||
// Herkunftsland selbst erzeugt, von dort — logisch nachvollziehbar (ein
|
||
// Tiroler Gröstl nutzt Tiroler Speck, Tiroler Kartoffeln …). Der Test
|
||
// „erzeugt das Heimatland diese Zutat?" filtert automatisch: Österreich
|
||
// baut Kartoffeln an (→ Österreich), aber keinen Pfeffer und kein Olivenöl
|
||
// (→ dort greift die normale Welt-Logik weiter unten). Kein Länder-Bias,
|
||
// weil die Antwort am Gericht hängt, nicht am Standort der App.
|
||
const _hDish = current && current.dish;
|
||
const heimat = _hDish && _hDish.heimat;
|
||
if (heimat && C[heimat] && ing.producers && ing.producers[heimat]) {
|
||
const correct = [heimat];
|
||
const others = Object.entries(ing.producers)
|
||
.filter(([c]) => C[c] && c !== heimat)
|
||
.sort((a, b) => (b[1].t || 0) - (a[1].t || 0))
|
||
.map(([c]) => c);
|
||
const climatePossible = (ing.climate_possible || []).filter(c => C[c] && c !== heimat);
|
||
const distr = others.slice(0, 3);
|
||
if (climatePossible.length && distr.length < 3) distr.push(climatePossible[0]);
|
||
const choices = [...correct, ...distr];
|
||
shuffleInPlace(choices);
|
||
const label = (_hDish.signature && _hDish.signature[ingKey]) || null;
|
||
_taskCache[ingKey] = { correct, choices, possible: new Set(), signature: true, heimat, label };
|
||
return _taskCache[ingKey];
|
||
}
|
||
// Alle Producer (für Klassifizierung) und EU-relevante separat (für Korrekt-Auswahl).
|
||
// Eigenkonsum-Riesen (Indien-Bananen, USA-Zitronen etc.) sind zwar echte Produzenten,
|
||
// landen aber nicht in unseren Supermärkten — also nicht als „Spiel-Slot-Korrekt" ziehen.
|
||
// Gewichts-Faktoren pro market-Kategorie:
|
||
// eu = AT-Nachbar (4×) — wird in regionalen Produkten klar bevorzugt
|
||
// global = außer-EU aber in AT-Märkten (1×) — Standard für Welthandelsprodukte
|
||
// world-far = Big Player am Weltmarkt, bei uns aber selten (0.4×) — kommt nur
|
||
// ab und zu dran, mit Zusatztext bei Auflösung
|
||
// off-season = Süd-Hemisphäre, nur EU-Winter (0× = nicht im Korrekt-Slot)
|
||
// local/us-only/asia-only = NICHT in EU-Markt (0× = nicht im Korrekt-Slot,
|
||
// kommen nur als Distraktor + producer-local-Feedback)
|
||
const WEIGHT_BY_MARKET = { 'eu': 4, 'global': 1, 'world-far': 0.4 };
|
||
const allProducers = Object.entries(ing.producers || {})
|
||
.filter(([c, info]) => C[c])
|
||
.map(([c, info]) => {
|
||
const market = info.market || 'global';
|
||
const baseWeight = Math.max(1, Math.log10((info.t || 1000) + 10));
|
||
const mult = WEIGHT_BY_MARKET[market] ?? 0;
|
||
return {key: c, weight: baseWeight * mult, market};
|
||
});
|
||
// Korrekt-Slot-Pool = alles mit positivem Gewicht (eu/global/world-far)
|
||
const euRelevant = allProducers.filter(p => p.weight > 0);
|
||
const sourcePool = euRelevant.length >= 2 ? euRelevant : allProducers;
|
||
const climatePossible = (ing.climate_possible || []).filter(c => C[c]);
|
||
|
||
// Die tatsächlichen HAUPTLIEFERANTEN für UNSEREN Markt — nicht gewürfelt.
|
||
// Stufenfolge: EU-Nachbar (kurzer Weg, kein Zoll → für uns der Hauptweg) VOR
|
||
// fernem Welt-Lieferant (nur wo es hier nicht wächst, z. B. Pfeffer) VOR
|
||
// „Big Player, der bei uns kaum ankommt". Innerhalb einer Stufe zählt die
|
||
// Menge. So bleibt DACH/EU priorisiert; Pfeffer (kein EU-Produzent) → die
|
||
// zwei größten Welt-Lieferanten Vietnam + Brasilien.
|
||
const MARKET_TIER = { 'eu': 0, 'global': 1, 'world-far': 2 };
|
||
const rankedPool = [...sourcePool].sort((a, b) =>
|
||
((MARKET_TIER[a.market] ?? 2) - (MARKET_TIER[b.market] ?? 2)) ||
|
||
(((ing.producers[b.key] || {}).t || 0) - ((ing.producers[a.key] || {}).t || 0)));
|
||
const correct = rankedPool.slice(0, Math.min(2, rankedPool.length)).map(p => p.key);
|
||
|
||
// 2-4 Distraktoren: 1-2 aus climate_possible (für didaktisches Feedback),
|
||
// Rest aus klar-unmöglichen
|
||
const distrCount = 2 + Math.floor(Math.random() * 3); // 2 bis 4
|
||
const possiblePool = climatePossible.filter(c => !correct.includes(c));
|
||
const possiblePicks = [...possiblePool];
|
||
shuffleInPlace(possiblePicks);
|
||
const possibleN = Math.min(Math.ceil(distrCount / 2), possiblePicks.length);
|
||
const fromPossible = possiblePicks.slice(0, possibleN);
|
||
|
||
const impossiblePool = impossibleCountries(correct, [...fromPossible, ...allProducers.map(p => p.key)]);
|
||
shuffleInPlace(impossiblePool);
|
||
const fromImpossible = impossiblePool.slice(0, distrCount - fromPossible.length);
|
||
|
||
const choices = [...correct, ...fromPossible, ...fromImpossible];
|
||
shuffleInPlace(choices);
|
||
|
||
_taskCache[ingKey] = { correct, choices, possible: new Set(fromPossible) };
|
||
return _taskCache[ingKey];
|
||
}
|
||
|
||
// Returns 'correct' | 'producer-eu-elsewhere' | 'producer-local' | 'climate-possible' | 'impossible'
|
||
function classifyGuess(ingKey, key) {
|
||
const task = _taskCache[ingKey];
|
||
const ing = ING[ingKey];
|
||
if (!task || !ing) return 'impossible';
|
||
if (task.correct.includes(key)) return 'correct';
|
||
if (ing.producers && ing.producers[key]) {
|
||
const m = ing.producers[key].market || 'global';
|
||
if (m === 'global' || m === 'eu') return 'producer-eu-elsewhere';
|
||
return 'producer-local'; // produziert, aber nicht für EU-Markt
|
||
}
|
||
if (ing.climate_possible && ing.climate_possible.includes(key)) return 'climate-possible';
|
||
return 'impossible';
|
||
}
|
||
|
||
function renderIngList(){
|
||
const d=current.dish,list=document.getElementById('ingList'); list.innerHTML="";
|
||
const worldIdx = [], pantryIdx = [];
|
||
d.ings.forEach((key, i) => {
|
||
if (!ING[key]) return;
|
||
if (isWorldIngredient(key)) worldIdx.push(i); else pantryIdx.push(i);
|
||
});
|
||
// Welt-Zutaten oben
|
||
worldIdx.forEach(i => {
|
||
const key = d.ings[i]; const ing=ING[key];
|
||
const solved=current.solved.includes(i); const active=i===current.idx&&!solved;
|
||
const task = active ? buildTaskFor(key) : (_taskCache[key] || null);
|
||
const targetCount = task ? task.correct.length : 2;
|
||
const twoFlag = targetCount > 1;
|
||
const li=document.createElement('li');
|
||
li.className="ing-item"+(solved?" solved":"")+(active?" active":"");
|
||
let right = solved?'<span class="chk">✓</span>':'';
|
||
if(active && twoFlag){ right='<span class="cnt">'+current.found.length+' / '+targetCount+'</span>'; }
|
||
li.innerHTML=`<span class="ic">${ing.ic}</span><span class="nm">${ing.nm}</span>${right}`;
|
||
list.appendChild(li);
|
||
});
|
||
// Spiesekammer-Block (lokal, kein Welt-Anbau)
|
||
if (pantryIdx.length) {
|
||
const hdr = document.createElement('li');
|
||
hdr.style.cssText = 'list-style:none;font-size:.7rem;color:var(--muted);font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:10px 0 4px;border-top:1px dashed var(--line);margin-top:8px';
|
||
hdr.textContent = '🏠 Lokal aus der Speisekammer';
|
||
list.appendChild(hdr);
|
||
pantryIdx.forEach(i => {
|
||
const key = d.ings[i]; const ing=ING[key];
|
||
const li=document.createElement('li');
|
||
li.className="ing-item solved";
|
||
li.style.opacity = '.65';
|
||
li.innerHTML=`<span class="ic">${ing.ic}</span><span class="nm">${ing.nm}</span><span class="pantry-badge">lokal</span>`;
|
||
list.appendChild(li);
|
||
});
|
||
}
|
||
const dots=document.getElementById('progressDots'); dots.innerHTML="";
|
||
worldIdx.forEach(i=>{const s=document.createElement('span');s.className="d"+(current.solved.includes(i)?" on":"");dots.appendChild(s);});
|
||
}
|
||
|
||
function loadIngredient(){
|
||
const d=current.dish;
|
||
// Spiesekammer-Zutaten überspringen (Hefe, Essig, Schmand, kulturelle Erzeugnisse)
|
||
while (current.idx < d.ings.length) {
|
||
if (current.solved.includes(current.idx)) { current.idx++; continue; }
|
||
const k = d.ings[current.idx];
|
||
if (!ING[k] || !isWorldIngredient(k)) {
|
||
current.solved.push(current.idx);
|
||
current.idx++;
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
if(current.idx>=d.ings.length){ finishDish(); return; }
|
||
const ingKey = d.ings[current.idx];
|
||
const ing=ingOf(current.idx);
|
||
// Aufgabe frisch bauen (Cache leeren). Die Korrekt-Auswahl ist bewusst
|
||
// deterministisch: Heimat-Regel bzw. echte Hauptlieferanten nach Menge.
|
||
delete _taskCache[ingKey];
|
||
const task = buildTaskFor(ingKey);
|
||
current.found=[]; current.wrongHere=0; current.wrongStreak=0; current.attempted=new Set();
|
||
const two = task && task.correct.length > 1;
|
||
const frage = two
|
||
? 'finde die <b>zwei</b> Herkunftsländer aus dieser Auswahl'
|
||
: (task && task.signature
|
||
? 'aus welchem Land stammt diese Zutat in <b>diesem Gericht</b>?'
|
||
: 'aus welchem Land kommt diese Zutat häufig zu uns auf den Markt?');
|
||
document.getElementById('taskBanner').innerHTML=
|
||
`🔎 Suche: <b>${ing.nm}</b> – ${frage}`;
|
||
document.getElementById('resultCard').className="result-card";
|
||
drawMarkers(ing); renderIngList();
|
||
}
|
||
|
||
function drawMarkers(ing){
|
||
if(!map||!markerLayer) initMap();
|
||
markerLayer.clearLayers(); current.markers={};
|
||
const pts=[];
|
||
const ingKey = current.dish.ings[current.idx];
|
||
const task = buildTaskFor(ingKey);
|
||
const choices = task ? task.choices : [];
|
||
choices.forEach(key=>{
|
||
const c=C[key]; if(!c) return;
|
||
pts.push([c.lat,c.lng]);
|
||
const icon=L.divIcon({className:'cm-wrap',
|
||
html:`<button class="country-btn" data-key="${key}"><span class="flag">${c.f}</span><span class="nm">${c.n}</span></button>`,
|
||
iconSize:[0,0],iconAnchor:[0,0]});
|
||
const m=L.marker([c.lat,c.lng],{icon,riseOnHover:true,zIndexOffset:200}).addTo(markerLayer);
|
||
current.markers[key]=m;
|
||
m.getElement().querySelector('.country-btn').addEventListener('click',e=>{e.stopPropagation();guess(key);});
|
||
});
|
||
if(pts.length) map.fitBounds(pts,{paddingTopLeft:[70,80],paddingBottomRight:[70,50],maxZoom:4});
|
||
}
|
||
function btnFor(key){const m=current.markers[key];return(m&&m.getElement())?m.getElement().querySelector('.country-btn'):null;}
|
||
|
||
function guess(key){
|
||
const b=btnFor(key); if(!b||b.disabled) return;
|
||
const ingKey = current.dish.ings[current.idx];
|
||
const ing=ingOf(current.idx);
|
||
const c=C[key];
|
||
const cls = classifyGuess(ingKey, key);
|
||
const task = _taskCache[ingKey];
|
||
if (current.attempted) current.attempted.add(key);
|
||
playSfx('click-country');
|
||
if (cls === 'correct') {
|
||
b.classList.add('correct');
|
||
const pts = current.wrongStreak===0?100:60;
|
||
state.score+=pts; current.dishScore+=pts;
|
||
state.totalCorrect++; current.wrongStreak=0; current.found.push(key);
|
||
map.flyTo([c.lat,c.lng],3,{duration:.7});
|
||
const targetCount = task.correct.length;
|
||
if(current.found.length >= targetCount){
|
||
let bonus=0;
|
||
if(targetCount > 1){ bonus=50; state.score+=bonus; current.dishScore+=bonus; awardLive(['pair']); }
|
||
if(current.wrongHere===0) state.cleanIngredients++;
|
||
current.solved.push(current.idx);
|
||
Object.keys(current.markers).forEach(k=>{const x=btnFor(k);if(x)x.disabled=true;});
|
||
checkLiveBadges(ing,key);
|
||
showSolved(ing,pts,bonus);
|
||
// Sound nach dem korrekten Render-Frame, damit click-country abgeklungen ist
|
||
setTimeout(()=>playSfx('ingredient-solved'), 120);
|
||
} else {
|
||
const remaining = targetCount - current.found.length;
|
||
checkLiveBadges(ing,key);
|
||
showInterim(ing,key,pts,remaining);
|
||
setTimeout(()=>playSfx('ingredient-correct'), 120);
|
||
}
|
||
updateHUD(); renderIngList();
|
||
} else {
|
||
b.classList.add('wrong'); b.disabled=true;
|
||
current.wrongStreak++; current.wrongHere++; current.mistakesInDish++;
|
||
showWrong(key, cls, ingKey);
|
||
setTimeout(()=>playSfx('ingredient-wrong'), 120);
|
||
}
|
||
}
|
||
|
||
/* ====================== ERGEBNIS-KARTEN ====================== */
|
||
function fmtTonnes(t) {
|
||
if (!t || t <= 0) return '';
|
||
// Für Sek I lieber die volle Zahl mit Tausenderpunkt (220.000 t) als „220 Tsd t"
|
||
// oder das SI-Kürzel „kt", das die Altersgruppe nicht kennt. Millionen bleiben
|
||
// als „Mio t" gut lesbar.
|
||
if (t >= 1000000) return (t/1000000).toFixed(t >= 10000000 ? 0 : 1).replace('.0','').replace('.', ',') + ' Mio t/Jahr';
|
||
return t.toLocaleString('de-DE') + ' t/Jahr';
|
||
}
|
||
function regionsHTML(keys){
|
||
return `<div class="regions"><div class="rlabel">Über die Region${keys.length>1?'en':''}</div>`+
|
||
keys.map(k=>{const c=C[k];return `<div class="region-item"><span class="rf">${c.f}</span><span><span class="rn">${c.n}</span>${c.info}</span></div>`;}).join('')+
|
||
`</div>`;
|
||
}
|
||
function producerEconHTML(ingKey, correctKeys) {
|
||
const ing = ING[ingKey];
|
||
if (!ing || !ing.producers) return '';
|
||
const all = Object.entries(ing.producers).filter(([c]) => C[c]);
|
||
all.sort((a,b) => (b[1].t||0) - (a[1].t||0));
|
||
const shown = all.slice(0, 5);
|
||
// Richtige Antwort(en) immer zeigen — auch wenn das Heimatland kein Top-5-Produzent ist
|
||
correctKeys.forEach(ck => {
|
||
if (!shown.some(([c]) => c === ck)) {
|
||
const row = all.find(([c]) => c === ck);
|
||
if (row) shown.push(row);
|
||
}
|
||
});
|
||
const sig = _taskCache[ingKey] && _taskCache[ingKey].signature;
|
||
let html = '<div class="econ-list"><div class="rlabel">' +
|
||
(sig ? 'Die größten Produzenten – zum Vergleich' : 'Weltweite Hauptproduzenten') + '</div>';
|
||
shown.forEach(([c, info]) => {
|
||
const isMatch = correctKeys.includes(c);
|
||
const cm = C[c];
|
||
const amount = info.t ? fmtTonnes(info.t) : (info.note || 'kulturell verbreitet');
|
||
const share = info.share ? ' · ' + info.share : '';
|
||
html += `<div class="prod-row${isMatch ? ' match' : ''}">
|
||
<span class="rf">${cm.f}</span>
|
||
<span class="prod-nm">${cm.n}${isMatch ? ' ✓' : ''}</span>
|
||
<span class="prod-val">${amount}${share}</span>
|
||
</div>`;
|
||
});
|
||
const rest = all.length - shown.length;
|
||
if (rest > 0) {
|
||
html += `<div class="prod-more">+ ${rest} weitere Länder</div>`;
|
||
}
|
||
if (sig) {
|
||
html += `<div class="prod-more" style="color:#4a7c4e;font-weight:600">Der größte Produzent ist nicht automatisch die Antwort – hier zählt, woher die Zutat für dieses Gericht kommt.</div>`;
|
||
}
|
||
html += '</div>';
|
||
return html;
|
||
}
|
||
/* Tierische vs. pflanzliche Zutaten — Heuristik aus Key-Slug. Wird für
|
||
* generische "wächst nicht / wird nicht typisch geliefert"-Texte gebraucht. */
|
||
function isAnimalIng(key){
|
||
return /^(ei|eier|milch|butter|quark|topfen|kaese|mozzarella|joghurt|sahne|rahm|sauerrahm|parmesan|honig|huhn|rind|schwein|wurst|schinken|speck|lachs|thun|fisch|garnele|krabbe|hummer|muschel|wachtel|lamm|schaf|ente|gans|truthahn|fischsauce)/.test(key);
|
||
}
|
||
|
||
/* Erklärt für eine einzelne Choice (richtig / falsch / nicht angeklickt)
|
||
* EINEN didaktischen Satz — wird in der allChoicesHTML-Übersicht gebraucht. */
|
||
function explainChoice(ingKey, key, isCorrect){
|
||
const ing = ING[ingKey];
|
||
const info = ing && ing.producers && ing.producers[key];
|
||
const amount = info && info.t ? fmtTonnes(info.t) : '';
|
||
const animal = isAnimalIng(ingKey);
|
||
// Signatur-Zutat (Heimat-Regel): Antwort folgt dem Gericht
|
||
const sTask = _taskCache[ingKey];
|
||
if (sTask && sTask.signature) {
|
||
const heimatN = C[sTask.heimat] ? C[sTask.heimat].n : '';
|
||
const was = sTask.label || ('der/die/das ' + ing.nm);
|
||
if (isCorrect) {
|
||
return {cls:'ok', badge:'✓', text:`Dieses Gericht kommt aus <b>${heimatN}</b> – ${was} ist hier heimisch und wird vor Ort erzeugt.`};
|
||
}
|
||
if (info) {
|
||
return {cls:'mid', badge:'⚠️', text:`${C[key].n} erzeugt zwar viel ${ing.nm}${amount?' ('+amount+')':''} – aber in einem Gericht aus ${heimatN} stammt ${was} von dort.`};
|
||
}
|
||
// Nicht-Produzent-Fehlklick → normale „unmöglich"-Logik unten
|
||
}
|
||
if (isCorrect){
|
||
if (info && info.market === 'world-far'){
|
||
return {cls:'ok', badge:'✓', text:`<b>Welt-Hauptproduzent</b> (${amount}) — kommt aber selten in den AT-Handel.`};
|
||
}
|
||
if (info && info.market === 'off-season'){
|
||
return {cls:'ok', badge:'✓', text:`<b>Off-Season-Lieferant</b> für unseren Winter (${amount}).`};
|
||
}
|
||
if (info && info.market === 'eu'){
|
||
return {cls:'ok', badge:'✓', text:`<b>Hauptlieferant für uns</b>${amount?' (' + amount + ')':''} – aus dem EU-Binnenmarkt: kurzer Weg, kein Zoll. Deshalb kommt die Ware meist von hier, auch wenn anderswo mehr wächst.`};
|
||
}
|
||
return {cls:'ok', badge:'✓', text:`<b>Hauptlieferant für uns</b>${amount?' (' + amount + ')':''}.`};
|
||
}
|
||
// Falsch: weitere Klassifizierung
|
||
if (info){
|
||
const m = info.market || 'global';
|
||
if (m === 'world-far'){
|
||
return {cls:'mid', badge:'🌐', text:`Weltweit großer Produzent (${amount}) – im EU-Handel aber kaum vertreten, du bekommst die Ware fast nie von dort.`};
|
||
}
|
||
if (m === 'eu' || m === 'global'){
|
||
return {cls:'mid', badge:'⚠️', text:`Auch ein echter Lieferant (${amount}) – aber nicht der Hauptweg zu unserem Markt.`};
|
||
}
|
||
if (m === 'local'){
|
||
return {cls:'mid', badge:'🏠', text:`Produziert ${amount}${animal?' — Tiere werden aber überwiegend für den lokalen Markt gehalten':' — fast alles bleibt im eigenen Land'}.`};
|
||
}
|
||
if (m === 'us-only'){
|
||
return {cls:'mid', badge:'🌐', text:`US-Export geht vor allem nach Asien (Japan, Südkorea) — kaum zu uns.`};
|
||
}
|
||
if (m === 'asia-only'){
|
||
return {cls:'mid', badge:'🌐', text:`Geht in regionale Märkte Asiens — landet praktisch nicht in unseren Supermärkten.`};
|
||
}
|
||
if (m === 'off-season'){
|
||
return {cls:'mid', badge:'🌍', text:`Süd-Hemisphäre — liefert nur im EU-Winter, sonst nicht der Hauptweg zu uns.`};
|
||
}
|
||
}
|
||
if (ing && ing.climate_possible && ing.climate_possible.includes(key)){
|
||
return {cls:'mid', badge:'🌱', text:animal
|
||
? `Wäre theoretisch möglich — kommt aber nicht typisch zu uns.`
|
||
: `Theoretisch möglich (Klimazone passt) — aber kein nennenswerter Hauptlieferant.`};
|
||
}
|
||
// impossible — Text pflanzen-/tier-spezifisch
|
||
if (animal){
|
||
return {cls:'no', badge:'✗', text:`Kein typischer Lieferant für uns — Tiere werden zwar auch dort gehalten, kommen aber praktisch nie als Produkt zu uns auf den Markt.`};
|
||
}
|
||
return {cls:'no', badge:'✗', text:`Klimatisch oder logistisch kein Lieferant für uns — wächst hier nicht im relevanten Umfang oder gelangt nicht in unsere Märkte.`};
|
||
}
|
||
|
||
/* Volltabelle: jede der 4–5 Karten-Optionen kriegt einen erklärenden Satz.
|
||
* Schüler:innen sehen so auch, was sie NICHT angeklickt haben (Lerngewinn:
|
||
* was wäre theoretisch möglich gewesen, was wäre falsch gewesen). */
|
||
function allChoicesHTML(ingKey, task, attempted){
|
||
if (!task || !task.choices || !task.choices.length) return '';
|
||
const rows = task.choices.map(k => {
|
||
const c = C[k]; if (!c) return '';
|
||
const isCorrect = task.correct.includes(k);
|
||
const x = explainChoice(ingKey, k, isCorrect);
|
||
const wasClicked = attempted && attempted.has && attempted.has(k);
|
||
const clickTag = wasClicked ? '<span class="clicked-tag">angeklickt</span>' : '';
|
||
return `<div class="all-choice ${x.cls}">
|
||
<span class="cf">${c.f}</span>
|
||
<span class="cnm">${c.n}</span>
|
||
<span class="cb">${x.badge}</span>
|
||
<div class="ctxt">${x.text}</div>
|
||
${clickTag}
|
||
</div>`;
|
||
}).filter(Boolean).join('');
|
||
return `<div class="all-choices"><div class="rlabel">Alle Länder dieser Runde</div>${rows}</div>`;
|
||
}
|
||
|
||
/* Bei Korrekt-Klick auf world-far oder off-season: ehrlicher Zusatzhinweis,
|
||
* dass das Land zwar weltweit Big Player ist, im AT-Handel aber selten landet. */
|
||
function marketCaveatFor(info) {
|
||
if (!info || !info.market) return '';
|
||
if (info.market === 'world-far') {
|
||
return `<div class="more-line" style="background:#fff7e0;border-left:3px solid #d2a24c">
|
||
⚠️ <b>Zwar weltweit wichtiger Produzent</b> — im österreichischen Handel kommt dieses Land aber <b>kaum vor</b>. Du würdest dieses Produkt fast nie von dort bekommen.
|
||
</div>`;
|
||
}
|
||
if (info.market === 'off-season') {
|
||
return `<div class="more-line" style="background:#fff7e0;border-left:3px solid #d2a24c">
|
||
🌍 <b>Süd-Hemisphäre — Off-Season-Lieferant.</b> Kommt nur im EU-Winter (Dezember–April) in unsere Märkte, wenn die europäische Saison vorbei ist.
|
||
</div>`;
|
||
}
|
||
return '';
|
||
}
|
||
function showInterim(ing,key,pts,remaining){
|
||
const c=C[key],card=document.getElementById('resultCard');
|
||
card.className="result-card mid show";
|
||
const ingKey = current.dish.ings[current.idx];
|
||
const info = (ing.producers && ing.producers[key]) ? ing.producers[key] : null;
|
||
const amount = info && info.t ? ` · ${fmtTonnes(info.t)}` : '';
|
||
card.innerHTML=`<div class="rc-head">✓ ${c.f} ${c.n} stimmt! (+${pts})${amount}</div>
|
||
<div class="note">${c.info}</div>
|
||
${marketCaveatFor(info)}
|
||
<div class="more-line">🔎 Noch <b>${remaining}</b> weiteres Herkunftsland gesucht — finde es für <b>Bonuspunkte</b>!</div>`;
|
||
}
|
||
function showSolved(ing,pts,bonus){
|
||
const card=document.getElementById('resultCard');
|
||
const ingKey = current.dish.ings[current.idx];
|
||
const task = _taskCache[ingKey];
|
||
const flags=task.correct.map(k=>C[k].f+' '+C[k].n).join(' & ');
|
||
card.className="result-card ok show";
|
||
const factLine = ing.fact ? `<div class="more-line"><b>Wusstest du?</b> ${esc(ing.fact)}</div>` : '';
|
||
// Heimat-Regel: erklären, warum die Region die Antwort ist (nicht der größte Produzent)
|
||
const sigLine = (task.signature && C[task.heimat])
|
||
? `<div class="more-line" style="background:#eef5ec;border-left:3px solid #4a7c4e">🍽️ Das ist ein Gericht aus <b>${C[task.heimat].n}</b>. ${task.label ? ('<b>'+task.label+'</b>') : ('Der/die/das '+esc(ing.nm))} kommt von hier – nur Zutaten, die bei uns gar nicht wachsen, reisen von weit her.</div>`
|
||
: '';
|
||
// Caveats für world-far/off-season Korrekt-Picks (pro Land separat)
|
||
const caveats = task.correct.map(k => {
|
||
const info = ing.producers && ing.producers[k];
|
||
if (!info || !info.market || !['world-far','off-season'].includes(info.market)) return '';
|
||
const cc = C[k]; if (!cc) return '';
|
||
const label = `${cc.f} ${cc.n}`;
|
||
if (info.market === 'world-far') {
|
||
return `<div class="more-line" style="background:#fff7e0;border-left:3px solid #d2a24c">
|
||
⚠️ <b>${label}:</b> zwar weltweit wichtiger Produzent — aber im österreichischen Handel kommt dieses Land <b>kaum vor</b>. Du würdest dieses Produkt fast nie von dort bekommen.
|
||
</div>`;
|
||
}
|
||
return `<div class="more-line" style="background:#fff7e0;border-left:3px solid #d2a24c">
|
||
🌍 <b>${label}:</b> Süd-Hemisphäre — kommt nur im EU-Winter (Dezember–April) in unsere Märkte, wenn die europäische Saison vorbei ist.
|
||
</div>`;
|
||
}).filter(Boolean).join('');
|
||
card.innerHTML=`<div class="rc-head">✓ Geschafft! ${esc(ing.nm)} kommt aus: ${flags}</div>
|
||
${sigLine}
|
||
${allChoicesHTML(ingKey, task, current.attempted)}
|
||
${producerEconHTML(ingKey, task.correct)}
|
||
${caveats}
|
||
${regionsHTML(task.correct)}
|
||
${factLine}
|
||
<div class="next-row"><button class="btn" onclick="nextIngredient()">+${pts+bonus} Punkte · Weiter →</button></div>`;
|
||
// Karte schrumpft beim Rundenabschluss, damit die Ergebnis-Übersicht mehr Platz bekommt
|
||
const mapEl = document.getElementById('map');
|
||
if (mapEl && map) {
|
||
mapEl.classList.add('solved');
|
||
setTimeout(() => {
|
||
try { map.invalidateSize(); } catch(e) {}
|
||
// Wieder auf alle Choices der Runde rauszoomen — Schüler:innen sehen die ganze Auswahl
|
||
const pts = task.choices.map(k=>{const c=C[k];return c?[c.lat,c.lng]:null;}).filter(Boolean);
|
||
if (pts.length && map.fitBounds) {
|
||
try { map.fitBounds(pts, {paddingTopLeft:[40,40], paddingBottomRight:[40,40], maxZoom:3, animate:true}); } catch(e) {}
|
||
}
|
||
}, 580);
|
||
}
|
||
}
|
||
function showWrong(key, kind, ingKey){
|
||
const c=C[key],card=document.getElementById('resultCard');
|
||
const ing = ING[ingKey];
|
||
let head, note;
|
||
// Signatur-Zutat: „Warum nicht Deutschland?" sofort beantworten
|
||
const sTask = _taskCache[ingKey];
|
||
if (sTask && sTask.signature && ing.producers && ing.producers[key]) {
|
||
card.className="result-card mid show";
|
||
const info = ing.producers[key];
|
||
const amount = info && info.t ? fmtTonnes(info.t) : 'verbreitet';
|
||
const heimatN = C[sTask.heimat] ? C[sTask.heimat].n : '';
|
||
const was = sTask.label || ('der/die/das ' + ing.nm);
|
||
card.innerHTML = `<div class="rc-head">⚠️ ${c.f} ${c.n} erzeugt zwar viel ${ing.nm} (${amount}) – aber dieses Gericht kommt aus ${heimatN}, und ${was} stammt von dort.</div>`+
|
||
`<div class="note">${c.info}<br><br>🔎 Tipp: Achte auf den Namen des Gerichts – er verrät oft die Herkunft.</div>`;
|
||
return;
|
||
}
|
||
if (kind === 'producer-eu-elsewhere') {
|
||
card.className="result-card mid show";
|
||
const info = ing.producers[key];
|
||
const amount = info && info.t ? fmtTonnes(info.t) : 'verbreitet';
|
||
head = `⚠️ ${c.f} ${c.n} ist auch ein echter Lieferant (${amount}) — aber nicht der Hauptweg zu unserem Markt.`;
|
||
note = c.info + '<br><br>🔎 Such weiter — du hast eines der gesuchten Herkunftsländer noch nicht entdeckt.';
|
||
} else if (kind === 'producer-local') {
|
||
// Produziert, aber bleibt im Inland / regionaler Markt
|
||
card.className="result-card mid show";
|
||
const info = ing.producers[key];
|
||
const amount = info && info.t ? fmtTonnes(info.t) : 'verbreitet';
|
||
const marketHint = {
|
||
'local': 'fast alles wird im eigenen Land verzehrt',
|
||
'us-only': 'der US-Export geht vor allem nach Asien (Japan, Südkorea)',
|
||
'asia-only': 'geht in regionale Märkte, kaum nach Europa',
|
||
'off-season': 'liefert nur im EU-Winter (Dezember–April), wenn die europäische Saison vorbei ist'
|
||
}[info?.market] || 'nicht für den EU-Markt';
|
||
head = `🏠 ${c.f} ${c.n} produziert ${amount} — aber ${marketHint}.`;
|
||
note = c.info + '<br><br>💡 Die Frage ist: <i>wo kommen die Zutaten her, die wir bei uns kaufen?</i> Such weiter.';
|
||
} else if (kind === 'climate-possible') {
|
||
card.className="result-card mid show";
|
||
const animal = isAnimalIng(ingKey);
|
||
head = `🌱 ${c.f} ${c.n} — theoretisch möglich, aber kein Hauptmarkt`;
|
||
note = c.info + (animal
|
||
? '<br><br>💡 Wäre vom Klima her möglich, ist aber kein nennenswerter Lieferant für unseren Markt.'
|
||
: '<br><br>💡 Die Pflanze würde dort klimatisch wachsen können, aber es wird kaum für den Markt produziert.');
|
||
} else {
|
||
card.className="result-card no show";
|
||
const animal = isAnimalIng(ingKey);
|
||
if (animal) {
|
||
head = `✗ ${c.f} ${c.n} — kein typischer Lieferant`;
|
||
note = c.info + '<br><br>🤔 Tierische Produkte aus dieser Region kommen praktisch nicht in unsere Märkte. Versuch ein anderes Land.';
|
||
} else {
|
||
head = `✗ ${c.f} ${c.n} — hier wächst das nicht`;
|
||
note = c.info + '<br><br>🤔 Denk ans Klima: zu kalt? zu trocken? kein Meer? Versuch ein anderes Land.';
|
||
}
|
||
}
|
||
card.innerHTML=`<div class="rc-head">${head}</div><div class="note">${note}</div>`;
|
||
}
|
||
function nextIngredient(){
|
||
document.getElementById('resultCard').className="result-card";
|
||
const mapEl = document.getElementById('map');
|
||
if (mapEl) {
|
||
mapEl.classList.remove('solved'); // Karte zurück auf Vollhöhe für die nächste Zutat
|
||
}
|
||
loadIngredient();
|
||
// iPad-Fix: zur Karte hochscrollen, damit die nächste Aufgabe gleich sichtbar ist
|
||
// (sonst rutscht der untere Result-Bereich der vorherigen Zutat über die Karte).
|
||
if (mapEl) {
|
||
setTimeout(() => { try { map && map.invalidateSize(); } catch(e) {} }, 580);
|
||
const top = mapEl.getBoundingClientRect().top + window.scrollY - 70; // 70 = sticky Header
|
||
window.scrollTo({top: Math.max(0, top), behavior: 'smooth'});
|
||
}
|
||
}
|
||
|
||
/* ====================== BADGES ====================== */
|
||
function hasBadge(id){return state.badges.includes(id);}
|
||
function awardLive(ids){const got=[];ids.forEach(id=>{if(!hasBadge(id)){state.badges.push(id);got.push(BADGES.find(b=>b.id===id));}});if(got.length){updateHUD();toastBadges(got);setTimeout(()=>playSfx('badge-earned'), 300);}return got;}
|
||
function checkLiveBadges(ing,key){
|
||
const ids=[];
|
||
if(state.totalCorrect>=1) ids.push('first');
|
||
if(state.totalCorrect>=8) ids.push('spice');
|
||
if(state.cleanIngredients>=3) ids.push('climate');
|
||
if(state.score>=3000) ids.push('trade');
|
||
if(ing.nm.indexOf('Nori')===0) ids.push('landlk');
|
||
awardLive(ids);
|
||
}
|
||
function toastBadges(list){
|
||
list.forEach((b,i)=>setTimeout(()=>{
|
||
const t=document.createElement('div');
|
||
t.style.cssText="position:fixed;left:50%;top:74px;transform:translateX(-50%);background:var(--card);border:1px solid var(--gold);border-radius:14px;padding:10px 16px;box-shadow:var(--shadow);z-index:950;font-weight:700;font-size:.9rem;display:flex;gap:9px;align-items:center";
|
||
t.innerHTML=`<span style="font-size:1.3rem">${b.icon}</span> Neues Abzeichen: ${b.name}!`;
|
||
document.body.appendChild(t);
|
||
setTimeout(()=>{t.style.opacity="0";t.style.transition="opacity .4s";},2200);
|
||
setTimeout(()=>t.remove(),2700);
|
||
},i*350));
|
||
}
|
||
|
||
/* ====================== GERICHT FERTIG ====================== */
|
||
function finishDish(){
|
||
const d=current.dish;
|
||
if(!state.completed.includes(d.id)) state.completed.push(d.id);
|
||
playSfx('dish-complete');
|
||
const perfect=current.mistakesInDish===0;
|
||
const newly=[];
|
||
if(perfect) newly.push(...awardLive(['perfect']));
|
||
newly.push(...awardLive(['dish']));
|
||
if(state.completed.length>=5) newly.push(...awardLive(['explorer']));
|
||
if(state.completed.length>=DISHES.length) newly.push(...awardLive(['star']));
|
||
if(state.score>=3000) newly.push(...awardLive(['trade']));
|
||
updateHUD(); renderDishes();
|
||
showRecipeFullScreen(d, perfect, newly);
|
||
}
|
||
/* Volle Infoseite NACH Abschluss: Erfolgs-Header + Bild + Rezept + Lehrplan-Bezug + Trivia + Klimabilanz */
|
||
function showRecipeFullScreen(d, perfect, newly) {
|
||
const host = document.getElementById('recipe-full-body');
|
||
const imgPath = d.imagePath ? (_basePath + d.imagePath) : null;
|
||
const n = d.nutrition || {};
|
||
const r = d.recipe || {};
|
||
const remaining = DISHES.length - state.completed.length;
|
||
let html = `
|
||
<div style="background:var(--green-soft);border:1px solid var(--green);border-radius:18px;padding:18px 20px;margin-bottom:18px;text-align:center">
|
||
<div style="font-size:2.6rem;line-height:1">${perfect?'🎉':'✨'}</div>
|
||
<h2 style="font-size:1.4rem;margin:6px 0;color:var(--green-d)">${perfect?'Perfekt geforscht!':'Gericht erforscht!'}</h2>
|
||
<div style="font-weight:700;color:var(--green-d)">+${current.dishScore} Punkte · Gesamt: ${state.score} ⭐</div>
|
||
${newly.length?`<div class="earned-badges" style="margin-top:12px">${newly.map(b=>`<div class="bdg"><div class="ring">${b.icon}</div><div class="lbl">${b.name}</div></div>`).join('')}</div>`:''}
|
||
</div>`;
|
||
if (imgPath) {
|
||
html += `<img src="${imgPath}" alt="${d.name}" style="width:100%;max-height:280px;object-fit:cover;border-radius:18px;margin-bottom:18px;background:var(--bg2)" onerror="this.style.display='none'">`;
|
||
}
|
||
html += `<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px">
|
||
<span style="font-size:2.4rem">${d.emoji}</span>
|
||
<div><h2 style="font-size:1.6rem;margin:0">${d.name}</h2>
|
||
<div style="color:var(--muted);font-size:.92rem">📍 ${d.origin} · <b>${d.diff}</b>${d.diff_note?' · <em>'+d.diff_note+'</em>':''}</div>
|
||
</div>
|
||
</div>`;
|
||
// Nährwerte
|
||
if (Object.keys(n).length) {
|
||
html += `<div style="background:var(--bg2);border-radius:14px;padding:14px;margin:14px 0">
|
||
<h3 style="font-size:.92rem;margin-bottom:10px">📊 Nährwerte pro Portion (${n.portion_g||'?'} g)</h3>
|
||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;font-size:.86rem">
|
||
<span>kcal: <b>${n.kcal||'–'}</b></span>
|
||
<span>Eiweiß: <b>${n.protein_g||'–'} g</b></span>
|
||
<span>Kohlenhydrate: <b>${n.carbs_g||'–'} g</b></span>
|
||
<span>Fett: <b>${n.fat_g||'–'} g</b></span>
|
||
<span>Ballaststoffe: <b>${n.fiber_g||'–'} g</b></span>
|
||
<span>Salz: <b>${n.salt_g||'–'} g</b></span>
|
||
</div>
|
||
</div>`;
|
||
}
|
||
// Rezept
|
||
if (r.steps && r.steps.length) {
|
||
html += `<div style="margin:14px 0">
|
||
<h3 style="font-size:1rem;margin-bottom:8px">👩🍳 So machst du es (${r.portions||'?'} Portionen, ${r.time_min||'?'} min)</h3>
|
||
<ol style="padding-left:1.4rem;line-height:1.55;font-size:.92rem">${r.steps.map(s=>'<li style="margin-bottom:6px">'+esc(s)+'</li>').join('')}</ol>
|
||
${r.tipp?'<div style="background:var(--gold-soft);border-radius:10px;padding:10px 12px;font-size:.86rem;margin-top:10px"><b style="color:#8a6516">💡 Tipp:</b> '+esc(r.tipp)+'</div>':''}
|
||
</div>`;
|
||
}
|
||
// Lehrplan
|
||
if (d.lehrplan && Object.keys(d.lehrplan).length) {
|
||
html += '<div style="margin:14px 0"><h3 style="font-size:1rem;margin-bottom:8px">📚 Wenn dich das interessiert</h3>';
|
||
const labels = {eh:'Ernährung & Haushalt', gw:'Geografie und wirtschaftliche Bildung', bu:'Biologie & Umwelt', de:'Deutsch'};
|
||
Object.entries(d.lehrplan).forEach(([k, v]) => {
|
||
html += '<div style="background:var(--green-soft);border-radius:10px;padding:10px 12px;font-size:.86rem;margin-bottom:6px"><b style="color:var(--green-d)">'+(labels[k]||k.toUpperCase())+':</b> '+esc(v)+'</div>';
|
||
});
|
||
html += '</div>';
|
||
}
|
||
// Trivia
|
||
if (d.fact) {
|
||
html += '<div style="background:#fff;border:1px solid var(--line);border-radius:10px;padding:10px 12px;font-size:.86rem;font-style:italic;margin:14px 0;color:var(--ink)">💡 '+esc(d.fact)+'</div>';
|
||
}
|
||
// Klimabilanz
|
||
html += climateImpactBlock(d);
|
||
// Nav — nur ein Button. Abzeichen sind über die Top-Bar erreichbar.
|
||
html += `<div style="text-align:center;margin:18px 0 40px">
|
||
<button class="btn" style="max-width:360px;margin:0 auto" onclick="show('dishes')">${remaining>0?'Nächstes Gericht ('+remaining+' offen) →':'Alle Gerichte geschafft! 🏆'}</button>
|
||
</div>`;
|
||
host.innerHTML = html;
|
||
show('recipe-full');
|
||
}
|
||
|
||
/* Rangliste komplett entfernt (Plattform-Konvention: keine Sim hat eine eigene Rangliste). */
|
||
function renderBadges(){
|
||
document.getElementById('badgeGrid').innerHTML=BADGES.map(b=>{
|
||
const got=hasBadge(b.id);
|
||
return `<div class="badge-card${got?'':' locked'}"><div class="ring">${got?b.icon:'🔒'}</div><h4>${b.name}</h4><p>${b.desc}</p></div>`;
|
||
}).join('');
|
||
}
|
||
updateHUD();
|
||
</script>
|
||
</body>
|
||
</html>
|