/** * Glossar-Experimente „Natur" — Astronomie, Klima, Hochwasser. * * Registrierung wie in glossar-experiments.js: * window.GlossarExperiments[key_slug] = function (root) { … } * Der Renderer (glossar.php) übergibt einen leeren Container; das Widget * baut seinen DOM selbst und bindet seine Events. Styles: .ge-* aus glossar.php. * * Richtlinien: * - Keine externen Libraries, Vanilla JS + SVG. * - Mobile-/Tap-freundlich, keine Hover-Abhängigkeit. * - Zahlen mit kurzer Quellenangabe in der .ge-note. * - Animations-Loops stoppen selbst, sobald der Container aus dem DOM fällt * (root.isConnected), damit kein Loop nach Modal-Schließen weiterläuft. */ (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 fmtDE = (n, dec) => n.toFixed(dec === undefined ? 1 : dec).replace('.', ','); // ===================================================================== // Experiment: Gebundene Rotation — warum wir immer dieselbe Mondseite sehen // ===================================================================== function tidalLock(root) { root.innerHTML = `

🌗 Warum sehen wir immer dieselbe Mondseite?

Der Mond umkreist die Erde. Seine rot markierte Hälfte zeigt dir, wie er sich dabei um sich selbst dreht. Schalte um und vergleiche!

Der Mond dreht sich in 27,3 Tagen genau einmal um sich selbst — exakt so lange, wie er für einen Umlauf um die Erde braucht. Diese „gebundene Rotation" entstand durch die Gezeitenkräfte der Erde. Quelle: NASA Moon Fact Sheet (siderische Rotation = siderischer Umlauf = 27,32 Tage).

`; const svg = root.querySelector('.ge-track'); const insightEl = root.querySelector('[data-ref="insight"]'); const viewEl = root.querySelector('[data-ref="view"]'); const playBtn = root.querySelector('[data-act="play"]'); const CX = 300, CY = 172, ORBIT_R = 118, MOON_R = 16; // Weltall-Hintergrund + Sterne svgEl('rect', { x: 0, y: 0, width: 600, height: 340, fill: '#152430', rx: 8 }, svg); for (let i = 0; i < 40; i++) { svgEl('circle', { cx: Math.round(Math.random() * 590 + 5), cy: Math.round(Math.random() * 330 + 5), r: (Math.random() * 1.2 + 0.4).toFixed(1), fill: '#dae8ec', opacity: (0.3 + Math.random() * 0.5).toFixed(2) }, svg); } // Mondbahn svgEl('circle', { cx: CX, cy: CY, r: ORBIT_R, fill: 'none', stroke: '#4a7c8a', 'stroke-width': '1', 'stroke-dasharray': '4 5', opacity: '.7' }, svg); // Erde svgEl('circle', { cx: CX, cy: CY, r: 30, fill: '#2e6b8a' }, svg); svgEl('circle', { cx: CX - 8, cy: CY - 6, r: 10, fill: '#5a8a5e', opacity: '.9' }, svg); svgEl('circle', { cx: CX + 10, cy: CY + 9, r: 7, fill: '#5a8a5e', opacity: '.9' }, svg); const earthLabel = svgEl('text', { x: CX, y: CY + 48, 'text-anchor': 'middle', 'font-size': '12', 'font-weight': '700', fill: '#dae8ec' }, svg); earthLabel.textContent = 'Erde'; // Sichtlinie Erde → Mond const sight = svgEl('line', { x1: CX, y1: CY, x2: CX + ORBIT_R, y2: CY, stroke: '#e8c547', 'stroke-width': '1', 'stroke-dasharray': '2 4', opacity: '.6' }, svg); // Mond: Gruppe mit heller + rot markierter Hälfte (markierte Hälfte zeigt lokal nach +x) const moonG = svgEl('g', {}, svg); svgEl('circle', { cx: 0, cy: 0, r: MOON_R, fill: '#cfc9bc', stroke: '#8a857a', 'stroke-width': '1' }, moonG); svgEl('path', { d: `M 0 ${-MOON_R} A ${MOON_R} ${MOON_R} 0 0 1 0 ${MOON_R} Z`, fill: '#c85c4a' }, moonG); // Tag-Zähler const dayLabel = svgEl('text', { x: 590, y: 24, 'text-anchor': 'end', 'font-size': '12', 'font-weight': '700', fill: '#dae8ec' }, svg); const PERIOD_DAYS = 27.3; const ORBIT_MS = 12000; // 1 Umlauf in 12 s let mode = 'locked'; let playing = true; let angle = -Math.PI / 2; // Start oben let last = null; let lastViewTxt = ''; function insightText() { return mode === 'locked' ? '🔒 Rotation = Umlauf: Die rote Seite zeigt immer zur Erde. Deshalb sehen wir die Mond-Rückseite nie.' : '🔓 Ohne Eigendrehung: Die rote Seite zeigt mal zur Erde, mal weg — wir würden im Lauf eines Monats den ganzen Mond sehen.'; } function applyMode(m) { mode = m; root.querySelectorAll('[data-mode]').forEach(b => b.classList.toggle('ge-btn-primary', b.dataset.mode === m)); insightEl.textContent = insightText(); insightEl.dataset.state = m === 'locked' ? 'good' : 'warn'; } function draw() { const mx = CX + Math.cos(angle) * ORBIT_R; const my = CY + Math.sin(angle) * ORBIT_R; const aDeg = angle * 180 / Math.PI; const rot = mode === 'locked' ? aDeg + 180 : 0; moonG.setAttribute('transform', `translate(${mx.toFixed(1)} ${my.toFixed(1)}) rotate(${rot.toFixed(1)})`); sight.setAttribute('x2', mx.toFixed(1)); sight.setAttribute('y2', my.toFixed(1)); const day = ((angle + Math.PI / 2) / (2 * Math.PI)) * PERIOD_DAYS; const dayShown = ((day % PERIOD_DAYS) + PERIOD_DAYS) % PERIOD_DAYS; dayLabel.textContent = 'Tag ' + fmtDE(dayShown, 0) + ' von 27,3'; // Was sieht man von der Erde aus? Winkel zwischen markierter Richtung und Richtung Mond→Erde const toEarth = aDeg + 180; let diff = ((rot - toEarth) % 360 + 360) % 360; if (diff > 180) diff = 360 - diff; const txt = diff < 90 ? '👁 Von der Erde aus sichtbar: die rote Seite' : '👁 Von der Erde aus sichtbar: die Rückseite'; if (txt !== lastViewTxt) { viewEl.textContent = txt; lastViewTxt = txt; } } function tick(now) { if (!root.isConnected) return; // Modal geschlossen → Loop beenden if (playing) { if (last !== null) angle += ((now - last) / ORBIT_MS) * 2 * Math.PI; draw(); } last = now; requestAnimationFrame(tick); } playBtn.addEventListener('click', () => { playing = !playing; playBtn.textContent = playing ? '⏸ Pause' : '▶ Weiter'; }); root.querySelectorAll('[data-mode]').forEach(b => { b.addEventListener('click', () => { applyMode(b.dataset.mode); draw(); }); }); applyMode('locked'); draw(); requestAnimationFrame(tick); } // ===================================================================== // Experiment: Kernschatten — Mond durch den Erdschatten schieben // ===================================================================== function umbraCones(root) { root.innerHTML = `

🌑 Schiebe den Mond durch den Erdschatten

Die Sonne beleuchtet die Erde — dahinter entstehen Kernschatten (ganz dunkel) und Halbschatten (teilweise dunkel). Bewege den Mond mit dem Regler auf seiner Bahn.

oberhalbmitten im Schattenunterhalb

Größen und Abstände sind nicht maßstäblich. Real ist der Kernschatten der Erde in Mondentfernung rund 9.000 km breit — etwa 2,6 Mond-Durchmesser. Bei einer totalen Mondfinsternis leuchtet der Mond rötlich („Blutmond"), weil die Erdatmosphäre rotes Sonnenlicht in den Schatten lenkt. Weil die Mondbahn um 5° geneigt ist, verfehlt der Mond den Schatten meistens — darum gibt es nicht jeden Vollmond eine Finsternis. Quelle: NASA Eclipse Web Site (F. Espenak).

`; const svg = root.querySelector('.ge-track'); const rng = root.querySelector('#ge-umbra-range'); const verdict = root.querySelector('[data-ref="verdict"]'); // Geometrie-Konstanten (Zeichnung und Klassifikation nutzen dieselben Werte) const SUN = { x: 60, y: 160, r: 32 }; const EARTH = { x: 280, y: 160, r: 24 }; const MOON_X = 460, MOON_R = 7; const D = EARTH.x - SUN.x; // Kernschatten-Grenze: Linie von Sonnen-Oberkante durch Erd-Oberkante const mU = (EARTH.r - SUN.r) / D * -1; // Steigung der oberen Umbra-Grenze relativ zur Achse // Halbschatten-Grenze: Linie von Sonnen-Unterkante durch Erd-Oberkante const mP = (SUN.r + EARTH.r) / D; const hU = x => Math.max(0, EARTH.r - mU * (x - EARTH.x)); // halbe Kernschatten-Höhe bei x const hP = x => EARTH.r + mP * (x - EARTH.x); // halbe Halbschatten-Höhe bei x // Weltall + Sterne svgEl('rect', { x: 0, y: 0, width: 600, height: 320, fill: '#152430', rx: 8 }, svg); for (let i = 0; i < 30; i++) { svgEl('circle', { cx: Math.round(Math.random() * 590 + 5), cy: Math.round(Math.random() * 310 + 5), r: (Math.random() * 1.1 + 0.4).toFixed(1), fill: '#dae8ec', opacity: (0.3 + Math.random() * 0.5).toFixed(2) }, svg); } // Halbschatten-Polygon (heller), dann Kernschatten (dunkler) darüber const X_END = 600; svgEl('polygon', { points: `${EARTH.x},${EARTH.y - EARTH.r} ${X_END},${EARTH.y - hP(X_END)} ${X_END},${EARTH.y + hP(X_END)} ${EARTH.x},${EARTH.y + EARTH.r}`, fill: '#4a5a66', opacity: '.35' }, svg); svgEl('polygon', { points: `${EARTH.x},${EARTH.y - EARTH.r} ${X_END},${EARTH.y - hU(X_END)} ${X_END},${EARTH.y + hU(X_END)} ${EARTH.x},${EARTH.y + EARTH.r}`, fill: '#0c151c', opacity: '.8' }, svg); // Beschriftung der Schattenzonen const lblU = svgEl('text', { x: 520, y: EARTH.y + 4, 'text-anchor': 'middle', 'font-size': '10', 'font-weight': '700', fill: '#8aa4b0' }, svg); lblU.textContent = 'Kernschatten'; const lblP = svgEl('text', { x: 520, y: EARTH.y - hP(520) - 6, 'text-anchor': 'middle', 'font-size': '10', fill: '#8aa4b0' }, svg); lblP.textContent = 'Halbschatten'; // Sonne mit Strahlenkranz svgEl('circle', { cx: SUN.x, cy: SUN.y, r: SUN.r + 8, fill: '#f4c94e', opacity: '.25' }, svg); svgEl('circle', { cx: SUN.x, cy: SUN.y, r: SUN.r, fill: '#f4c94e' }, svg); const sunLbl = svgEl('text', { x: SUN.x, y: SUN.y + SUN.r + 18, 'text-anchor': 'middle', 'font-size': '12', 'font-weight': '700', fill: '#f4c94e' }, svg); sunLbl.textContent = 'Sonne'; // Erde svgEl('circle', { cx: EARTH.x, cy: EARTH.y, r: EARTH.r, fill: '#2e6b8a' }, svg); svgEl('circle', { cx: EARTH.x - 6, cy: EARTH.y - 5, r: 8, fill: '#5a8a5e', opacity: '.9' }, svg); // Nachtseite der Erde svgEl('path', { d: `M ${EARTH.x} ${EARTH.y - EARTH.r} A ${EARTH.r} ${EARTH.r} 0 0 1 ${EARTH.x} ${EARTH.y + EARTH.r} Z`, fill: '#0c151c', opacity: '.55' }, svg); const earthLbl = svgEl('text', { x: EARTH.x, y: EARTH.y + EARTH.r + 18, 'text-anchor': 'middle', 'font-size': '12', 'font-weight': '700', fill: '#dae8ec' }, svg); earthLbl.textContent = 'Erde'; // Mondbahn (angedeutet, senkrecht) svgEl('line', { x1: MOON_X, y1: 30, x2: MOON_X, y2: 290, stroke: '#4a7c8a', 'stroke-width': '1', 'stroke-dasharray': '3 5', opacity: '.6' }, svg); // Mond: Basis + Schatten-Halbscheibe const moon = svgEl('circle', { cx: MOON_X, cy: 60, r: MOON_R, fill: '#cfc9bc', stroke: '#8a857a', 'stroke-width': '.8' }, svg); const moonHalf = svgEl('path', { d: '', fill: '#241a16', opacity: '0' }, svg); const moonLbl = svgEl('text', { x: MOON_X + 14, y: 64, 'font-size': '11', 'font-weight': '700', fill: '#dae8ec' }, svg); moonLbl.textContent = 'Mond'; const HU = hU(MOON_X), HPn = hP(MOON_X); function update() { const yOff = +rng.value * 0.9; // Slider ±100 → ±90 px const my = EARTH.y + yOff; moon.setAttribute('cy', my.toFixed(1)); moonLbl.setAttribute('y', (my + 4).toFixed(1)); const a = Math.abs(yOff); let state, txt, fill, halfOp = 0; if (a + MOON_R <= HU) { state = 'bad'; txt = '🔴 Totale Mondfinsternis — der Mond steht ganz im Kernschatten (Blutmond)'; fill = '#a63c2a'; } else if (a - MOON_R < HU) { state = 'warn'; txt = '🟠 Partielle Mondfinsternis — nur ein Teil des Mondes taucht in den Kernschatten'; fill = '#cfc9bc'; halfOp = 0.85; } else if (a - MOON_R < HPn) { state = 'now'; txt = '🌫 Halbschattenfinsternis — der Mond wird nur leicht dunkler, mit freiem Auge kaum sichtbar'; fill = '#a8a397'; } else { state = 'good'; txt = '🌕 Keine Finsternis — der Mond verfehlt den Erdschatten (der Normalfall)'; fill = '#cfc9bc'; } moon.setAttribute('fill', fill); // Bei partieller Finsternis: die dem Schattenzentrum zugewandte Hälfte verdunkeln if (halfOp > 0) { const sweep = yOff > 0 ? 0 : 1; // Mond unterhalb → obere Hälfte im Schatten moonHalf.setAttribute('d', `M ${MOON_X - MOON_R} ${my.toFixed(1)} A ${MOON_R} ${MOON_R} 0 0 ${sweep} ${MOON_X + MOON_R} ${my.toFixed(1)} Z`); } moonHalf.setAttribute('opacity', halfOp); verdict.textContent = txt; verdict.dataset.state = state; } rng.addEventListener('input', update); update(); } // ===================================================================== // Experiment: Keeling-Kurve — CO₂-Messung 1958 bis heute, animiert // ===================================================================== function keelingCurve(root) { root.innerHTML = `

📈 Die Keeling-Kurve zeichnet sich selbst

Seit 1958 wird auf dem Vulkan Mauna Loa (Hawaii) die CO₂-Konzentration gemessen. Starte die Aufzeichnung — an wichtigen Stellen hält die Kurve an.

Kurve vereinfacht nachgebildet: Jahresmittel von 315 ppm (1958) auf rund 430 ppm (2025), Sägezahn ±3 ppm durch die Jahreszeiten der Nordhalbkugel. Quelle: Scripps CO₂ Program / NOAA Global Monitoring Laboratory, Mauna Loa.

`; const svg = root.querySelector('.ge-track'); const playBtn = root.querySelector('[data-act="play"]'); const nextBtn = root.querySelector('[data-act="next"]'); const milestoneEl = root.querySelector('[data-ref="milestone"]'); const seasonEl = root.querySelector('[data-ref="season"]'); // Plot-Geometrie const X0 = 62, X1 = 580, Y0 = 275, Y1 = 30; const YEAR0 = 1958, YEAR1 = 2025.5, PPM0 = 300, PPM1 = 445; const xOf = year => X0 + (year - YEAR0) / (YEAR1 - YEAR0) * (X1 - X0); const yOf = ppm => Y0 - (ppm - PPM0) / (PPM1 - PPM0) * (Y0 - Y1); // Vereinfachtes Kurvenmodell (an Scripps/NOAA-Jahresmittel angelehnt) const ppmAt = year => { const t = year - 1958; const annual = 315 + 0.55 * t + 0.0175 * t * t; const frac = year - Math.floor(year); return annual + 3.0 * Math.cos(2 * Math.PI * (frac - 0.30)); }; // Achsen + Gitter [320, 360, 400, 440].forEach(p => { svgEl('line', { x1: X0, y1: yOf(p), x2: X1, y2: yOf(p), stroke: '#dae8ec', 'stroke-width': '1' }, svg); const t = svgEl('text', { x: X0 - 6, y: yOf(p) + 4, 'text-anchor': 'end', 'font-size': '10', fill: '#4a4a4a' }, svg); t.textContent = p; }); const yTitle = svgEl('text', { x: X0 - 6, y: Y1 - 8, 'text-anchor': 'end', 'font-size': '10', 'font-weight': '700', fill: '#4a4a4a' }, svg); yTitle.textContent = 'ppm'; [1960, 1980, 2000, 2020].forEach(yr => { svgEl('line', { x1: xOf(yr), y1: Y0, x2: xOf(yr), y2: Y0 + 5, stroke: '#4a4a4a', 'stroke-width': '1' }, svg); const t = svgEl('text', { x: xOf(yr), y: Y0 + 18, 'text-anchor': 'middle', 'font-size': '10', fill: '#4a4a4a' }, svg); t.textContent = yr; }); svgEl('line', { x1: X0, y1: Y0, x2: X1, y2: Y0, stroke: '#1f4e5a', 'stroke-width': '1.2' }, svg); svgEl('line', { x1: X0, y1: Y0, x2: X0, y2: Y1, stroke: '#1f4e5a', 'stroke-width': '1.2' }, svg); // Datenpunkte (monatlich) const pts = []; for (let y = YEAR0; y <= YEAR1; y += 1 / 12) { pts.push({ year: y, ppm: ppmAt(y), x: xOf(y), y: yOf(ppmAt(y)) }); } const line = svgEl('polyline', { points: '', fill: 'none', stroke: '#c85c4a', 'stroke-width': '2', 'stroke-linejoin': 'round' }, svg); const head = svgEl('circle', { cx: X0, cy: yOf(315), r: 4, fill: '#c85c4a', opacity: '0' }, svg); const readout = svgEl('text', { x: X1, y: Y1 + 4, 'text-anchor': 'end', 'font-size': '13', 'font-weight': '800', fill: '#1f4e5a' }, svg); const markerG = svgEl('g', {}, svg); const milestones = [ { year: 1968, txt: '🌲 Der Sägezahn ist die „Atmung" der Vegetation: Jeden Nordsommer nimmt sie CO₂ auf (Kurve sinkt), jeden Winter gibt sie es wieder ab (Kurve steigt).' }, { year: 1990, txt: '📋 Um 354 ppm — der erste IPCC-Bericht (1990) warnt vor der Erwärmung. Die Kurve steigt immer schneller.' }, { year: 2015, txt: '🌍 400 ppm überschritten — im Jahr des Pariser Klimaabkommens. So viel CO₂ war seit Millionen Jahren nicht in der Luft.' }, { year: 2025.4, txt: '🏁 Heute: rund 430 ppm — gut ein Drittel mehr als 1958. Der Sägezahn bleibt, der Anstieg leider auch.' } ]; let idx = 0, state = 'idle', ptStrings = [], msIdx = 0; function showMilestone(m) { state = 'paused'; milestoneEl.textContent = m.txt; milestoneEl.style.display = ''; milestoneEl.dataset.state = 'now'; nextBtn.disabled = false; nextBtn.classList.add('ge-btn-primary'); svgEl('circle', { cx: xOf(m.year), cy: yOf(ppmAt(m.year)), r: 5, fill: 'none', stroke: '#1f4e5a', 'stroke-width': '2' }, markerG); } function updateSeason() { if (idx < 3) return; const rising = pts[idx - 1].ppm > pts[idx - 3].ppm; seasonEl.style.display = ''; seasonEl.dataset.state = rising ? 'warn' : 'good'; seasonEl.textContent = rising ? '🍂 Kurve steigt gerade: Nordwinter — Laub verrottet, wenig Photosynthese' : '🌱 Kurve sinkt gerade: Nordsommer — die Vegetation nimmt CO₂ auf'; } function tick() { if (!root.isConnected || state !== 'playing') return; for (let s = 0; s < 2 && idx < pts.length; s++) { const p = pts[idx++]; ptStrings.push(p.x.toFixed(1) + ',' + p.y.toFixed(1)); } line.setAttribute('points', ptStrings.join(' ')); const cur = pts[Math.min(idx, pts.length) - 1]; head.setAttribute('cx', cur.x.toFixed(1)); head.setAttribute('cy', cur.y.toFixed(1)); head.setAttribute('opacity', '1'); readout.textContent = Math.floor(cur.year) + ' · ' + fmtDE(cur.ppm, 0) + ' ppm'; updateSeason(); if (msIdx < milestones.length && cur.year >= milestones[msIdx].year) { showMilestone(milestones[msIdx++]); return; } if (idx >= pts.length) { state = 'done'; return; } requestAnimationFrame(tick); } function start() { idx = 0; msIdx = 0; ptStrings = []; markerG.innerHTML = ''; line.setAttribute('points', ''); milestoneEl.style.display = 'none'; seasonEl.style.display = 'none'; nextBtn.disabled = true; nextBtn.classList.remove('ge-btn-primary'); playBtn.textContent = '↺ Von vorn'; state = 'playing'; requestAnimationFrame(tick); } function resume() { if (state !== 'paused') return; nextBtn.disabled = true; nextBtn.classList.remove('ge-btn-primary'); milestoneEl.style.display = 'none'; if (idx >= pts.length) { state = 'done'; return; } state = 'playing'; requestAnimationFrame(tick); } playBtn.addEventListener('click', start); nextBtn.addEventListener('click', resume); } // ===================================================================== // Experiment: Pegel / HQ100 — Regen aufdrehen, Pegel verzögert beobachten // ===================================================================== function riverGauge(root) { root.innerHTML = `

🌧 Regen aufdrehen — was macht der Pegel?

Stelle die Regen-Intensität ein und beobachte die Pegellatte. Der Fluss reagiert verzögert — genau wie in echt.

trockenLandregenStarkregenExtremereignis

HQ30 / HQ100 = Hochwasser, das im langjährigen Mittel statistisch einmal in 30 bzw. 100 Jahren erreicht oder überschritten wird (Quelle: eHYD, BML Österreich). Die Marken hier sind Beispielwerte — jeder echte Pegel hat eigene Grenzwerte. Die Verzögerung entsteht, weil Regen erst durch Boden, Bäche und Zuflüsse wandern muss, bis er am Pegel ankommt.

`; const svg = root.querySelector('.ge-track'); const rng = root.querySelector('#ge-rain-range'); const rainValEl = root.querySelector('[data-ref="rainval"]'); const levelEl = root.querySelector('[data-ref="level"]'); const trendEl = root.querySelector('[data-ref="trend"]'); // cm → y (Flussbett bei y=290, 400 cm bei y=60) const BED_Y = 290, CM_SCALE = (290 - 60) / 400; const yOfCm = cm => BED_Y - cm * CM_SCALE; const BANK_CM = 280; // Uferoberkante const MARKS = [ { cm: 220, label: 'Vorwarnstufe', color: '#e8c547' }, { cm: 300, label: 'HQ30', color: '#e8833a' }, { cm: 360, label: 'HQ100', color: '#c85c4a' } ]; // Himmel const sky = svgEl('rect', { x: 0, y: 0, width: 600, height: 320, fill: '#dae8ec', rx: 8 }, svg); // Wasser (Polygon, dynamisch) const water = svgEl('polygon', { points: '', fill: '#4a7c8a', opacity: '.85' }, svg); const floodL = svgEl('rect', { x: 0, y: 0, width: 0, height: 0, fill: '#4a7c8a', opacity: '.7' }, svg); const floodR = svgEl('rect', { x: 0, y: 0, width: 0, height: 0, fill: '#4a7c8a', opacity: '.7' }, svg); // Ufer-Profil: links Flur — Böschung — Flussbett — Böschung — rechts Flur const bankY = yOfCm(BANK_CM); // = 129 svgEl('polygon', { points: `0,320 0,${bankY} 90,${bankY} 150,${BED_Y} 150,320`, fill: '#5a8a5e' }, svg); svgEl('polygon', { points: `600,320 600,${bankY} 410,${bankY} 350,${BED_Y} 350,320`, fill: '#5a8a5e' }, svg); svgEl('rect', { x: 150, y: BED_Y, width: 200, height: 30, fill: '#6b4a2e' }, svg); // Wolke + Regen const cloud = svgEl('g', {}, svg); svgEl('ellipse', { cx: 150, cy: 42, rx: 46, ry: 20, fill: '#8aa4b0' }, cloud); svgEl('ellipse', { cx: 195, cy: 50, rx: 38, ry: 17, fill: '#8aa4b0' }, cloud); svgEl('ellipse', { cx: 110, cy: 52, rx: 32, ry: 15, fill: '#8aa4b0' }, cloud); const rainG = svgEl('g', { stroke: '#4a7c8a', 'stroke-width': '2', 'stroke-linecap': 'round' }, svg); // Marken-Linien über dem Wasserbereich MARKS.forEach(m => { const y = yOfCm(m.cm); svgEl('line', { x1: 60, y1: y, x2: 470, y2: y, stroke: m.color, 'stroke-width': '2', 'stroke-dasharray': '6 4' }, svg); const t = svgEl('text', { x: 478, y: y + 4, 'font-size': '11', 'font-weight': '800', fill: m.color }, svg); t.textContent = m.label + ' · ' + m.cm + ' cm'; }); // Pegellatte const LATTE_X = 430; svgEl('rect', { x: LATTE_X - 7, y: yOfCm(400), width: 14, height: BED_Y - yOfCm(400), fill: '#fff', stroke: '#1f4e5a', 'stroke-width': '1.2', rx: 2 }, svg); for (let cm = 0; cm <= 400; cm += 50) { const w = cm % 100 === 0 ? 10 : 6; svgEl('line', { x1: LATTE_X - 7, y1: yOfCm(cm), x2: LATTE_X - 7 + w, y2: yOfCm(cm), stroke: '#1f4e5a', 'stroke-width': '1' }, svg); if (cm % 100 === 0 && cm > 0) { const t = svgEl('text', { x: LATTE_X + 12, y: yOfCm(cm) + 3, 'font-size': '9', fill: '#1f4e5a' }, svg); t.textContent = cm; } } // Ablese-Pfeil an der Latte const pointer = svgEl('polygon', { points: '', fill: '#1f4e5a' }, svg); // Böschungs-Geometrie fürs Wasser-Polygon const slope = (BED_Y - bankY) / 60; // px pro px (150-90 bzw. 350-410) function waterPoly(levelCm) { const yW = yOfCm(Math.min(levelCm, BANK_CM)); const lxx = 150 - (BED_Y - yW) / slope; const rxx = 350 + (BED_Y - yW) / slope; return `${lxx.toFixed(1)},${yW.toFixed(1)} ${rxx.toFixed(1)},${yW.toFixed(1)} 350,${BED_Y} 150,${BED_Y}`; } function drawRain(intensity) { rainG.innerHTML = ''; const n = Math.round(intensity / 7); for (let i = 0; i < n; i++) { const x = 80 + Math.random() * 150; const y = 70 + Math.random() * 50; svgEl('line', { x1: x, y1: y, x2: x - 3, y2: y + 12, opacity: '.8' }, rainG); } cloud.setAttribute('opacity', intensity > 0 ? 1 : 0.45); sky.setAttribute('fill', intensity > 60 ? '#b8c9cf' : '#dae8ec'); } let inflow = 10, level = 130, lastRedraw = 0; function statusFor(cm) { if (cm >= 360) return { s: 'bad', t: '🔴 HQ100 erreicht — Jahrhunderthochwasser' }; if (cm >= 300) return { s: 'warn', t: '🟠 HQ30 überschritten — großes Hochwasser' }; if (cm >= 220) return { s: 'now', t: '🟡 Vorwarnstufe überschritten' }; return { s: 'good', t: '🟢 Normaler Wasserstand' }; } function tick() { if (!root.isConnected) return; const rain = +rng.value; // Zweistufige Verzögerung: Boden/Zufluss-Speicher → Pegel inflow += (rain - inflow) * 0.008; const target = 100 + inflow * 3.0; const before = level; level += (target - level) * 0.015; const cm = Math.max(0, Math.min(400, level)); water.setAttribute('points', waterPoly(cm)); // Ausuferung über die Uferkante if (cm > BANK_CM) { const h = (cm - BANK_CM) * CM_SCALE; floodL.setAttribute('x', 0); floodL.setAttribute('y', bankY - h); floodL.setAttribute('width', 150); floodL.setAttribute('height', h); floodR.setAttribute('x', 350); floodR.setAttribute('y', bankY - h); floodR.setAttribute('width', 250); floodR.setAttribute('height', h); } else { floodL.setAttribute('width', 0); floodL.setAttribute('height', 0); floodR.setAttribute('width', 0); floodR.setAttribute('height', 0); } const y = yOfCm(cm); pointer.setAttribute('points', `${LATTE_X - 22},${y - 5} ${LATTE_X - 22},${y + 5} ${LATTE_X - 10},${y}`); // UI-Texte gedrosselt aktualisieren const now = performance.now(); if (now - lastRedraw > 180) { lastRedraw = now; const st = statusFor(cm); levelEl.textContent = 'Pegel: ' + fmtDE(cm, 0) + ' cm — ' + st.t; levelEl.dataset.state = st.s; const d = level - before; trendEl.textContent = d > 0.05 ? '↗ Pegel steigt (verzögert)' : d < -0.05 ? '↘ Pegel fällt (verzögert)' : '→ Pegel stabil'; } requestAnimationFrame(tick); } rng.addEventListener('input', () => { rainValEl.textContent = rng.value; drawRain(+rng.value); }); drawRain(+rng.value); requestAnimationFrame(tick); } // ===================================================================== // Experiment: Wärmeausdehnung — wärmeres Meer braucht mehr Platz // ===================================================================== function thermalExpansion(root) { root.innerHTML = `

🌡 Wärmeres Wasser braucht mehr Platz

Erwärme das Meer mit dem Regler. Der Meeresspiegel steigt hier allein durch die Ausdehnung des Wassers — noch ganz ohne Gletscherschmelze.

heute+1 °C+2 °C+3 °C+4 °C

Vereinfachung: Auf lange Sicht dehnt sich das Meerwasser um rund 0,26 m je °C Erwärmung aus (Hieronymus 2019, Environ. Res. Lett. — Update zur IPCC-AR5-Schätzung von bis zu 0,42 m je °C nach Levermann et al. 2013). Schmelzwasser von Gletschern und Eisschilden kommt zusätzlich dazu. Darstellung stark überhöht.

`; const svg = root.querySelector('.ge-gh-svg'); const rng = root.querySelector('#ge-thx-range'); const dtEl = root.querySelector('[data-ref="dt"]'); const riseEl = root.querySelector('[data-ref="rise"]'); const statusEl = root.querySelector('[data-ref="status"]'); const M_PER_DEG = 0.26; // m Anstieg je °C (nur Ausdehnung, Langfrist-Näherung) const PX_PER_M = 45; // Überhöhung: 1 m = 45 px const Y_SEA0 = 180; // heutiger Meeresspiegel // Strand-Profil: flaches Vorland links, Böschung nach rechts oben const G0 = { x: 90, y: 236 }, G1 = { x: 600, y: 110 }; const gSlope = (G0.y - G1.y) / (G1.x - G0.x); // px sinken je px nach rechts: 0.247 const groundY = x => x <= G0.x ? 238 : G0.y - gSlope * (x - G0.x); const shoreX = yW => G0.x + (G0.y - yW) / gSlope; // Boden svgEl('polygon', { points: `0,300 0,240 ${G0.x},${G0.y} ${G1.x},${G1.y} 600,300`, fill: '#e8d5b5' }, svg); // Referenz-Objekte am Strand (Höhe über heutigem Meer) const refs = [ { m: 0.3, label: 'Strandhütte', emoji: '🛖' }, { m: 0.6, label: 'Strandweg', emoji: '🚏' }, { m: 1.2, label: 'Haus an der Düne', emoji: '🏠' } ]; refs.forEach(r => { const gy = Y_SEA0 - r.m * PX_PER_M; const gx = shoreX(gy); r.x = gx; r.groundY = gy; const e = svgEl('text', { x: gx, y: gy - 4, 'text-anchor': 'middle', 'font-size': '22' }, svg); e.textContent = r.emoji; const t = svgEl('text', { x: gx, y: gy - 30, 'text-anchor': 'middle', 'font-size': '9', 'font-weight': '700', fill: '#4a4a4a' }, svg); t.textContent = r.label + ' · +' + fmtDE(r.m, 1) + ' m'; }); // Wasser const water = svgEl('polygon', { points: '', fill: '#4a7c8a', opacity: '.85' }, svg); const waterLine = svgEl('line', { x1: 0, y1: Y_SEA0, x2: 0, y2: Y_SEA0, stroke: '#1f4e5a', 'stroke-width': '1.5' }, svg); // Heutiger Meeresspiegel als Referenzlinie svgEl('line', { x1: 0, y1: Y_SEA0, x2: shoreX(Y_SEA0) + 40, y2: Y_SEA0, stroke: '#1f4e5a', 'stroke-width': '1', 'stroke-dasharray': '5 4', opacity: '.7' }, svg); const refLbl = svgEl('text', { x: 8, y: Y_SEA0 + 14, 'font-size': '9', fill: '#1f4e5a' }, svg); refLbl.textContent = 'Meeresspiegel heute'; // Maßstabs-Lineal links [0, 0.5, 1].forEach(m => { const y = Y_SEA0 - m * PX_PER_M; svgEl('line', { x1: 30, y1: y, x2: 42, y2: y, stroke: '#1f4e5a', 'stroke-width': '1.5' }, svg); const t = svgEl('text', { x: 46, y: y + 3, 'font-size': '9', fill: '#1f4e5a' }, svg); t.textContent = m === 0 ? '±0' : '+' + fmtDE(m, 1) + ' m'; }); svgEl('line', { x1: 36, y1: Y_SEA0, x2: 36, y2: Y_SEA0 - PX_PER_M, stroke: '#1f4e5a', 'stroke-width': '1' }, svg); // Wärme-Symbol im Wasser const heat = svgEl('text', { x: 150, y: 250, 'text-anchor': 'middle', 'font-size': '18', opacity: '0' }, svg); heat.textContent = '♨'; function update() { const dT = +rng.value; const riseM = M_PER_DEG * dT; const yW = Y_SEA0 - riseM * PX_PER_M; const xs = shoreX(yW); water.setAttribute('points', `0,${yW.toFixed(1)} ${xs.toFixed(1)},${yW.toFixed(1)} ${G0.x},${G0.y} 0,240`); waterLine.setAttribute('y1', yW.toFixed(1)); waterLine.setAttribute('y2', yW.toFixed(1)); waterLine.setAttribute('x2', xs.toFixed(1)); heat.setAttribute('opacity', Math.min(1, dT / 2).toFixed(2)); water.setAttribute('fill', dT > 2 ? '#5a7c8a' : '#4a7c8a'); dtEl.textContent = '+' + fmtDE(dT, 1) + ' °C'; riseEl.textContent = 'Anstieg nur durch Ausdehnung: +' + fmtDE(riseM, 2) + ' m (0,26 m · ' + fmtDE(dT, 1) + ')'; riseEl.dataset.state = dT < 1 ? 'good' : dT < 2.5 ? 'warn' : 'bad'; const flooded = refs.filter(r => r.groundY > yW).map(r => r.label); if (flooded.length === 0) { statusEl.textContent = '🏖 Der Strand wird schmaler, alles bleibt trocken.'; statusEl.dataset.state = 'good'; } else { statusEl.textContent = '🌊 Unter Wasser: ' + flooded.join(' und '); statusEl.dataset.state = flooded.length > 1 ? 'bad' : 'warn'; } } rng.addEventListener('input', update); update(); } // ===================================================================== // Experiment: Klimaträgheit — nach Netto-Null geht es trotzdem weiter // ===================================================================== function climateInertia(root) { root.innerHTML = `

🐌 Das Klima ist träge — teste es selbst

Lass die Zeit laufen und stelle die Emissionen ein. Drücke dann „Emissionen sofort auf null" — und beobachte, was Temperatur und Meeresspiegel trotzdem machen.

Drücke ▶ Start, um die Zeit laufen zu lassen.

Vereinfachtes Anschauungs-Modell, keine Klimasimulation (relative Skalen). Kernaussage nach IPCC AR6 (WG1, Kap. 9): CO₂ bleibt Jahrhunderte in der Atmosphäre, die träge Tiefsee erwärmt sich nach — deshalb steigt der Meeresspiegel selbst bei Netto-Null-Emissionen noch über Jahrhunderte bis Jahrtausende weiter.

`; const svg = root.querySelector('.ge-track'); const rng = root.querySelector('#ge-inertia-range'); const eValEl = root.querySelector('[data-ref="eval"]'); const insightEl = root.querySelector('[data-ref="insight"]'); const playBtn = root.querySelector('[data-act="play"]'); const zeroBtn = root.querySelector('[data-act="zero"]'); const resetBtn = root.querySelector('[data-act="reset"]'); // Plot-Geometrie const X0 = 55, X1 = 580, Y_BASE = 280, Y_TOP = 30; const T_END = 160; // Modell-Jahre const xOf = yr => X0 + (yr / T_END) * (X1 - X0); // Achsen svgEl('line', { x1: X0, y1: Y_BASE, x2: X1, y2: Y_BASE, stroke: '#1f4e5a', 'stroke-width': '1.2' }, svg); svgEl('line', { x1: X0, y1: Y_BASE, x2: X0, y2: Y_TOP, stroke: '#1f4e5a', 'stroke-width': '1.2' }, svg); [0, 40, 80, 120, 160].forEach(yr => { svgEl('line', { x1: xOf(yr), y1: Y_BASE, x2: xOf(yr), y2: Y_BASE + 5, stroke: '#4a4a4a', 'stroke-width': '1' }, svg); const t = svgEl('text', { x: xOf(yr), y: Y_BASE + 18, 'text-anchor': 'middle', 'font-size': '10', fill: '#4a4a4a' }, svg); t.textContent = 'Jahr ' + yr; }); const axisLbl = svgEl('text', { x: X0, y: Y_TOP - 8, 'font-size': '9', fill: '#4a4a4a' }, svg); axisLbl.textContent = 'relative Skalen ↑'; // Kurven + Netto-Null-Markierung const lineE = svgEl('polyline', { points: '', fill: 'none', stroke: '#8a857a', 'stroke-width': '2' }, svg); const lineT = svgEl('polyline', { points: '', fill: 'none', stroke: '#c85c4a', 'stroke-width': '2.5' }, svg); const lineS = svgEl('polyline', { points: '', fill: 'none', stroke: '#4a7c8a', 'stroke-width': '2.5' }, svg); const zeroMark = svgEl('g', { opacity: '0' }, svg); const zeroLine = svgEl('line', { x1: 0, y1: Y_TOP, x2: 0, y2: Y_BASE, stroke: '#1f4e5a', 'stroke-width': '1.5', 'stroke-dasharray': '5 4' }, zeroMark); const zeroTxt = svgEl('text', { x: 0, y: Y_TOP + 12, 'font-size': '10', 'font-weight': '800', fill: '#1f4e5a' }, zeroMark); zeroTxt.textContent = 'Netto-Null'; // Skalen: E 0..1 → unteres Band, T 0..2 °C-Index, S 0..160 Einheiten const yE = e => Y_BASE - e * 55; const yT = t => Y_BASE - (t / 2.0) * 200; const yS = s => Y_BASE - (s / 160) * 230; // Modell-Zustand let sim, running = false; function freshSim() { return { year: 0, C: 0, T: 0, S: 0, zeroYear: null, pE: [], pT: [], pS: [], lastTxt: '' }; } sim = freshSim(); function setInsight(txt, state) { if (txt === sim.lastTxt) return; sim.lastTxt = txt; insightEl.textContent = txt; insightEl.dataset.state = state; } function step(dt) { const E = +rng.value / 100; // CO₂ reichert sich an, wird nur sehr langsam abgebaut (Ozeane, Verwitterung) sim.C += (E * 0.012 - sim.C * 0.0015) * dt; // Temperatur folgt der Gleichgewichts-Temperatur mit Jahrzehnte-Verzögerung const Teq = sim.C * 1.6; const dT = (Teq - sim.T) / 45; sim.T += dT * dt; // Meeresspiegel integriert die Erwärmung (Tiefsee + Eis reagieren am trägsten) sim.S += Math.max(0, sim.T) * 0.85 * dt; sim.year += dt; return dT; } function pushPoints() { const x = xOf(sim.year).toFixed(1); sim.pE.push(x + ',' + yE(+rng.value / 100).toFixed(1)); sim.pT.push(x + ',' + yT(sim.T).toFixed(1)); sim.pS.push(x + ',' + yS(Math.min(sim.S, 160)).toFixed(1)); lineE.setAttribute('points', sim.pE.join(' ')); lineT.setAttribute('points', sim.pT.join(' ')); lineS.setAttribute('points', sim.pS.join(' ')); } function tick() { if (!root.isConnected || !running) return; let dTLast = 0; for (let i = 0; i < 2; i++) dTLast = step(0.15); pushPoints(); if (sim.zeroYear === null) { setInsight('Solange emittiert wird, steigen CO₂, Temperatur und Meeresspiegel.', 'now'); } else if (dTLast > 0.0015) { setInsight('⚠ Emissionen sind null — die Temperatur steigt trotzdem noch weiter! Die Ozeane holen die Erwärmung erst nach.', 'warn'); } else { setInsight('Erst nach Jahrzehnten stabilisiert sich die Temperatur — der Meeresspiegel steigt sogar noch Jahrhunderte weiter. Das ist Klimaträgheit.', 'bad'); } if (sim.year >= T_END) { running = false; playBtn.textContent = '▶ Start'; if (sim.zeroYear === null) { setInsight('Zeit abgelaufen — probiere es noch einmal und drücke unterwegs „Emissionen sofort auf null".', 'now'); } return; } requestAnimationFrame(tick); } playBtn.addEventListener('click', () => { if (running) { running = false; playBtn.textContent = '▶ Weiter'; return; } if (sim.year >= T_END) { sim = freshSim(); zeroMark.setAttribute('opacity', '0'); rng.value = 80; eValEl.textContent = '80 %'; } running = true; playBtn.textContent = '⏸ Pause'; requestAnimationFrame(tick); }); zeroBtn.addEventListener('click', () => { rng.value = 0; eValEl.textContent = '0 %'; if (running && sim.zeroYear === null) { sim.zeroYear = sim.year; const x = xOf(sim.year); zeroLine.setAttribute('x1', x.toFixed(1)); zeroLine.setAttribute('x2', x.toFixed(1)); zeroTxt.setAttribute('x', (x > 480 ? x - 5 : x + 5).toFixed(1)); zeroTxt.setAttribute('text-anchor', x > 480 ? 'end' : 'start'); zeroMark.setAttribute('opacity', '1'); } }); resetBtn.addEventListener('click', () => { running = false; sim = freshSim(); lineE.setAttribute('points', ''); lineT.setAttribute('points', ''); lineS.setAttribute('points', ''); zeroMark.setAttribute('opacity', '0'); rng.value = 80; eValEl.textContent = '80 %'; playBtn.textContent = '▶ Start'; insightEl.textContent = 'Drücke ▶ Start, um die Zeit laufen zu lassen.'; insightEl.dataset.state = 'now'; sim.lastTxt = ''; }); rng.addEventListener('input', () => { eValEl.textContent = rng.value + ' %'; }); } // Registry — Keys entsprechen key_slug aus der DB. // pegel und hq100 teilen sich bewusst dasselbe Widget. window.GlossarExperiments = Object.assign(window.GlossarExperiments || {}, { 'gebundene-rotation': tidalLock, 'kernschatten': umbraCones, 'keeling-kurve': keelingCurve, 'pegel': riverGauge, 'hq100': riverGauge, 'waermeausdehnung': thermalExpansion, 'klimatraegheit': climateInertia }); })();