diff --git a/App/Don_t_Deploy/2026-07-17-glossar-anschaulich.sql b/App/Don_t_Deploy/2026-07-17-glossar-anschaulich.sql
new file mode 100644
index 0000000..8820654
--- /dev/null
+++ b/App/Don_t_Deploy/2026-07-17-glossar-anschaulich.sql
@@ -0,0 +1,2 @@
+UPDATE glossar SET short=CONVERT(FROM_BASE64('T2JzdCBvZGVyIEdlbcO8c2UgYXXDn2VyaGFsYiBzZWluZXIgU2Fpc29uIOKAkyBvZnQgZWluZ2VmbG9nZW4gb2RlciBhdXMgYmVoZWl6dGVuIEdld8OkY2hzaMOkdXNlcm4sIG1pdCB2aWVsIGjDtmhlcmVyIEtsaW1hYmlsYW56Lg==') USING utf8mb4), text=CONVERT(FROM_BASE64('4oCeT2ZmLVNlYXNvbuKAnCBoZWnDn3Qg4oCeYXXDn2VyaGFsYiBkZXIgU2Fpc29u4oCcLiBEYW1pdCBtZWludCBtYW4gT2JzdCB1bmQgR2Vtw7xzZSwgZGFzIGJlaSB1bnMgZ2VyYWRlIGdhciBuaWNodCB3w6RjaHN0IOKAkyB6dW0gQmVpc3BpZWwgRXJkYmVlcmVuIGltIERlemVtYmVyIG9kZXIgV2VpbnRyYXViZW4gaW0gRmVicnVhci4gRGFtaXQgc2llIHRyb3R6ZGVtIGltIFJlZ2FsIGxpZWdlbiwga29tbWVuIHNpZSBhdXMgZmVybmVuLCB3YXJtZW4gTMOkbmRlcm4gb2RlciBhdXMgYmVoZWl6dGVuIEdld8OkY2hzaMOkdXNlcm4uCgpEYXMgbWFjaHQgZsO8cnMgS2xpbWEgZWluZW4gcmllc2lnZW4gVW50ZXJzY2hpZWQuIEVpbmUgaGVpbWlzY2hlIEVyZGJlZXJlIGltIE1haSB2b20gRmVsZCB2ZXJ1cnNhY2h0IHJ1bmQgMCwzIGtnIENP4oKCIHBybyBLaWxvLiBEaWVzZWxiZSBNZW5nZSBXaW50ZXItRXJkYmVlcmVuIGF1cyBkZW0gYmVoZWl6dGVuIEdld8OkY2hzaGF1cyBvZGVyIG1pdCBkZW0gRmx1Z3pldWcgZWluZ2VmbG9nZW4ga29tbXQgYXVmIHJ1bmQgMyDigJMgNCBrZyDigJMgYWxzbyBldHdhIHplaG5tYWwgc28gdmllbCAoSUZFVSAvIFVtd2VsdGJ1bmRlc2FtdCkuCgpXaWNodGlnIGlzdCBkYWJlaSB3ZW5pZ2VyIGRpZSByZWluZSBFbnRmZXJudW5nLCBzb25kZXJuIFdJRSBldHdhcyByZWlzdC4gTWl0IGRlbSBTY2hpZmYgaXN0IHNvZ2FyIGVpbiB3ZWl0ZXIgV2VnIG9mdCBzcGFyc2FtOiBFaW4gQXBmZWwgYXVzIFPDvGRhZnJpa2EgcGVyIFNjaGlmZiBpc3Qga2F1bSBzY2hsZWNodGVyIGFscyBlaW4gaGVpbWlzY2hlciBBcGZlbCwgZGVyIGJlaSB1bnMgZGVuIFdpbnRlciDDvGJlciBpbSBLw7xobGxhZ2VyIGxhZyAoYmVpZGUgcnVuZCAwLDMg4oCTIDAsNCBrZyBDT+KCgiBwcm8gS2lsbykuIFJpY2h0aWcgdGV1ZXIgZsO8cnMgS2xpbWEgd2lyZCBlcyBpbSBGbHVnemV1ZyDigJMgc28gd2VyZGVuIEJlZXJlbiBvZGVyIFdlaW50cmF1YmVuIG1pdHRlbiBpbSBXaW50ZXIgenUgZWNodGVuIEtsaW1hc8O8bmRlcm4uCgpEaWUgZWluZmFjaHN0ZSBSZWdlbDogSXNzLCB3YXMgZ2VyYWRlIGJlaSB1bnMgU2Fpc29uIGhhdC4gRWluIFNhaXNvbmthbGVuZGVyIHplaWd0LCB3YW5uIHdhcyByZWlmIGlzdC4=') USING utf8mb4), short_easy=CONVERT(FROM_BASE64('T2JzdCwgZGFzIGJlaSB1bnMgZ2VyYWRlIG5pY2h0IHfDpGNoc3Qg4oCTIHp1bSBCZWlzcGllbCBFcmRiZWVyZW4gaW0gV2ludGVyLg==') USING utf8mb4), text_easy=CONVERT(FROM_BASE64('T2ZmLVNlYXNvbiBoZWnDn3Q6IGF1w59lcmhhbGIgZGVyIFNhaXNvbi4KWnVtIEJlaXNwaWVsIEVyZGJlZXJlbiBpbSBXaW50ZXIuCkRhbm4gd2FjaHNlbiBzaWUgYmVpIHVucyBuaWNodC4KU2llIGtvbW1lbiB2b24gd2VpdCBoZXIgb2RlciBhdXMgZGVtIHdhcm1lbiBHbGFzaGF1cy4KCkRhcyBpc3Qgc2NobGVjaHQgZsO8cnMgS2xpbWE6CkVpbmUgRXJkYmVlcmUgaW0gU29tbWVyIHZvbSBGZWxkIG1hY2h0IHdlbmlnIENP4oKCLgpFaW5lIEVyZGJlZXJlIGltIFdpbnRlciBtYWNodCBmYXN0IHplaG5tYWwgc28gdmllbC4KCkFtIGJlc3RlbiBpc3N0IGR1IE9ic3QsIHdlbm4gZXMgYmVpIHVucyBTYWlzb24gaGF0Lg==') USING utf8mb4) WHERE key_slug='off-season-import' COLLATE utf8mb4_unicode_ci;
+UPDATE glossar SET short=CONVERT(FROM_BASE64('R3V0ZXMsIG5vY2ggZXNzYmFyZXMgRXNzZW4gbGFuZGV0IGltIE3DvGxsIOKAkyBpbiDDlnN0ZXJyZWljaCBydW5kIDcwIGtnIHBybyBQZXJzb24gdW5kIEphaHIu') USING utf8mb4), text=CONVERT(FROM_BASE64('U3RlbGwgZGlyIHZvciwgdm9uIHplaG4gdm9sbGVuIEphdXNlbmJyb3RlbiB3aXJmc3QgZHUgamVkZW4gVGFnIGVpbmVzIHVuZ2VnZXNzZW4gd2VnLiBVbmdlZsOkaHIgc28gdmllbCBndXRlcyBFc3NlbiBsYW5kZXQgYmVpIHVucyBpbSBNw7xsbCwgb2J3b2hsIG1hbiBlcyBub2NoIGVzc2VuIGvDtm5udGUg4oCTIGRhcyBuZW5udCBtYW4gTGViZW5zbWl0dGVsdmVyc2Nod2VuZHVuZy4gU2llIHBhc3NpZXJ0IMO8YmVyYWxsOiB6dSBIYXVzZSwgaW0gU3VwZXJtYXJrdCwgaW4gS2FudGluZW4gdW5kIEdhc3Row6R1c2Vybi4KCkluIMOWc3RlcnJlaWNoIHdpcmZ0IGplZGUgUGVyc29uIHBybyBKYWhyIHJ1bmQgNzAga2cgbm9jaCBnZW5pZcOfYmFyZSBMZWJlbnNtaXR0ZWwgd2VnIChXV0Ygw5ZzdGVycmVpY2gpLiBXaWUgdmllbCBpc3QgZGFzPyA3MCBrZyB3aWVndCB1bmdlZsOkaHIgZWluIGVyd2FjaHNlbmVyIE1lbnNjaC4gVW5kIGVpbiBNZW5zY2ggaXNzdCBhbSBUYWcgZXR3YSAxLDUga2cgTmFocnVuZyDigJMgNzAga2cgc2luZCBhbHNvIGRhcyBFc3NlbiB2b24gZmFzdCBzZWNocyBXb2NoZW4uIEplZGUgdW5kIGplZGVyIHZvbiB1bnMgd2lyZnQgc29taXQgamVkZXMgSmFociBydW5kIHNlY2hzIFdvY2hlbiBFc3NlbiBpbiBkZW4gTcO8bGwuCgpEYXMgaXN0IGRvcHBlbHQgc2NoYWRlLiBHbGVpY2h6ZWl0aWcgaGFiZW4gd2VsdHdlaXQgw7xiZXIgNzAwIE1pbGxpb25lbiBNZW5zY2hlbiB6dSB3ZW5pZyB6dSBlc3NlbiAoRkFPKS4gVmllbGVzIHZvbiBkZW0sIHdhcyBiZWkgdW5zIHdlZ2dld29yZmVuIHdpcmQsIHfDpHJlIGbDvHIgc2llIGVpbmUgcmljaHRpZ2UgTWFobHplaXQuIFVuZCBpbSBFc3NlbiBzdGVja3QgdmllbCBtZWhyIGFscyBkYXMgRXNzZW4gc2VsYnN0OiBkYXMgV2Fzc2VyIHp1bSBBbmJhdWVuLCBkYXMgRmVsZCwgZGllIEFyYmVpdCBkZXIgQsOkdWVyaW5uZW4gdW5kIEJhdWVybiB1bmQgZGVyIHdlaXRlIFRyYW5zcG9ydCDigJMgZGllIGdhbnplIFJlaXNlIHdhciB1bXNvbnN0LgoKRGFnZWdlbiBrYW5uIGplZGUgdW5kIGplZGVyIGV0d2FzIHR1bjogdm9yIGRlbSBFaW5rYXVmIGVpbmUgTGlzdGUgc2NocmVpYmVuIHVuZCBuaWNodCBodW5ncmlnIGxvc3ppZWhlbiwgUmVzdGUgYW0gbsOkY2hzdGVuIFRhZyBlc3NlbiB1bmQgd2lzc2VuLCBkYXNzIOKAnm1pbmRlc3RlbnMgaGFsdGJhciBiaXPigJwgbnVyIOKAnmFtIGJlc3RlbiBiaXPigJwgYmVkZXV0ZXQuIFZpZWxlIEpvZ2h1cnRzLCBOdWRlbG4gb2RlciBLZWtzZSBzaW5kIGF1Y2ggZGFuYWNoIG5vY2ggZ3V0IOKAkyBSaWVjaGVuIHVuZCBQcm9iaWVyZW4gdmVycsOkdCBtZWhyIGFscyBkYXMgRGF0dW0u') USING utf8mb4), short_easy=CONVERT(FROM_BASE64('R3V0ZXMgRXNzZW4gbGFuZGV0IGltIE3DvGxsIOKAkyBpbiDDlnN0ZXJyZWljaCBydW5kIDcwIEtpbG8gcHJvIFBlcnNvbiBpbSBKYWhyLg==') USING utf8mb4), text_easy=CONVERT(FROM_BASE64('T2Z0IGxhbmRldCBndXRlcyBFc3NlbiBpbSBNw7xsbC4KSW4gw5ZzdGVycmVpY2ggc2luZCBkYXMgZXR3YSA3MCBLaWxvIHBybyBQZXJzb24gaW0gSmFoci4KNzAgS2lsbyDigJMgc28gdmllbCB3aWVndCBlaW4gZXJ3YWNoc2VuZXIgTWVuc2NoLgpEYXZvbiBrw7ZubnRlIGVpbiBNZW5zY2ggZmFzdCBzZWNocyBXb2NoZW4gbGFuZyBlc3Nlbi4KCkdsZWljaHplaXRpZyBoYWJlbiB2aWVsZSBNZW5zY2hlbiBhdWYgZGVyIFdlbHQgenUgd2VuaWcgenUgZXNzZW4uClVuc2VyIE3DvGxsIHfDpHJlIGbDvHIgc2llIGVpbmUgZ3V0ZSBNYWhsemVpdC4KCkltIEVzc2VuIHN0ZWNrdCB2aWVsIEFyYmVpdCB1bmQgV2Fzc2VyLgpCZXNzZXI6IE51ciBrYXVmZW4sIHdhcyBtYW4gd2lya2xpY2ggYnJhdWNodC4KUmVzdGUgYW0gbsOkY2hzdGVuIFRhZyBlc3Nlbi4=') USING utf8mb4) WHERE key_slug='lebensmittelverschwendung' COLLATE utf8mb4_unicode_ci;
diff --git a/App/assets/js/glossar-experiments-klimazonen.js b/App/assets/js/glossar-experiments-klimazonen.js
new file mode 100644
index 0000000..841b9d4
--- /dev/null
+++ b/App/assets/js/glossar-experiments-klimazonen.js
@@ -0,0 +1,198 @@
+/**
+ * Glossar-Experimente — Klimazonen.
+ *
+ * Registrierung: window.GlossarExperiments['klimazone'] = function (root) { … }
+ * Der Renderer (glossar.php) hängt einen leeren Container ein und ruft die
+ * Funktion mit diesem Container als einzigem Argument auf.
+ *
+ * Nur bestehende .ge-*-Klassen aus glossar.php verwenden, kein neues CSS.
+ */
+
+(function () {
+ 'use strict';
+
+ const NS = 'http://www.w3.org/2000/svg';
+ const svgEl = (tag, attrs, parent) => {
+ const el = document.createElementNS(NS, tag);
+ if (attrs) for (const k in attrs) el.setAttribute(k, attrs[k]);
+ if (parent) parent.appendChild(el);
+ return el;
+ };
+
+ // =====================================================================
+ // Experiment: Klimazonen der Erde — Querschnitt Äquator → Pol
+ // Bänder anklicken → Temperatur, Regen und was dort wächst.
+ // =====================================================================
+ function climateZones(root) {
+ // Bänder von OBEN (Pol, kalt) nach UNTEN (Äquator, heiß).
+ // y-Werte passen zu viewBox 0 0 600 340.
+ const zones = [
+ {
+ key: 'polar',
+ name: 'Polargebiete',
+ where: 'Arktis · Antarktis',
+ temp: '−30 – 0 °C',
+ rain: 'sehr wenig, meist Schnee',
+ grows: 'kein Ackerbau — zu kalt',
+ emoji: '🧊',
+ color: '#dbe9ee', text: '#2a4a55', border: '#9fbcc6',
+ state: 'now'
+ },
+ {
+ key: 'boreal',
+ name: 'Kalte Zone (Nadelwald)',
+ where: 'Skandinavien · Kanada',
+ temp: '0 – 5 °C im Jahr',
+ rain: 'mittel, lange kalte Winter',
+ grows: 'Nadelwald, Beeren — kaum Ackerbau',
+ emoji: '🌲',
+ color: '#5a8a8f', text: '#ffffff', border: '#3d6b70',
+ state: 'now'
+ },
+ {
+ key: 'temperate',
+ name: 'Gemäßigte Zone',
+ where: 'Österreich · Mitteleuropa',
+ temp: '8 – 12 °C im Jahr',
+ rain: '600 – 1.000 mm, 4 Jahreszeiten',
+ grows: 'Äpfel, Getreide, Erdäpfel',
+ emoji: '🍎',
+ color: '#4a7c4e', text: '#ffffff', border: '#356139',
+ state: 'good',
+ home: true
+ },
+ {
+ key: 'subtropic',
+ name: 'Subtropen',
+ where: 'Mittelmeer · Kalifornien',
+ temp: '15 – 22 °C im Jahr',
+ rain: 'trockene heiße Sommer',
+ grows: 'Oliven, Zitrusfrüchte, Mandeln',
+ emoji: '🍊',
+ color: '#e8c547', text: '#4a3d0a', border: '#c9a516',
+ state: 'warn'
+ },
+ {
+ key: 'tropic',
+ name: 'Tropen (Äquator)',
+ where: 'heiß und feucht',
+ temp: '24 – 28 °C, immer warm',
+ rain: '1.500 – 3.000 mm, sehr viel Regen',
+ grows: 'Kakao, Banane, Kaffee, Reis',
+ emoji: '🍌',
+ color: '#2e7d4f', text: '#ffffff', border: '#1f5a38',
+ state: 'warn'
+ }
+ ];
+
+ root.innerHTML = `
+
+
+
🌍 Die Klimazonen der Erde
+
Von unten (Äquator, viel Sonne) nach oben (Pol, wenig Sonne) wird es kühler. Tippe auf ein Band und schau, was dort wächst.
+
+
+
+
Tippe auf ein Band …
+
Wo etwas wächst, hängt von Wärme und Regen ab.
+
+
Einteilung nach den Klimazonen der Köppen-Geiger-Klassifikation; Temperatur- und Niederschlagsbereiche sind grobe kindgerechte Richtwerte. Quelle: Klimadiagramme & Köppen-Geiger-Klassifikation.
+
+ `;
+
+ const svg = root.querySelector('.ge-gh-svg');
+ const info = root.querySelector('#ge-kz-info');
+ const keyPill = root.querySelector('#ge-kz-key');
+ if (!svg || !info || !keyPill) return;
+
+ const BAND_X = 92;
+ const BAND_W = 466;
+ const BAND_H = 54;
+ const TOP = 26;
+
+ // Linke Achse: Pol oben → Äquator unten, mit Sonnen-Symbol.
+ const axisTop = TOP;
+ const axisBot = TOP + zones.length * BAND_H;
+ svgEl('line', { x1: 60, y1: axisTop, x2: 60, y2: axisBot, stroke: '#4a4a4a', 'stroke-width': '1.5' }, svg);
+ // Pfeilspitzen (oben = kalt, unten = warm)
+ svgEl('polygon', { points: `60,${axisTop - 8} 55,${axisTop + 2} 65,${axisTop + 2}`, fill: '#4a4a4a' }, svg);
+ svgEl('polygon', { points: `60,${axisBot + 8} 55,${axisBot - 2} 65,${axisBot - 2}`, fill: '#4a4a4a' }, svg);
+ const polTxt = svgEl('text', { x: 44, y: axisTop + 6, 'text-anchor': 'middle', 'font-size': '11', 'font-weight': '800', fill: '#2a4a55', transform: `rotate(-90, 44, ${axisTop + 40})` }, svg);
+ polTxt.textContent = 'Pol ❄';
+ const aeqTxt = svgEl('text', { x: 44, y: axisBot - 4, 'text-anchor': 'middle', 'font-size': '11', 'font-weight': '800', fill: '#2e7d4f', transform: `rotate(-90, 44, ${axisBot - 40})` }, svg);
+ aeqTxt.textContent = 'Äquator ☀';
+
+ const nodes = [];
+ let current = null;
+
+ zones.forEach((z, i) => {
+ const y = TOP + i * BAND_H;
+ const g = svgEl('g', { class: 'ge-kz-band', style: 'cursor:pointer', tabindex: '0', role: 'button', 'aria-label': z.name }, svg);
+ const rect = svgEl('rect', {
+ x: BAND_X, y: y + 2, width: BAND_W, height: BAND_H - 4, rx: 8,
+ fill: z.color, stroke: z.border, 'stroke-width': '1.5'
+ }, g);
+ // Emoji links im Band
+ svgEl('text', { x: BAND_X + 26, y: y + BAND_H / 2 + 9, 'text-anchor': 'middle', 'font-size': '26' }, g)
+ .textContent = z.emoji;
+ // Zonenname
+ const name = svgEl('text', { x: BAND_X + 52, y: y + BAND_H / 2 - 2, 'font-size': '14', 'font-weight': '800', fill: z.text }, g);
+ name.textContent = z.name;
+ // Wo (klein darunter)
+ const sub = svgEl('text', { x: BAND_X + 52, y: y + BAND_H / 2 + 15, 'font-size': '10.5', 'font-weight': '600', fill: z.text, opacity: '0.9' }, g);
+ sub.textContent = z.where;
+ // „Hier ist Österreich"-Marker
+ if (z.home) {
+ const marker = svgEl('g', {}, g);
+ svgEl('rect', { x: BAND_X + BAND_W - 118, y: y + BAND_H / 2 - 13, width: 112, height: 24, rx: 12, fill: '#ffffff', stroke: '#c85c4a', 'stroke-width': '1.5' }, marker);
+ svgEl('text', { x: BAND_X + BAND_W - 62, y: y + BAND_H / 2 + 3, 'text-anchor': 'middle', 'font-size': '11', 'font-weight': '800', fill: '#c85c4a' }, marker)
+ .textContent = '📍 Österreich';
+ }
+ nodes.push({ z, rect });
+
+ const activate = () => select(i);
+ g.addEventListener('click', activate);
+ g.addEventListener('keydown', (ev) => {
+ if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault(); activate(); }
+ });
+ });
+
+ function select(i) {
+ const z = zones[i];
+ current = i;
+ // Auswahl-Rahmen setzen
+ nodes.forEach((n, j) => {
+ n.rect.setAttribute('stroke', j === i ? '#1f4e5a' : n.z.border);
+ n.rect.setAttribute('stroke-width', j === i ? '3.5' : '1.5');
+ });
+ info.dataset.state = z.state;
+ info.innerHTML =
+ `${z.name} · ${z.where}` +
+ ` 🌡 ${z.temp}` +
+ ` 💧 Regen: ${z.rain}` +
+ ` 🌱 Wächst: ${z.grows}`;
+
+ // Kernerkenntnis dynamisch auf die gewählte Zone beziehen
+ let msg;
+ if (z.key === 'tropic') {
+ msg = 'In den Tropen ist es heiß und feucht — nur hier wächst Kakao für Schokolade.';
+ } else if (z.key === 'temperate') {
+ msg = 'Bei uns in Österreich ist es mild mit Regen — perfekt für Äpfel und Getreide, aber zu kühl für Kakao.';
+ } else if (z.key === 'polar' || z.key === 'boreal') {
+ msg = 'Zu kalt und zu wenig Sonne: hier gibt es kaum oder gar keinen Ackerbau.';
+ } else {
+ msg = 'Heiße, trockene Sommer mögen Oliven und Zitrusfrüchte — bei uns wäre es dafür zu kühl.';
+ }
+ keyPill.dataset.state = 'now';
+ keyPill.innerHTML = `Merke: ${msg}`;
+ }
+
+ // Startzustand: gemäßigte Zone (Österreich) vorgewählt
+ select(2);
+ }
+
+ // Registry — Object.assign, damit die Ladereihenfolge egal ist.
+ window.GlossarExperiments = Object.assign(window.GlossarExperiments || {}, {
+ 'klimazone': climateZones
+ });
+})();
diff --git a/App/assets/js/glossar-experiments-monsun.js b/App/assets/js/glossar-experiments-monsun.js
new file mode 100644
index 0000000..b2fda21
--- /dev/null
+++ b/App/assets/js/glossar-experiments-monsun.js
@@ -0,0 +1,194 @@
+/**
+ * Glossar-Experimente — Gruppe „monsun".
+ *
+ * Registrierung via window.GlossarExperiments[key_slug] = fn(root).
+ * Eigener svgEl-Helper, nur bestehende .ge-*-CSS-Klassen (aus glossar.php).
+ * Keine Libraries. Mobile-/Tap-freundlich. rAF-Loops stoppen bei geschlossenem
+ * Modal über den root.isConnected-Guard.
+ */
+
+(function () {
+ 'use strict';
+
+ const NS = 'http://www.w3.org/2000/svg';
+ const svgEl = (tag, attrs, parent) => {
+ const el = document.createElementNS(NS, tag);
+ if (attrs) for (const k in attrs) el.setAttribute(k, attrs[k]);
+ if (parent) parent.appendChild(el);
+ return el;
+ };
+ const deAT = (n) => Number(n).toLocaleString('de-AT');
+
+ // =====================================================================
+ // Experiment: Monsun-Regen — Regen pro Monat in Mumbai (Balkendiagramm)
+ // =====================================================================
+ function monsunRain(root) {
+ root.innerHTML = `
+
+
+
🌧 Wann fällt der Regen in Indien?
+
Jeder Balken ist ein Monat in Mumbai (Indien). Schau, wie riesig die Balken von Juni bis September werden. In dieser kurzen Zeit fällt fast der ganze Jahresregen — davon hängt die Reisernte ab.
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ const svg = root.querySelector('.ge-mon-svg');
+ const sharePill = root.querySelector('#ge-mon-share');
+ const totalPill = root.querySelector('#ge-mon-total');
+ const noteEl = root.querySelector('#ge-mon-note');
+ if (!svg || !sharePill || !totalPill || !noteEl) return;
+
+ const MONTHS = ['Jän', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'];
+ // Monsun-Monate (0-basiert): Juni–September
+ const MONSOON = [5, 6, 7, 8];
+
+ // Regen pro Monat in mm.
+ // Normal: Mumbai (Colaba) 1981–2010, India Meteorological Department.
+ const NORMAL = [1, 0, 1, 0, 16, 506, 769, 472, 356, 82, 9, 3];
+ // Schwacher Monsun (Dürre-Jahr): Spitzen-Monate stark reduziert.
+ const WEAK = [1, 0, 1, 0, 10, 280, 420, 240, 180, 60, 6, 2];
+ // Vergleich: Wien fällt Monat für Monat ziemlich gleichmäßig (~60 mm).
+ const WIEN_REF = 60;
+
+ const NOTES = {
+ normal: 'Regen pro Monat in Mumbai, Normalwerte 1981 – 2010. Quelle: India Meteorological Department (über „Climate of Mumbai"). Ganzes Jahr rund 2.215 mm. Die dünne Linie zeigt Wien: dort fallen jeden Monat etwa 60 mm — ziemlich gleichmäßig.',
+ weak: 'Schwacher Monsun (Dürre-Jahr): geschätzte Werte, in denen die Monsun-Monate viel weniger Regen bringen. So etwas macht Bäuerinnen und Bauern Sorge — der Klimawandel lässt den Monsun unberechenbarer werden. Quelle Normalwerte: India Meteorological Department.'
+ };
+
+ // Layout
+ const X0 = 58, X1 = 588; // Plot links/rechts
+ const BASE = 278, TOP = 34; // Baseline / oberer Rand
+ const PLOT_H = BASE - TOP; // Zeichenhöhe
+ const MAX_MM = 800; // feste Skala (Balken schrumpfen sichtbar)
+ const PX_PER_MM = PLOT_H / MAX_MM;
+ const slot = (X1 - X0) / MONTHS.length;
+ const BAR_W = slot * 0.66;
+
+ const yFor = (mm) => BASE - Math.min(MAX_MM, mm) * PX_PER_MM;
+
+ // --- Statische Achsen / Gitter ---
+ // Y-Gitterlinien + Beschriftung
+ [0, 200, 400, 600, 800].forEach(mm => {
+ const y = yFor(mm);
+ svgEl('line', { x1: X0, y1: y, x2: X1, y2: y, stroke: '#dae8ec', 'stroke-width': mm === 0 ? '1.6' : '1' }, svg);
+ const t = svgEl('text', { x: X0 - 8, y: y + 4, 'text-anchor': 'end', 'font-size': '11', fill: '#4a4a4a', 'font-weight': '600' }, svg);
+ t.textContent = mm;
+ });
+ svgEl('text', { x: X0 - 8, y: TOP - 14, 'text-anchor': 'end', 'font-size': '11', fill: '#4a4a4a', 'font-weight': '700' }, svg).textContent = 'mm';
+
+ // Monat-Beschriftung + farbige Monsun-Markierung am Boden
+ const barNodes = [];
+ const valNodes = [];
+ MONTHS.forEach((m, i) => {
+ const cx = X0 + slot * i + slot / 2;
+ const isMon = MONSOON.includes(i);
+ // Balken (Höhe wird animiert gesetzt)
+ const bar = svgEl('rect', {
+ x: cx - BAR_W / 2, y: BASE, width: BAR_W, height: 0, rx: 3,
+ fill: isMon ? '#2f7d8a' : '#a9c7cd'
+ }, svg);
+ barNodes.push(bar);
+ // Wert-Label über dem Balken (nur für die großen Monsun-Balken sichtbar)
+ const val = svgEl('text', {
+ x: cx, y: BASE, 'text-anchor': 'middle', 'font-size': '10',
+ 'font-weight': '800', fill: '#1f4e5a', opacity: '0'
+ }, svg);
+ valNodes.push(val);
+ // Monatsname
+ const lbl = svgEl('text', {
+ x: cx, y: BASE + 18, 'text-anchor': 'middle', 'font-size': '11',
+ 'font-weight': isMon ? '800' : '600', fill: isMon ? '#1f4e5a' : '#6a6a6a'
+ }, svg);
+ lbl.textContent = m;
+ });
+
+ // „Regenzeit"-Klammer über Jun–Sep
+ const bx0 = X0 + slot * MONSOON[0] + slot * 0.10;
+ const bx1 = X0 + slot * (MONSOON[MONSOON.length - 1] + 1) - slot * 0.10;
+ svgEl('line', { x1: bx0, y1: TOP - 6, x2: bx1, y2: TOP - 6, stroke: '#e8833a', 'stroke-width': '2', 'stroke-linecap': 'round' }, svg);
+ svgEl('text', { x: (bx0 + bx1) / 2, y: TOP - 12, 'text-anchor': 'middle', 'font-size': '11', 'font-weight': '800', fill: '#e8833a' }, svg).textContent = 'Regenzeit';
+
+ // Wien-Vergleichslinie (dünn, gestrichelt) + Label
+ const wienY = yFor(WIEN_REF);
+ svgEl('line', { x1: X0, y1: wienY, x2: X1, y2: wienY, stroke: '#c85c4a', 'stroke-width': '2', 'stroke-dasharray': '6 4' }, svg);
+ svgEl('text', { x: X1, y: wienY - 6, 'text-anchor': 'end', 'font-size': '10.5', 'font-weight': '800', fill: '#c85c4a' }, svg)
+ .textContent = 'Wien: ~60 mm pro Monat';
+
+ // --- Zustand + Animation ---
+ let animId = null;
+ let current = NORMAL;
+
+ function paint(fromData, toData, scen) {
+ cancelAnimationFrame(animId);
+ const dur = 650;
+ const start = performance.now();
+
+ function tick(now) {
+ if (!root.isConnected) { cancelAnimationFrame(animId); return; }
+ const t = Math.min(1, (now - start) / dur);
+ const e = 1 - Math.pow(1 - t, 3);
+ for (let i = 0; i < barNodes.length; i++) {
+ const mm = fromData[i] + (toData[i] - fromData[i]) * e;
+ const y = yFor(mm);
+ barNodes[i].setAttribute('y', y);
+ barNodes[i].setAttribute('height', Math.max(0, BASE - y));
+ // Wert nur bei den großen Balken (> 100 mm) zeigen
+ if (toData[i] > 100) {
+ valNodes[i].setAttribute('y', y - 5);
+ valNodes[i].textContent = Math.round(mm);
+ valNodes[i].setAttribute('opacity', (t).toFixed(2));
+ } else {
+ valNodes[i].setAttribute('opacity', '0');
+ }
+ }
+ if (t < 1) animId = requestAnimationFrame(tick);
+ }
+ animId = requestAnimationFrame(tick);
+ }
+
+ function setScenario(scen) {
+ const data = scen === 'weak' ? WEAK : NORMAL;
+ const from = current;
+ current = data;
+
+ const total = data.reduce((a, b) => a + b, 0);
+ const monsoonSum = MONSOON.reduce((a, i) => a + data[i], 0);
+ const share = Math.round((monsoonSum / total) * 100);
+
+ sharePill.innerHTML = `Juni – September: ${share} % vom Jahresregen`;
+ sharePill.dataset.state = 'warn';
+ totalPill.innerHTML = `Ganzes Jahr: ${deAT(total)} mm`;
+ totalPill.dataset.state = scen === 'weak' ? 'bad' : 'good';
+ noteEl.textContent = NOTES[scen];
+
+ root.querySelectorAll('[data-scen]').forEach(b => {
+ b.classList.toggle('ge-btn-primary', b.dataset.scen === scen);
+ });
+
+ paint(from, data, scen);
+ }
+
+ root.querySelectorAll('[data-scen]').forEach(b => {
+ b.addEventListener('click', () => setScenario(b.dataset.scen));
+ });
+
+ // Startanimation von 0 hoch
+ current = NORMAL.map(() => 0);
+ setScenario('normal');
+ }
+
+ // Registry — Object.assign, damit Ladereihenfolge egal ist.
+ window.GlossarExperiments = Object.assign(window.GlossarExperiments || {}, {
+ 'monsun': monsunRain
+ });
+})();
diff --git a/App/assets/js/glossar-experiments-pyramide.js b/App/assets/js/glossar-experiments-pyramide.js
new file mode 100644
index 0000000..4c5c934
--- /dev/null
+++ b/App/assets/js/glossar-experiments-pyramide.js
@@ -0,0 +1,182 @@
+/**
+ * Glossar-Experimente — Gruppe „pyramide".
+ *
+ * Registrierung: window.GlossarExperiments[key_slug] = function (root) { … }
+ * Der Renderer (glossar.php) hängt einen leeren Container ein und ruft die
+ * Funktion mit diesem Container als einzigem Argument auf.
+ *
+ * Nur bestehende .ge-*-Klassen, kein neues CSS. Vanilla JS, keine Libraries.
+ */
+
+(function () {
+ 'use strict';
+
+ const NS = 'http://www.w3.org/2000/svg';
+ const svgEl = (tag, attrs, parent) => {
+ const el = document.createElementNS(NS, tag);
+ if (attrs) for (const k in attrs) el.setAttribute(k, attrs[k]);
+ if (parent) parent.appendChild(el);
+ return el;
+ };
+
+ // =====================================================================
+ // Experiment: Ernährungspyramide — früher (US 1992) vs. heute (DACH 2024)
+ // =====================================================================
+ function foodPyramid(root) {
+ // Ansichten: Stufen jeweils von OBEN (Spitze) nach UNTEN (Basis).
+ // dark:true → weiße Schrift auf dunkler Stufe.
+ const VIEWS = {
+ us: {
+ btn: 'Alte US-Pyramide (1992)',
+ title: '🇺🇸 Die alte US-Pyramide (1992)',
+ intro: 'So empfahlen die USA früher zu essen. Fällt dir auf, was ganz unten die größte Gruppe ist?',
+ note: 'Quelle: USDA „Food Guide Pyramid" (1992). 2005 durch MyPyramid, 2011 durch MyPlate ersetzt.',
+ levels: [
+ { lines: ['Fette, Öle & Süßes', 'sparsam'], color: '#d98c4a', dark: true,
+ hint: 'Ganz oben, klein: Fette und Süßes nur sparsam — aber alle Fette wurden hier in einen Topf geworfen.' },
+ { lines: ['Milch · Fleisch, Fisch, Ei, Nüsse', 'je 2 – 3 Portionen'], color: '#b57f9e', dark: true,
+ hint: 'Milchprodukte und Eiweiß-Lebensmittel: je 2 – 3 Portionen pro Tag.' },
+ { lines: ['Gemüse 3 – 5 · Obst 2 – 4', 'Portionen'], color: '#5a8a5e', dark: true,
+ hint: 'Gemüse und Obst — aber weiter oben als heute, also als weniger wichtig dargestellt.' },
+ { lines: ['Brot, Getreide, Reis, Nudeln', '6 – 11 Portionen'], color: '#e8c547', dark: false,
+ hint: 'Die größte Gruppe ganz unten: 6 – 11 Portionen Kohlenhydrate pro Tag! Weißmehl zählte dabei genauso wie Vollkorn.' }
+ ]
+ },
+ dach: {
+ btn: 'Heute (D-A-CH)',
+ title: '🥦 Heutige Empfehlung (D-A-CH)',
+ intro: 'So empfehlen Österreich, die Schweiz und Deutschland heute zu essen. Unten steht jetzt Wasser.',
+ note: 'Quellen: Österreichische Ernährungspyramide 2024 (BMSGPK · Gesundheit Österreich · AGES) · Schweizer Lebensmittelpyramide (SGE) · Deutschland: DGE-Empfehlungen 2024 — über ¾ pflanzlich, unter ¼ tierisch.',
+ levels: [
+ { lines: ['Süßes, Fettes & Fast Food', 'selten'], color: '#c85c4a', dark: true,
+ hint: 'Ganz oben, ganz klein: Süßes, Fettiges und Fast Food nur selten und in kleinen Mengen.' },
+ { lines: ['Fette & Öle', 'sparsam, pflanzlich'], color: '#d98c4a', dark: true,
+ hint: 'Öle und Fette sparsam — am besten pflanzliche Öle wie Rapsöl oder Olivenöl.' },
+ { lines: ['Fleisch, Fisch & Ei', 'sparsam'], color: '#b56b6b', dark: true,
+ hint: 'Fleisch, Fisch und Ei nur sparsam: höchstens rund 300 g Fleisch pro Woche.' },
+ { lines: ['Milchprodukte', '2 – 3 Portionen / Tag'], color: '#a9c0d0', dark: false,
+ hint: 'Milch, Joghurt und Käse: 2 – 3 Portionen pro Tag.' },
+ { lines: ['Getreide & Erdäpfel', '4 Portionen · am besten Vollkorn'], color: '#e8c547', dark: false,
+ hint: 'Getreide, Brot und Erdäpfel: 4 Portionen pro Tag — Vollkorn ist besser als Weißmehl. Neu: auch Hülsenfrüchte wie Linsen und Bohnen.' },
+ { lines: ['Gemüse & Obst', '5 Portionen / Tag'], color: '#5a8a5e', dark: true,
+ hint: 'Viel Gemüse und Obst: 5 Portionen pro Tag — davon 3-mal Gemüse und 2-mal Obst.' },
+ { lines: ['Wasser & ungesüßte Getränke', 'rund 1,5 Liter / Tag'], color: '#4a9cc4', dark: true,
+ hint: 'Die neue Basis: viel trinken — Wasser oder ungesüßter Tee, rund 1,5 Liter pro Tag.' }
+ ]
+ }
+ };
+
+ root.innerHTML = `
+
+
+
🥦 Ernährungspyramide
+
Vergleiche, wie sich die Empfehlung fürs Essen verändert hat. Tippe auf eine Stufe, um mehr zu erfahren.
+
+
+
+
+
+
+
+
+
Der Denkfehler der alten US-Pyramide:
+
Ganz unten, als größte Gruppe, standen Brot, Reis und Nudeln mit bis zu 6 – 11 Portionen pro Tag. Das sind sehr viele Kohlenhydrate. Dabei wurde Weißmehl und Zucker genauso behandelt wie Vollkorn — obwohl Vollkorn viel gesünder ist.
+
Außerdem galten alle Fette pauschal als „schlecht" und sollten sparsam gegessen werden — auch gesunde pflanzliche Öle. Kritiker:innen bemängelten zudem den Einfluss der Lebensmittel-Industrie auf diese Empfehlung.
+
Heute weiß man es besser: Die Basis sind Wasser, Gemüse und Obst, Getreide am besten als Vollkorn — und der Speiseplan ist überwiegend pflanzlich (rund ¾ pflanzlich, ¼ tierisch).
+
+
+
Tippe auf eine Stufe der Pyramide.
+
+
+
+ `;
+
+ const svg = root.querySelector('#fp-svg');
+ const titleEl = root.querySelector('#fp-title');
+ const introEl = root.querySelector('#fp-intro');
+ const noteEl = root.querySelector('#fp-note');
+ const detail = root.querySelector('#fp-detail');
+ const fehler = root.querySelector('#fp-fehler');
+ if (!svg || !titleEl || !noteEl || !detail || !fehler) return;
+
+ // Pyramiden-Geometrie
+ const TOP_Y = 18, BOT_Y = 350, CX = 310, HALF_BASE = 258;
+ const halfAt = (y) => HALF_BASE * (y - TOP_Y) / (BOT_Y - TOP_Y);
+
+ function drawPyramid(view) {
+ svg.innerHTML = '';
+ const levels = view.levels;
+ // Gewichte: untere Stufen etwas höher (mehr Platz für Text), Spitze schmal.
+ const weights = levels.map((_, i) => 1 + i * 0.18);
+ const wSum = weights.reduce((a, b) => a + b, 0);
+ const totalH = BOT_Y - TOP_Y;
+ let y = TOP_Y;
+ levels.forEach((lv, i) => {
+ const h = (weights[i] / wSum) * totalH;
+ const yTop = y, yBot = y + h;
+ y = yBot;
+ const lT = CX - halfAt(yTop), rT = CX + halfAt(yTop);
+ const lB = CX - halfAt(yBot), rB = CX + halfAt(yBot);
+ const g = svgEl('g', { class: 'fp-level', style: 'cursor:pointer' }, svg);
+ svgEl('polygon', {
+ points: `${lT},${yTop} ${rT},${yTop} ${rB},${yBot} ${lB},${yBot}`,
+ fill: lv.color, stroke: '#ffffff', 'stroke-width': '3', 'stroke-linejoin': 'round'
+ }, g);
+ // Beschriftung mittig
+ const midY = (yTop + yBot) / 2;
+ const fill = lv.dark ? '#ffffff' : '#1f3a2a';
+ const lines = lv.lines;
+ const startY = midY - (lines.length - 1) * 8 + 4;
+ const txt = svgEl('text', {
+ x: CX, y: startY, 'text-anchor': 'middle',
+ 'font-size': i === 0 ? '11' : '13', 'font-weight': '700',
+ fill, 'pointer-events': 'none'
+ }, g);
+ lines.forEach((ln, li) => {
+ const ts = svgEl('tspan', { x: CX, dy: li === 0 ? 0 : 16, 'font-weight': li === 0 ? '800' : '600', 'font-size': li === 0 ? (i === 0 ? '11' : '13') : '11' }, txt);
+ ts.textContent = ln;
+ });
+ g.addEventListener('click', () => {
+ detail.textContent = lv.hint;
+ svg.querySelectorAll('polygon').forEach(p => p.setAttribute('stroke', '#ffffff'));
+ const poly = g.querySelector('polygon');
+ if (poly) poly.setAttribute('stroke', '#1f3a2a');
+ });
+ });
+ }
+
+ function setView(key) {
+ root.querySelectorAll('[data-view]').forEach(b =>
+ b.classList.toggle('ge-btn-primary', b.dataset.view === key));
+
+ if (key === 'fehler') {
+ svg.style.display = 'none';
+ fehler.style.display = '';
+ titleEl.textContent = '🤔 Was war der Denkfehler?';
+ introEl.textContent = 'Warum passt die alte US-Pyramide nicht mehr zu dem, was wir heute wissen?';
+ detail.textContent = 'Lies den Text oben — und vergleiche dann die beiden Pyramiden.';
+ noteEl.textContent = 'Quellen: USDA Food Guide Pyramid (1992) · Österreichische Ernährungspyramide 2024 · DGE-Empfehlungen 2024.';
+ return;
+ }
+
+ const view = VIEWS[key];
+ svg.style.display = '';
+ fehler.style.display = 'none';
+ titleEl.textContent = view.title;
+ introEl.textContent = view.intro;
+ noteEl.textContent = view.note;
+ detail.textContent = 'Tippe auf eine Stufe der Pyramide.';
+ drawPyramid(view);
+ }
+
+ root.querySelectorAll('[data-view]').forEach(b =>
+ b.addEventListener('click', () => setView(b.dataset.view)));
+
+ setView('us');
+ }
+
+ // Registry — Key entspricht key_slug aus DB.
+ window.GlossarExperiments = Object.assign(window.GlossarExperiments || {}, {
+ 'ernaehrungspyramide': foodPyramid
+ });
+})();
diff --git a/App/assets/js/glossar-experiments-schoko.js b/App/assets/js/glossar-experiments-schoko.js
new file mode 100644
index 0000000..17579e8
--- /dev/null
+++ b/App/assets/js/glossar-experiments-schoko.js
@@ -0,0 +1,309 @@
+/**
+ * Glossar-Experimente — Gruppe „schoko".
+ *
+ * Registrierung: window.GlossarExperiments[key_slug] = function (root) { … }
+ * Der Renderer (glossar.php) hängt einen leeren Container ein und ruft die
+ * Funktion mit diesem Container als einzigem Argument auf.
+ *
+ * Nur `.ge-*`-Klassen aus glossar.php verwenden, kein neues CSS.
+ */
+
+(function () {
+ 'use strict';
+
+ const NS = 'http://www.w3.org/2000/svg';
+ const svgEl = (tag, attrs, parent) => {
+ const el = document.createElementNS(NS, tag);
+ if (attrs) for (const k in attrs) el.setAttribute(k, attrs[k]);
+ if (parent) parent.appendChild(el);
+ return el;
+ };
+
+ // =====================================================================
+ // Experiment: Von der Kakaobohne zur Schokolade
+ // Teil 1 — Fließband mit Maschinen (Schritt für Schritt)
+ // Teil 2 — Mischmaschine: Kakao / Zucker / Milch -> Schoko-Sorte
+ // =====================================================================
+ function beanToBar(root) {
+ root.innerHTML = `
+
+
+
🍫 Von der Kakaobohne zur Schokolade
+
Schicke die Kakaobohne über das Fließband. Tippe auf ▶ Weiter und schau, was jede Maschine macht.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🥣 Die Mischmaschine
+
Jetzt kommt die Kakaomasse in die Mischmaschine. Stelle mit den Reglern ein — oder tippe einen fertigen Vorschlag.
+
+
+
+
+
+
+
+
+
+
+
keinemittelsehr viel
+
+
+
+
+
keinermittelsehr viel
+
+
+
+
+
keineetwasviel
+
+
+
+
+
+
+
+
+
So entsteht Schokolade: Kakaomasse + Kakaobutter + Zucker (bei Milchschokolade auch Milchpulver). Weiße Schokolade hat gar keine Kakaomasse, nur die helle Kakaobutter. Beispielwerte: dunkle Schokolade 50 – 90 % Kakao, Vollmilch 25 – 40 %, weiße 0 %. Quelle: Leitsätze für Kakao und Schokoladen · EU-Kakaoverordnung 2000/36/EG.