Compare commits

..

325 Commits

Author SHA1 Message Date
Adminator 3dba841830 Lizenz-Feinschliff: CSV-Mailanhang, Lehrer-Listenspalten, Klassenliste-Haken
- Mailanhang bei grossen Chargen: zusaetzlich zur .txt jetzt auch .csv
  (gleiche reine Codeliste), Dateiname <ExtBestellnr|Datum>_geograsim_<intNr>.
- Lehrer-Lizenzlisten (zugewiesen + frei): vorne laufende Nr., Eingeloest am,
  Gueltig bis (Backend liefert redeemed_at + valid_until).
- Klassenliste Lizenz-Spalte: nur Haken (gruen) bzw. "fehlt" (rot); Tooltip
  zeigt Lizenznummer + gueltig bis. "Gueltig" via unlicensed-Liste (nicht
  storniert/abgelaufen). Bulk-Auswahl/Zaehler nutzen jetzt dieselbe Wahrheit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 11:33:44 +02:00
Adminator ff889865e1 Lizenz-Gate: Schueler ohne Lizenz gesperrt + Lehrer-Cockpit-Warnung
Teil 1 — Schueler-Lizenz-Gate (gilt fuer alle Klassen-Schueler ohne gueltige
Lizenz = zugewiesen, nicht storniert, nicht abgelaufen):
- Server-Gate zentral im Front-Controller (index.php + php/lib/license_gate.php):
  eingeloggte Klassen-Schueler ohne Lizenz sehen bei JEDER Sim-Route statt der
  Simulation eine Hinweis-Seite "Deine Lehrperson muss dir noch eine gueltige
  Lizenz zuweisen". Schnell-Ausstieg fuer Nicht-Schueler/Nicht-Sim (kein Cookie
  -> keine DB-Abfrage). Autodidakt ohne Klasse: nicht lizenzpflichtig.
- modules.php?student=1 liefert 'licensed'; Schueler-Cockpit (schueler.html)
  zeigt oben die Meldung und sperrt die Start-Buttons.

Teil 2 — Lehrer-Cockpit:
- Rote Warnleiste ganz oben, wenn Schueler:innen ohne gueltige Lizenz in der
  Klasse sind (+ Link "Jetzt zuweisen"). licenses?class_id=X liefert dazu die
  Liste 'unlicensed'.
- Lizenzen-Lasche: neue Sektion "Ohne gueltige Lizenz" mit Einzel- und
  Sammel-Zuweisung (auto_assign) — sonst fuehrt die Warnung ins Leere.

Kunden-Vereinfachung (Feedback): keine Auto-Erkennung/Dedup mehr (Ansprech-
partner koennen wechseln). Kunde = Freitext "Name/Verlag/Schule" + Mailadresse,
je Bestellung gespeichert. Editierbares Kundennummer-Feld + Autocomplete entfernt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-26 16:15:16 +02:00
Adminator eea5688c76 Lizenz-Vertrieb: CSV-Download ohne Kopfzeile (nur Codes)
Auf Wunsch: txt und csv liefern beide reine Code-Zeilen (eine pro Zeile,
keine Metadaten-Kopfzeile) -> direkt per Copy-Paste einloesbar. Nur die
Datei-Endung/MIME unterscheidet sich. Dateiname unveraendert
<ExtBestellnr|Datum>_geograsim_<interneNr>.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-26 15:36:52 +02:00
Adminator 50c72fa8d8 Lizenz-Vertrieb Feinschliff: Zugabe +10%, sichere Datei-Einloesung, Mail-Config
Umsetzung des Thomas-Feedbacks:
- Reihenfolge: freie Lizenzen beim Lehrer jetzt ORDER BY id (= Reihenfolge der
  reinkopierten Liste); stornierte ausgeblendet.
- Gratis-Zugabe: Checkbox (Default an) "+10 % aufgerundet" -> geliefert =
  ceil(bestellt*1.1) (14 -> 16). Batch speichert qty_ordered (verrechnet) und
  qty (geliefert); Live-Vorschau im Formular.
- Einloeselink/QR ENTFERNT (Sicherheitsrisiko: falscher eingeloggter Account).
  einloesen.html + pages/einloesen.php geloescht, Sammellink aus UI/Mail raus.
- CSV: Gueltig-bis + Metadaten nur in der KOPFZEILE, danach reine Code-Zeilen
  (per Copy-Paste einloesbar). Dateiname: <ExtBestellnr|Datum>_geograsim_<intNr>.
- Interne Bestellnummer = batch.id (laufend, nicht editierbar, storno-stabil).
- Lehrer-Einloesen: sichere .txt/.csv-Ablage (mehrstufig: Endung+MIME, Groessen-
  limit, nur Text lesen, Whitelist-Regex extrahiert nur Code-Tokens, Server
  validiert) — kein Upload, keine Ausfuehrung/Rendering.
- Mailsystem: zentrale Config in neuer app_settings (Provider server|aws,
  Absender-Name/-Adresse, oeffentliche URL), pflegbar in Admin + Test-Versand.
  Mailer liest Absender aus Config (SMTP-Zugang bleibt in .env). Mailtext nutzt
  kanonische geograsim.at (nicht v3/localhost).

Migrationen: -2 (qty_ordered), -3 (app_settings). Beide additiv, auf v3 angewandt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-26 11:15:53 +02:00
Adminator 96a73e22b2 Lizenz-Vertrieb: Chargen-Vergabe, Storno, Verlaengerung, Mail, Einloeselink
Erweitert das Lizenzmodul um eine Vertriebs-/Vergabe-Schicht ueber dem
bestehenden Einloese-Flow (non-breaking, alle Alt-Codes + generate/redeem
/assign unveraendert).

Schema (Migration 2026-08-26-lizenz-vertrieb.sql, additiv):
- license_customers (Besteller: Kundennummer/Email/Name)
- license_batches (Bestellung/Charge: externe Bestellnummer, Typ, Menge,
  Gueltigkeit, Sammel-Einloesetoken, Storno)
- licenses += batch_id, issued_at, valid_from/until (pro Code -> Verlaengerung),
  canceled_at/reason; code auf VARCHAR(32) (laengere Codes)
- license_mails (Mail-Archiv, unabh. vom IMAP), license_redeem_attempts
  (Brute-Force-Schutz), license_events (Audit-Log)

Backend (php/api/licenses.php):
- issue_batch: N frische 100-Bit-Codes in Transaktion, sofort vergeben,
  an Kunde/Bestellung/Gueltigkeit gebunden; sticky Default Gueltig-bis
- Uebersicht (view=batches mit Aggregat ausgegeben/storniert/netto/eingeloest
  /abgelaufen), batch_detail, Kunden-Autocomplete, sales_default
- cancel_batch/cancel_license, extend_batch/extend_license
- send_batch_mail (Server-Treiber via Mailer, archiviert, Anhang bei >40)
- redeem_charge (Sammel-Einloeselink)
- Brute-Force-Bremse + Audit + Storno-Ablehnung im redeem/redeem_batch

UI (admin-licenses.html): Ausgabe-Formular (Bestaetigung bei Grossmengen),
Ergebnis mit Copy/Download(txt+csv)/Mail-Composer/Sammellink, Bestellungs-
Uebersicht mit Filtern + Detail-Modal (Storno/Verlaengern/Verlauf/Mails).

Einloese-Landingpage (einloesen.html + pages/einloesen.php): ?charge=Token
(alle auf einmal) und ?code=CODE, mit Lehrer-Login-Gate.

Lib: Database Transaktionen; Mailer sendWithAttachments + renderPlainBody.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-26 01:39:14 +02:00
Adminator 7691649f37 Farmer Anbaugebiete: SO-Afrika-Polygon + Blickwinkel-Hinweis (Klima vs. Ertrag)
- koeppen-zones.json: eigenes subtropisch_feucht-Polygon ueber SO-Afrika
  (KwaZulu-Natal/Lowveld, lon 25-33 / lat -34..-22). Vorher fiel die Region
  durch bis zum groben Breitengrad-Fallback (+ fehlerhafter Wueste-Streifen im
  Sueden). Jetzt intentional per Polygon klassifiziert.
- game.html: Ernte-Bericht zeigt bei knapper Eignung (S3) einen schuelerlichten
  "Blickwinkel"-Hinweis: die Sim bewertet die NATUERLICHE Klima-Eignung; mit
  Bewaesserung/Schutz ernten Profis auch dort viel (Banane z.B. in Suedafrika,
  Ertrags-Weltmeister Syrien). Klima-Eignung != Anbau-Effizienz.
- Eignungswerte unveraendert: subtropische Banane bleibt bewusst S3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-26 00:29:12 +02:00
Adminator bb4d3b4cb6 heli-uebersicht: Cockpit-Link -> Haus-Icon (unser Stil) auf /schueler; Zur-Simulation-Button entfernt
Cockpit-Pille (fuehrte auf /sim ins Leere) ersetzt durch schwebendes
Haus-Icon -> /schueler (Schueler-Module). Zur-Simulation-Button raus,
da die Seite via target=_blank oeffnet (sonst mehrere Fenster).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 23:51:34 +02:00
Adminator b109ebb099 Heli: vorhandene Phasen-Links auf App-Icon-Stil (Option 2, kein Umbau)
Home/Hilfe/Uebersichtskarte als schwebende Icon-Buttons (Home/?/Karten-Pin)
statt Text/Emoji; Positionen als Icon-Cluster angepasst. Kein Header-Umbau,
keine JS-Aenderung (IDs/Handler unveraendert).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 22:57:18 +02:00
Adminator 93c742b381 Sonnensystem: Onboarding-Button auf ?-Icon (wie andere Sims), i weggelassen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 22:48:41 +02:00
Adminator ff91c489f7 Sonnensystem: Aufgaben + Onboarding-Info in den Header (eigene Icons)
- obs-open-btn (Aufgaben, Klemmbrett-Icon) + obs-help-btn (Onboarding,
  Info-i-Icon) in den Header vor die Musik verschoben; obs-launcher weg.
- Redundantes help-toggle (moonTilt-?) aus dem Header entfernt, Handler
  null-sicher (moonTilt weiter ueber Inline-? in der Mondbahn-Sektion).
- obs-Pill-CSS neutralisiert (Header-Stil greift).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 22:39:37 +02:00
Adminator 085b0b6aa8 Sonnensystem-Header-Fixes: Pille-Breite, obs-launcher, eigene Icons
- Komponente: .ggs-header--float bekommt display:flex (war auf design-
  system.css angewiesen) -> behebt volle-Breite-Pille bei Sims ohne
  design-system (Sonnensystem).
- obs-launcher (Aufgaben/Info) unter den Header (top 64, links 268).
- Eigene SVG-Icons: Leichte-Sprache (Textseite), Lights (Gluehbirne),
  Theme (Sonne/Mond-Umschaltung) statt Emoji.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 21:43:30 +02:00
Adminator 637c058507 Sonnensystem: einheitliche Kopfzeile oben, #ui + Top-Elemente nach unten
- ggs-header (Overlay) oben: Titel-Pille + Toolbar-Buttons (Home/Reset/BGM/
  Hilfe iconisiert, Easy/Light/Theme als Sim-Buttons) rechts oben.
- Toolbar + Titel aus #ui entfernt; #ui nach unten (top 62).
- Datums-Banner (top 64) und Lupe (top 104, inkl. renderInspect-Offset)
  unter den Header geschoben.
- Komponente: cockpit-link/home, reset-defaults-toggle/reset, help-toggle/
  help, bgm-toggle/music erkannt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 21:10:47 +02:00
Adminator 5b6c25bcd9 Sonnensystem: linke Sidebar schmaler (280->244), Datum-Feld + lange Labels gekuerzt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 20:48:04 +02:00
Adminator 77a3e2f16c Sonnensystem: Lupen-Beschriftung skaliert mit den Stufen (kleiner bei 50/25 %)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 20:43:27 +02:00
Adminator 3f29ad0533 Sonnensystem: Umbenennung + Lupe mit 3 Stufen (iPad)
- Titel Himmelsmechanik -> Sonnensystem.
- Lupe (#inspect-frame) in 3 Stufen 100/50/25 % ueber Eck-Griff unten
  rechts: Frame-Groesse (CSS) + Render-Groesse (lupeScale in renderInspect)
  synchron -> Inhalt schrumpft mit. Behebt die riesige Lupe am iPad.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 20:17:41 +02:00
Adminator 248a46f829 Tourismus-Icons: Reduzierte-Animation als Funkeln (durchgestrichen wenn
aktiv), Tal-Log-Glocke, Tal-Meldungsmodus Sprechblase/ABC

- Komponente: generisches Zwei-Zustands-Icon (.ic-state-on/.ic-state-off
  via .active) -> reduzierte Animation zeigt durchgestrichenes Funkeln.
- Region: Schildkroete -> Funkeln (mit Aus-Zustand).
- Tal: btnLog -> Glocken-SVG; _syncInfoModeBtn -> Sprechblase (Audio) /
  ABC (Lese-Modus) statt Emoji (Buch war fuers Glossar vergeben).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 19:50:57 +02:00
Adminator 2e9ad6a83f Tourismusregion: eigene SVG-Icons (Log/Anim/Kamera) + Tempo ganz links
- Komponente: Tempo-Steuerung an erste Aktions-Position (ganz links),
  Rest ans Ende.
- Region: 🔔/🐢/🎯 durch eigene SVG-Icons ersetzt (logBadge erhalten).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 19:34:02 +02:00
Adminator 6009eabca1 ggs-header: Tourismustal + Tourismusregion (Batch 2)
- Komponente: btnHome/⌂-Erkennung (ganzer Header durchsucht), sim-eigener
  Musik-Toggle (btnMusic) -> Musik-Icon, Panel-Optik (blur/shadow/radius)
  gestrippt fuer Sims mit .panel-Leiste.
- Beide Tourismus-Sims: topbar -> ggs-header (Klassen additiv, alle IDs +
  ui.js-Bindings erhalten), Tempo-Popover ueber .ggs-speed, Musik bleibt
  eigenes Panel (btnMusic oeffnet es). ggs-header.css vor style.css fuer
  position:fixed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 17:46:46 +02:00
Adminator 41731254b1 ggs-quick-intro: alwaysShow-Option; Busfahrt zeigt Onboarding bei jedem Besuch
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 17:19:14 +02:00
Adminator 1dffef8200 Busfahrt: nur EIN Onboarding (Welcome-Overlay abgeloest)
Ursache 2x: Busfahrt zeigte sein eigenes #welcome-overlay UND das
GgsQuickIntro-Onboarding. Jetzt ist GgsQuickIntro das einzige Start-Gate:
- ggs-quick-intro: onStart-Callback (feuert einmal beim Schliessen bzw.
  sofort wenn schon gesehen), isSeen exportiert.
- Busfahrt: welcome-overlay wird nicht mehr gezeigt; onStart -> startGame;
  startGame wartet per Retry auf boot() (kein Timing-Problem).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 17:09:28 +02:00
Adminator 496fc59c7b Busfahrt: Neustart-Button + Onboarding-Doppelung abgesichert
- ggs-quick-intro: open() entfernt vorab alle vorhandenen Overlays (nie 2x).
- Busfahrt: Neustart-Button (btn-reset, location.reload mit Rueckfrage).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 16:54:34 +02:00
Adminator 8f89613eee Busfahrt: Auftrags-Card tiefer, EN-Button -> Onboarding Seite 2, Texte
- ggs-quick-intro.js: optionale zweite Seite (config.page2 {title, html})
  mit Weiter/Zurueck-Navigation. Rueckwaertskompatibel (ohne page2 = 1 Seite).
- Busfahrt: EN-Button entfernt; die "Warum Englisch?"-Erklaerung liegt jetzt
  auf Onboarding-Seite 2 + Satz zur mehrsprachigen Hintergrundmusik.
- Onboarding-Schritte ohne lange "-" ausformuliert.
- Auftrags-Card (.bf-order) 20px tiefer (top 84).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 16:32:32 +02:00
Adminator 6135dd2648 Busfahrt: Vollbreite-Header (Logo/Haus links, Rest rechts), Karten nach
unten, eigenes Kartennamen-Icon

- Header wieder Vollbreite: Pille+Home links, Steuerung rechts.
- Auftrags-Card (.bf-order) + Info-Card (.bf-info) top 8 -> 64 (unter den
  rechten Buttons, keine Ueberlappung).
- Beschriftung-ein/aus (black-map-toggle): eigenes Tag-SVG statt Emoji
  (an = Tag, aus = Tag durchgestrichen), auch in applyTileVariant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 16:23:12 +02:00
Adminator aab9b35781 Busfahrt v2: Header kompakt-links, Budget/Auftrag nach links, Zoom unten
- Header kompakt links (nicht mehr Vollbreite) -> keine Ueberlappung mit
  der Auftrags-Karte oben rechts.
- Budget aus Header raus (widersprach dem Graph); Graph-Budget-Wert wird
  jetzt live in updateTopbar aktualisiert. updateTopbar null-sicher.
- Auftrag-Nr ins Stats-Panel (links bei den Graphen).
- Leaflet-Zoom nach unten links.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 16:14:09 +02:00
Adminator 014eb418a6 Busfahrt: ggs-header eingebaut (Batch 2)
ggs-topbar -> ggs-header (Vollbreite, klick-durchlaessig), Titel ohne
Emoji, Logo-Fallback, KPIs (Budget/Auftrag/Nacht) bleiben als Pillen,
eigener Musik-Player in #ggs-music gewrappt (Popup), Sim-Buttons
(Beschriftung/EN-Info) + Ton/Hilfe/Home + Plattform-Glossar. Keine
Elemente entfernt (alle IDs/onclicks erhalten).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 15:54:57 +02:00
Adminator e901f5addb Farmer: Markt + Erntejahr wieder untereinander (gestapelt), gleich breit/hoch
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 15:47:37 +02:00
Adminator abc8bf5149 Farmer: Markt + Erntejahr gleich breit/hoch nebeneinander (nicht mehr Vollbreite)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 15:39:49 +02:00
Adminator 54772180a2 Farmer-Feinschliff: Markt+Erntejahr gleich breit, Buch -> Plattform-Glossar
- farmer-side-status: Markt-Feld und Erntejahr-Button beide volle Box-Breite.
- Kaputte interne Fachinfo (openInfoModal) raus; Komponente ergaenzt den
  Glossar-Button (/glossar?module=farmer, neuer Tab).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 15:36:01 +02:00
Adminator 4cbdb6bbb1 Farmer: Klick-Blocker gefixt (updateMoneyDisplay-Crash) + Konsolidierung
ROOT CAUSE: #money-display aus Header entfernt, aber updateMoneyDisplay()
las es beim Boot ungeschuetzt -> null.textContent wirft -> Boot bricht ab
-> map.on('click') wird nie gebunden -> Sim unbedienbar. Jetzt null-safe.

Konsolidierung (Thomas-Wunsch): Jahr/Markt/Erntejahr-starten in die
Anbau-Versuch-Box (farmer-side-status) statt separater Jahr-Leiste;
Jahr-Badge aus der Pille raus. Anbau-Box wieder hoeher (top 64).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 15:31:33 +02:00
Adminator b835475ef7 Fix: schwebende Kopfzeile blockierte Karten-Klicks (Farmer unbedienbar)
Ursache: die transparente ggs-header ueber der Vollbild-Leaflet-Karte
fing alle Klicks ab -> Pflanzen unmoeglich. Fix zentral: Container
pointer-events:none, nur Bedienelemente pointer-events:auto (Karte
darunter bleibt bedienbar). Gilt fuer alle Karten/Canvas-Sims.

Farmer: Pille nach links (Vollbreite-Header, Steuerung rechts),
Leaflet-Zoom nach unten links verschoben.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 15:20:33 +02:00
Adminator ec746f250f Header-Komponente: Bespoke-Fixes (Logo-Verzerrung, Icon-Groesse, Glossar-Icon)
- Logo-Kreis resettet Padding/Border/min-height (Sim-eigenes a-Styling
  vererbte sich -> verzerrter Logo-Kreis).
- .ic-Groesse (20px) auch fuer Buttons/Links ohne .ggs-btn-ghost-Klasse
  (Lautsprecher/Hilfe rendeten riesig).
- Sim-interne Glossar-Buttons (glossar-btn u.a.) bekommen das Buch-Icon
  statt Emoji.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 15:07:09 +02:00
Adminator 6c2990aa5e Farmer-Header v2 nach Thomas-Feedback + Default-Musikplayer
- Header nach oben RECHTS (raus aus dem Leaflet-Zoom oben links),
  Geld-Anzeige entfernt (steht in linker Sidebar), Emoji aus Titel,
  Jahr als Badge in die Pille (Konsistenz mit Batch 1), Inter-Font.
- Jahr-Leiste + Anbau-Versuch-Spalte nach unten geschoben (unter Header).
- Komponente: wiederverwendbarer Default-Musikplayer (data-ggs-music,
  Klima-Tracks als Plattform-Standard) + Musik-Widget-Styling fuer Sims
  ohne design-system.css.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 15:02:18 +02:00
Adminator 7b1bdb8d1b ggs-header Batch 2 Start: Farmer + Komponenten-Vorruestung
- Komponente: Logo-Fallback (Kreis-Logo wenn Sim keins hat), Overlay-Modus
  (data-ggs-overlay fuer Vollbild-Canvas), generische Icon-Button-Optik fuer
  Aktionen ohne .ggs-btn-ghost-Klasse, Ton-IDs erweitert (audio-toggle/btnSound).
- Farmer als erstes Bespoke-Muster: ggs-topbar -> ggs-header, schwebende Pille
  (Fallback-Logo + Titel) + Geld-KPI + Icon-Buttons; interne Fachinfo bleibt
  (data-ggs-no-glossar).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 14:33:53 +02:00
Adminator c913522a77 Kofferdetektiv: Kopfzeile schwebt ueber Vollbild-3D (schwarzer Balken weg)
#stage jetzt inset:0 (Canvas bis ganz oben), Header position:absolute
transparent darueber. Obere HUD-Pillen + Minimap + Karten-Button unter
die Kopfzeile geschoben (top 14->64, btn-map 166->216).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 14:20:36 +02:00
Adminator 502f271f36 Einheitliche Sim-Kopfzeile (Entwurf B) als Komponente + Lizenz-Datenmodell
- ggs-header.css/.js: schwebende Pillen-Kopfzeile aus vorhandenem
  .ggs-header-Markup (Kreis-Logo/Titel/Level als eine Pille, eigene
  SVG-Icons, Tempo-Panel links + Zustandsbutton, Musik-Panel unten,
  30% transparente Untermenues, Glossar target=_blank, Lehrplan raus).
  Enhance-Ansatz: Spiel-Logik (IDs/Klassen, data-speed) bleibt unberuehrt.
- Eingebaut in Klima 2D/3D, Fluss, Logistik (Auto-Tempo), Energiemanager,
  Kofferdetektiv 3D.
- admin-licenses.html: Datenmodell-Sektion (licenses-Tabelle, Lebenszyklus
  Generiert->Eingeloest->Zugewiesen, Beziehungen, Jahr->Schuljahr->Code).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 14:06:41 +02:00
Adminator 362aaba883 Feinschliff-Spalten + Kopfzeilen-Panels (Placement/Transparenz)
- teacher.html Aktuell-Spalten: Weltkueche '🏆 Punkte' (score), Fluss
  '👥 Einw.' + '🌾 Ernte' (population/food), Busfahrt '📍 Ø km' (avgTapKm).
  (Kofferdetektiv Items-% vertagt: Sim liefert live noch keine Item-Zahl.)
- admin-styleguide.html Kopfzeile: Untermenues 30% transparent (Blur);
  Tempo oeffnet links + bleibt offen, Musik klappt nach unten + schliesst
  nach Song-Auswahl; Toggle per Klick.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 13:04:32 +02:00
Adminator 88e3c09e5c Per-Sim leistung_pct: alle 14 Sims senden das eine Leistungs-% (0-100)
Jede Sim liefert jetzt selbst leistung_pct in GGS_LIVE_STATE UND im Submit
(Fundament fuer das einheitliche %-System) — kein Rueckrechnen aus Sternen
mehr, keine 0-3-Sonderfaelle. Formeln je Sim wie vereinbart:
- klima/fluss/kofferdetektiv/heli: = vorhandener 0-100-score
- weltkueche: richtige/(richtige+falsche)*100 (Fehler in %)
- busfahrt/fluggesellschaft: topoPunkte/(Auftraege*3)*100
- farmer: gewichteter totalScore; energiemanager: ausgegl./gesamt -15/Blackout
- eu-werkstatt: firstTry/Schritte -5/Muelleimer; sonnensystem: Erstversuch-Quote
- tourismustal: Ziele+Zufriedenheit+Natur gewichtet; tourismusregion: gewichteter score
- logistik: Kontostand->% (sendet erstmals eine Kennzahl, auch an /api/assessment)
Teacher _leistungPct + live.php extractScore bevorzugen bereits diesen Wert.
Additive Aenderungen, bestehende Logik/Sterne unveraendert; alle 15 Dateien
syntaktisch geprueft.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 12:35:56 +02:00
Adminator 539f6b012e leistung_pct-Fundament (Teacher+Backend bevorzugen) + Tempo-Button-Zustaende
- teacher.html _leistungPct: von der Sim geliefertes s.leistung_pct hat
  Vorrang, sonst zentrale Ableitung (Fallback).
- live.php extractScore: leistung_pct (0-100) hat Vorrang vor score/Sterne.
- admin-styleguide.html: Tempo-Button zeigt IMMER den Stand (1x/2x/8x Auto/
  Pause); Toggle-Verhalten (Panel bleibt offen bis erneutem Klick) beschrieben.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 12:31:23 +02:00
Adminator 7e1a7c5228 Kopfzeile: nur Entwurf B, eigene SVG-Icons, aufgeklappte Controls
- A und C entfernt, nur schwebende Variante B.
- Emojis raus -> eigene SVG-Icons im App-Stil (Home/Tempo/Musik/Ton/
  Neustart/Hilfe/Glossar) via <symbol>/<use>.
- Logo (bildLogo.png) + Titel + Level in EINER Pille, alle Elemente 38px
  hoch, gleiche Hoehe (Hoehen-Mismatch behoben).
- Tempo (Segmente Pause/1x/2x/4x) und Musikplayer (Song-Dropdown + Play +
  Lautstaerke) zusaetzlich in aufgeklappter Version dargestellt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 11:40:50 +02:00
Adminator ce7bff3184 Drilldown-Metriken: schwarze Balken -> farbig + Sterne raus (sim-metrics.js)
bar() faerbte ueber ivm-CSS-Klassen, die im Schueler-Drilldown fehlen ->
Balken erschienen schwarz. Jetzt INLINE gefaerbt (ivmColor = _pctColor-Skala).
starsBlock (Heli Start/Landung) zeigt jetzt %+Balken statt Sterne.
pct/pctInv/ratio/money/temp/bool ebenfalls inline gefaerbt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 11:34:25 +02:00
Adminator 89d4d8587e Teacher-Fixes (Heli-Flugplan, Modul-Label) + Kopfzeilen-Entwuerfe
- teacher.html: Modul-Detail-Spalte 'Sterne' -> 'Leistung' (zeigt %-Balken).
  Heli 'Aktuell': neue Spalte 'Flugplan' (kumuliert Wegpunkt/Treffer/Fehlkl.),
  die drei Detailspalten bleiben, 'Gesamt' entfaellt (Leistung deckt es ab).
- admin-styleguide.html: neue Sektion 'Kopfzeile' mit 3 Entwuerfen
  (A durchgehend / B schwebend / C schwebend+halbtransparent), je als
  iPad- und Full-HD-Mockup ueber simuliertem Sim-Inhalt. Glossar target=_blank,
  Neustart drin, Lehrplan raus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 11:28:03 +02:00
Adminator a4e57d7aa6 Cockpit-Triage + Sterne->%+Balken durchgaengig (teacher.html)
Cockpit (Uebersicht):
- Farben auf kanonische Palette (--ivg..--ivr = _pctColor-Hex); ivAmp-
  Schwellen auf 50/60/80/90.
- ivTriage neu: >=90 Fordern (kann alles) / 50-89 Laeuft gut /
  <50 & fleissig -> Begleiten / <50 sonst -> Foerdern. Bestaetigen entfaellt.
- Fleiss im Klassenvergleich (_fleissig = 0.5*Zeit + 0.5*Durchgaenge >= Schnitt),
  fuer HEUTE- und insgesamt-Triage. IV_META-Texte angepasst.

Sterne raus -> %+Balken (colorblind-freundlich):
- _pctBadge (Run-Karten) neu. Alle 7 Run-Karten + generische Sonnensystem-
  Karte auf %+Balken (Busfahrt/Flug rechnen /3). Heli Start/Landung/Gesamt-
  Spalten in Aktuell auf _pctBar. Spectator, Drilldown (ivStars,
  Top-Performers), fmtMetric, Heli-Detail: Balken bzw. %.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 10:17:05 +02:00
Adminator 3bc0f418be Bewertungs-Vorschau: EINE Skala-Darstellung + 4-stufige Triage
- Doppeldarstellung (Beispiel-Balken + Legende) zu EINER Tabelle
  zusammengefuehrt: Farbe + Bereich + Label + Beispiel-Balken je Zeile.
  Beispielwert = Bereichsschwelle (90/80/60/50), nicht willkuerlich.
- Triage jetzt 4 Felder an denselben Farbgrenzen:
  <50 Foerdern (Hilfe) / 50-79 Begleiten / 80-89 Laeuft gut /
  >=90 Fordern (herausfordern). Styleguide + Preview angeglichen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 02:51:50 +02:00
Adminator 0a6fa2172f Einheitliche Leistungs-Skala (%) + Farbcodes ueberall vereinheitlicht
- Schwellen NEU (Thomas): >=90 Dunkelgruen / >=80 Hellgruen / >=60 Gelb /
  >=50 Orange / <50 Rot. Sterne entfallen.
- teacher.html: _pctColor (kanonisch) — _tagTraffic + _liveBar + _liveBarColor
  delegieren jetzt alle daran (EINE Skala; 'niedrig-ist-gut' invertiert).
  Neu: _leistungPct(simId,state) = das eine 0-100-% je Sim (eigener Score
  oder abgeleitet), _pctBar (kompakt, schmal). Neue Spalte 'Leistung' in
  'Aktuell'. _liveMood aus _leistungPct (>=80 gut / >=50 mittel / <50) —
  deckungsgleich mit Triage Foerdern/Begleiten/Laeuft gut (50/80).
- admin-styleguide.html: neue Sektion 'Leistungs-Skala' (5 Stufen, %+Balken,
  Triage, CSS-Variablen).
- admin-lehrer-view.html: Schwellen/Triage/Preview auf neue Werte, Balken
  kompakt, Sterne-Hinweis raus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 02:37:33 +02:00
Adminator c79ef691ad Lehrer-View-Uebersicht: Vorschau einheitliches Leistungs-% (Sterne raus)
- Vorschau-Karte: %+Balken-Standardanzeige, 5-stufige Farbskala,
  Triage-Felder (Foerdern/Begleiten/Laeuft gut) aus %xFleiss mit
  Grenzen = Farbgrenzen (50/65), damit Farbe<->Feld immer stimmen.
- Neue Spalte 'Leistung' je Sim: Vorschlags-Formel auf die Testdaten
  gerechnet (PERF), als pctBar + Triage-Chip. Busfahrt/Flug/Sonnensystem
  Naeherung (Rohwerte fehlen in Testdaten).
- Bewertungs-Block je Sim um 'Vorschlag %' (PROPOSED_PCT) erweitert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 02:25:35 +02:00
Adminator bf579310ed Lehrer-View-Uebersicht: Sterne- + Prozent-Formeln je Sim dokumentiert
- Globale Erklaer-Box: wie der Zeilen-% entsteht (live.php extractScore:
  Score sonst Sterne/5x100) + Uneinheitlichkeiten (0-3 vs 0-5 Sterne,
  rohe score-Werte, Weltkueche pro Gericht, Logistik sendet nichts) +
  Vorschlag 'ein System' (% = Wahrheit, Sterne abgeleitet, gleiche Schwellen).
- Pro Sim ein Bewertungs-Block mit exakter Sterne-Formel + Prozent-Herkunft
  (aus dem Sterne-Audit der Game-Files) inkl. Warnhinweisen.
- Tourismustal: Gaeste-Spalte auf gruen (Gesamtgaeste = Spielfaktor).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 01:24:05 +02:00
Adminator 691387c4d8 Admin: Lehrer-View Parameter-Ueberblick Schuelerleistung
Neue Referenzseite /admin-lehrer-view.html: pro Sim (14, der Reihe nach)
die 'Ergebnisse/Aktuell'-Tabelle mit Testdaten, gerendert mit der ECHTEN
Logik (LIVE_PRIMARY_FIELDS + Formatter aus teacher.html gespiegelt).
- Unter jedem Spaltentitel der State-Key (Spiel->Backend-Verkabelung).
- Spalten GRUEN, die nach Abschluss relevant bleiben (Ergebnis/Fortschritt/
  Identitaet), GRAU = nur live; je Sim Begruendungs-Liste der Grau-Spalten.
Verlinkt aus admin-sitemap.html.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 19:37:26 +02:00
Adminator 62e903a5ec Kofferdetektiv iPad-Performance: sicheres Buendel (Aufloesung + AA + GPU)
Nur auf Touch-Geraeten (Desktop unveraendert):
- Render-Aufloesung auf dpr 1.3 gedeckelt (dprCap-Helper) statt bis 2 —
  dpr 2 = 4x Pixel war der teuerste Posten.
- antialias aus (MSAA teuer auf Mobile), Retina-Dichte kaschiert Kanten.
- powerPreference:'high-performance' fuer die staerkere GPU-Pipeline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 14:13:35 +02:00
Adminator 11c48817de Kofferdetektiv iPad: zwei feste Joysticks (immer 50% sichtbar)
Statt schwebendem Links-Joystick + Rechts-Wisch jetzt zwei feste,
halbtransparente Joysticks:
- links = Umsehen (Auge 👁), ratenbasiertes Drehen/Neigen in update()
- rechts = Bewegen (Fuß 🦶)
Touch wird dem naeheren Joystick im Fangradius zugewiesen (assign/stickVec).
Interaktions-Button (🧳) nach unten-mittig verschoben (kollidierte sonst
mit dem rechten Bewegungs-Joystick). Joysticks nur auf Touch sichtbar
(body.kd-touch).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 13:57:20 +02:00
Adminator 8ec005f889 EU-Werkstatt: Probierzuege (Karten rausziehen) erfassen + Sterne ehrlicher
Bisher konnte man Karten in Slots einsetzen und wieder herausziehen, bis
alle voll waren — erst dann wurde ausgewertet. Dieses Herumprobieren war
unsichtbar und senkte die Sterne nicht (5 Sterne trotz Trial-and-Error).

- game.html: reshufflesByStep/totalReshuffles; manuelles Herausziehen einer
  platzierten Karte zaehlt als Probierzug. Ein Schritt gilt nur ohne
  Probierzuege als 'sauber auf Anhieb' -> Sterne (firstTryHits/steps) und
  Treffsicher-Achievement spiegeln Trial-and-Error jetzt. Endbild zeigt
  '🔄 N Probierzuege'; Live-State + Submit tragen die Zahlen.
- teacher.html: needsHelp eu-werkstatt auch bei vielen Probierzuegen;
  '🔄 Probier'-Spalte in Aktuell; Run-Karte: Gesamt-Probierzuege +
  pro Schritt ('🔄 nach Probieren' statt 'auf Anhieb').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 13:33:44 +02:00
Adminator 7de71b0e83 Energiemanager: Blackouts fuer Lehrer sichtbar machen
- game.html: bei Blackout sofort action:'blackout' an /progress feuern
  (setId/Tag/Block/Frequenz/Ursache, score:0) — Fehlversuch bleibt auch
  bei Set-Abbruch sichtbar, kein player_progress/Sterne.
- progress.php: neuer action:'blackout' -> eigene assessments-Zeile,
  KEIN XP/plays/best_stars.
- live.php: Blackout-Flag (event:'blackout') durch die Tag-Summary reichen
  (blackout/blackoutCount pro Session).
- teacher.html: needsHelp energiemanager (>=2 Blackouts oder Tag <4/8);
  Blackout-Spalte + '🔌 neu planen'-Status in Aktuell; Tag-Balken als
  🔌 Blackout kennzeichnen; Run-Karte: eigenes Blackout-Kaertchen +
  '🔌 N Blackouts'-Badge im fertigen Set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 13:19:21 +02:00
Adminator b77f4ed912 Tag-Ansicht: student_results einbeziehen + Datum/Zoom-Verhalten
- live.php summary: fertige Laeufe aus student_results ergaenzt (dedupliziert gegen assessment/
  live) -> JEDER heutige Durchgang erscheint als Balken, egal ueber welchen Submit-Pfad.
- teacher.html: Ergebnisse-Tab/Menuewechsel setzt Datum auf HEUTE + Regler auf Auto zurueck;
  Datumswahl/Heute setzt Regler auf Auto; Auto-Zoom jetzt +/-1 h um die Aktivitaet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 09:33:32 +02:00
Adminator 868cdda276 Fluss-Endscreen: zwei Felder (Rueckmeldung -> Aktionen) + Badges nebeneinander
Feld 1 = Rueckmeldung (Sterne/Stats/Badges/Reflexion) mit "Weiter"-Button (erst nach
Reflexions-Antwort aktiv); Feld 2 = "Wie geht's weiter?" (Stufen/Erneut versuchen +
Zurueck zur Startseite). end-badges jetzt display:flex (nebeneinander statt gestapelt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 09:19:10 +02:00
Adminator 9a1ee0fae1 Tag-Zeitachse: gestauchte Skala - Nacht 00-06 und Abend 21-24 je 1 Block, Tag 06-21 stuendlich
Regler UND Zeitachse/Balken nutzen dieselbe Stunde-Einheit-Abbildung (_hourToUnit/_unitToHour,
17 gleich breite Bloecke). Nacht-/Abend-Bloecke dunkel ohne interne Linien; Tag stuendlich
beschriftet + Trennlinien. Auto-Default rundet auf Block-Grenzen. Marker an den Block-Grenzen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 22:23:34 +02:00
Adminator 1e1c1238bd Stadt & Raumplanung auf 'geplant' (In Vorbereitung, nicht spielbar) in v3 + Prod-SQL notiert
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 21:51:58 +02:00
Adminator de104e915a Tag-Zeitstrahl: keine Trennlinien in den Nacht-Baendern (zusammengefasst)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 21:50:13 +02:00
Adminator 193c6dcc8f Tag-Zeitstrahl: 23 Stunden-Trennlinien + stuendliche Beschriftung + dunklere Nachtzeiten (00-01/20-24)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 21:48:33 +02:00
Adminator 74736a33aa Tag-Ansicht: Zeitfenster-Doppelschieberegler (00-24 Uhr, stuendlich) ueber der Tabelle
Default = Auto (erste bis letzte Aktivitaet, auf volle Stunden). Zwei Fader zum
Zusammenschieben (z.B. aktuelle Stunde). Balken werden aufs Fenster gekappt (overflow).
"Ganzer Zeitraum"-Reset zurueck auf Auto.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 21:42:27 +02:00
Adminator f7bf0f0901 Fix: alle heutigen Leistungen zaehlen (Ben) + Live-Session started_at bei Modulwechsel (Clara)
- live.php: ON DUPLICATE KEY UPDATE-Reihenfolge korrigiert (started_at VOR module_id), sonst
  wird der Modulwechsel nie erkannt -> Balken behielt alten Startzeitpunkt (Clara EU-Werkstatt seit 14:30).
- results.php view=today: liefert todayScores (Pro-Schueler-Aggregat aus student_results, zuverlaessig).
- teacher.html HEUTE-Triage: fertige Laeufe aus student_results + laufende aus Summary zusammengefuehrt;
  Totalversagen heute (<40) -> Foerdern. Ben (klima 36) erscheint jetzt korrekt in Foerdern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 15:21:04 +02:00
Adminator cc83493055 Lehrer-Live: idle-pausierte gelten in Aktuell als pausiert (nicht aktiv) + Tag-App-Symbol immer sichtbar
- Aktuell: paused beruecksichtigt jetzt __idlePaused/__paused (heartbeatet trotz Pause weiter)
  -> kein Widerspruch mehr "navigiert UND pausiert" (Klara).
- Tag: App-Symbol als immer sichtbares Overlay am Balkenstart, auch bei winzigem Balken (darf ueberstehen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 15:11:20 +02:00
Adminator abbc0580c5 Heli M17: A10-Tauern-Zielpunkt auf Tauerntunnel-Nordportal (lag 7km noerdlich im Gebirge)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 15:07:51 +02:00
Adminator be7d9fce1a Empfehlung: 6. Karte 'Laeuft gut' in gleicher Form (volle Liste mit Balken) statt Kompakt-Streifen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 15:05:08 +02:00
Adminator da1e6e6e87 Weltkueche: correct_for im Spiel scharfgeschaltet (V3)
ING-Build kopiert jetzt v.correct_for -> der bestehende Heimat-Branch (ing.correct_for[heimat])
liefert die kuratierten Zweit-Laender: mehl->Ungarn, majoran->DE, safran->Iran, feta/lamm->
Bulgarien (statt Gravitations-Schaetzung). Datenkonsistenz geprueft (Moussaka feta/lamm).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 14:50:13 +02:00
Adminator bc468272ba Heli M10: A2-Baden-Zielpunkt zurueck auf die A2-Linie (DB-Wert lag daneben); Prod-SQL ergaenzt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 14:36:26 +02:00
Adminator 4976582400 Heli Routenplanung: Timing-basierte Wertung (3 Stufen) + Bonus vor rotem Hint
Pro Wegpunkt: 2=Treffer VOR der roten Einblendung (Bonus) · 1=waehrend der 10 s · 0=danach.
computePlanStars() aus Ø-Timing (5/4/3/2/1); Fehlklick-Abbruch bleibt 0. wpRevealAt merkt
Reveal-Zeitpunkt, onPlanClick klassifiziert. Bonus im Endbildschirm + Assessment (planBeforeRed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 14:32:02 +02:00
Adminator bf5b026b46 Heli-Wegpunkt-Fix: Prod-SQL persistent im Repo (Live-DB hat gleiche Luecke)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 14:04:32 +02:00
Adminator 77023ff7a3 Heli: fehlende Wegpunkte-Datenluecke behoben + Routing-Score = Klick-Genauigkeit
- 5 Wegpunkte fehlten in geo_waypoints (bodensee_bregenz/kaiser_kufstein/felswand_raetikon/
  schlucht_mellau/maria_saal) -> Missionen M5/M7 u.a. hingen nach dem vorletzten WP. In v3-DB
  eingespielt; Prod-SQL in .dontDeploy (Live-DB gleiche Luecke). waypoints.js: maria_saal ergaenzt.
- GGS_LIVE_STATE score: Routing zaehlt jetzt Klick-Genauigkeit (richtige/alle) statt Fortschritt
  -> kein roter Tag-Balken mehr am Planungs-Anfang (0 Klicks = grau, nicht rot).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 14:03:53 +02:00
Adminator aaa40e9f1a Heli Aktuell: letzten Einsatz in der Auftragswahl behalten
game.html: bei Mission-Ende Snapshot game.lastResult (Start/Landung/Gesamt); GGS_LIVE_STATE
zeigt in mission/briefing den letzten Einsatz statt leerer Spalten. teacher.html Status:
"✓/⚠ letzter Einsatz" wenn Werte aus dem letzten Lauf stammen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 13:57:57 +02:00
Adminator 0d9481cae2 Tag-Ansicht: pausierte Zeiten bleiben als Verlauf stehen (verschwinden nicht mehr)
live-client loggt Pausen-Spannen (__pauseSpans: {s,e}), live.php reicht sie durch,
renderLiveSummary zeichnet jede als bleibendes halbhohes Segment im Tag-Balken
(offene/laufende Pause blinkt). Fallback fuer Sim-Pause via phase bleibt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 12:53:07 +02:00
Adminator 0d765961af Idle-Pause fuer Heli funktionsfaehig + iframe-aware Interaktionserfassung
- Interaktionen aus gleich-origin iFrames (Heli-Start/-Flug/-Landung) zaehlen jetzt
  (periodisches _hookFrame), plus postMessage {type:'ggs-activity'} + __GGS_LIVE__.ping().
  Aktives Steuern gilt nicht mehr faelschlich als idle.
- EVENTS erweitert (keyup/touchmove/pointermove). Heli in IDLE_PAUSE_SIMS aufgenommen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 12:50:30 +02:00
Adminator 7986bb2eb6 Heli: Treibstoff kann leer werden (1★-Takeover) + Live-Score fuer Tag-Farbe + Aktuell-Status/Phase
- start.html: Sprit-Floor 0.05->0; Treibstoff-leer loest Chefpilotin-Takeover aus (stars:1)
- game.html: GGS_LIVE_STATE liefert progressiven score (Routing/Start/Landung/Gesamt) -> farbige Tag-Balken
- teacher.html: Phasen-Map + Status-Spalte fuer mission/briefing/unterwegs (kein leeres "...")

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 12:48:26 +02:00
Adminator 6dfcf4d57f Idle-Pause vorerst NUR in Klima (per-App-Freischaltung)
Die plattformweite Idle-Pause stoerte passive/iframe-Phasen (Heli-Flug: aktiv gesteuert,
aber Steuerung im iframe/Halte-Taste zaehlte nicht als Interaktion -> Fehlpause).
Jetzt Allowlist (klima/klima-3d) bzw. window.GGS_IDLE_PAUSE=true. Andere Apps: keine
Auto-Pause, bis sie dort einzeln getestet + die Interaktionserfassung angepasst ist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 12:27:01 +02:00
Adminator 0532327e6a Heli: Onboarding-Zurueck-Button + Routen-Card-Sicherheitsnetz (ohne Ton)
- Onboarding: "Zurueck"-Button (ab Schritt 2, prevHeliOnboarding)
- Routenplanung: planInstruction-Card erscheint garantiert auch ohne Ton / bei
  Audio-Ladefehler (Fallback + Timeout-Netz); bleibt bis zum Wegpunkt-Fund stehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 12:25:08 +02:00
Adminator 5d79fa3c3a Klima: Flut-Bewertung ans 40%-Spielziel angeglichen
Flut-Score (End- + Live-Score in game-2d/3d) = max(0, 25*(1-Flut/50)): 0% volle 25 Pkt,
40% noch ~5 Pkt, erst ~50% gegen 0 (statt 0 schon ab 31%). Lehrer-Geflutet-Ampel gruen
<25 / gelb <=40 / rot >40. Klima-Stimmung nutzt jetzt den (fairen) Score.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 12:19:41 +02:00
Adminator c9eb0e7764 Cockpit: "Empfohlene Aktivitaet fuer dich" zweigeteilt - HEUTE (oben) + insgesamt (darunter)
HEUTE triagiert exakt gleich (support/more/work/good/idle), aber nur auf die heutigen
Laeufe (Erfolg = Ø der heutigen Scores aus summary-Endpoint). Karten-Renderer in
_ivTriageCards ausgelagert + wiederverwendet. Neuer summary-Fetch im Promise.all.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 12:16:10 +02:00
Adminator 3c28794012 Tag-Ansicht: pausierte Laeufe als halbhoher weiterlaufender Balken
live.php meldet paused (Idle-Pause/Sim-Pause/phase=paused) + activeUntilMs (letzte
Interaktion). renderLiveSummary: Hauptbalken volle Hoehe bis Pausenbeginn, danach
halbhoher gedaempfter "Pausen-Schwanz" der mit der Zeit weiterwaechst. Legende + tz.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 12:02:27 +02:00
Adminator 5633d3f9f9 Lehrer-Live-Zeiten fest auf Europe/Vienna (Server laeuft UTC) -> immer oesterreichische Zeit
_hhmm/_vdt/_fmtHM formatieren mit timeZone Europe/Vienna; _asMs normalisiert Epoch UND
MySQL-UTC-Strings. Betrifft Tag-Achse+Balken, Session-Detail, "Heute erledigt", Cockpit
"zuletzt aktiv", Reflexionen, letztes Lebenszeichen. Vorher zeigte new Date().getHours()
je nach Geraet evtl. UTC.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 11:55:44 +02:00
Adminator e4b877de4b Tag-Ansicht: Zeitachse hugt die Aktivitaet (Start am ersten Spiel statt gerundeter halber Stunde)
Vorher rundete winStart auf 30-min + 15-min-Polster -> bis zu 45 min leerer Platz vor
dem ersten Balken (z.B. Spiel 09:44, Achse begann 09:00). Jetzt winStart = erstes Spiel
minus 4 min, Marker adaptiv auf sauberen Uhrzeiten (15/30/60-min je nach Spanne).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 11:48:45 +02:00
Adminator 550a35eae8 Klima Aktuell: Bevoelkerungs-Balken relativ zur Startbevoelkerung (klemmte vorher bei 100%)
Skala war fix 1500-4500 -> alle Populationen (5000-8800) klammerten bei 100%. Jetzt
relativ zum Level-Start (6000/7000), 30%-160% -> unter Start rot, gewachsen gruen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 11:45:21 +02:00
Adminator 47adf0497d Klima: Geflutet an Meeresspiegel koppeln (kein Deckel-Plateau) -> Zahl passt zur Karte
floodedPct = sea*0.45 + max(0,sea-prot)*1.1. Vorher deckelte der Strand-Anteil bei
12%, sobald Schutz das Inland trocken hielt -> alle Spieler zeigten 12% egal wie das
Klima stand. Die Karte zeichnet die Flut aber aus seaLevelCm -> Zahl und Bild liefen
auseinander. Jetzt steigt die Zahl stetig mit dem Meeresspiegel (Klimafolge), Schutz
daempft nur den Zusatzschaden. Node-validiert: gutes Klima=niedrige Flut, hohe Temp=
hohe Flut, gleiche-Tick-Spreizung statt Plateau.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 11:43:58 +02:00
Adminator 48971b4588 Plattformweit: Idle-Pause-Overlay nach 3 min Untaetigkeit mit "Weiter"-Button
live-client.js (laedt in JEDER Sim): nach 3 min ohne Interaktion sichtbare Pause
(friert Aktivzeit ein -> Idle zaehlt nicht als Arbeitszeit), Weiterarbeiten per
Klick. Heartbeat meldet __paused/__idlePaused. Kein Sim-Code noetig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 23:32:50 +02:00
Adminator 2386756a20 Klima: Geflutet-Kurve stetig + Schutz wirksam (Rebalance) · Lehrer-Cockpit "Nicht aktiv" responsiv
- engine.js: floodedPct steigt jetzt stetig mit dem schutzbereinigten Meeresspiegel statt bei 15% zu plateauen. Guter Schutz haelt das Inland lange trocken (effRise = sea - prot*0.85), Strand-Verlust gedeckelt bei 12%. Downstream mitrebalanciert: Pop-Verlust-Multiplikator 5->3, Wachstums-Schwellen 15/35, Flut-Events 8-20/40, Ende bei >60. Node-validiert: guter Spieler gewinnt + Bevoelkerung waechst (statt Kollaps), Flut diskriminiert gut/mittel/schlecht.
- teacher.html: iv-prow/iv-cols Grid flexibel (Name minmax(0,1fr) + Ellipsis, Datenspalten auto) -> "Nicht aktiv"-Liste laeuft nicht mehr aus der Karte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 23:29:39 +02:00
Adminator 0c4fb84606 Lehrer-Live: Avatar-Slug (avatar:xxx) wird als Bild gerendert statt als Rohtext (Reflexionen + Session-Detail)
Gemeinsamer _avatarHtml-Helper: erkennt Slug in avatar_slug ODER als "avatar:<slug>" im emoji-Feld.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 23:21:32 +02:00
Adminator 2160a99896 Lehrer-Live: laufende Klima-Balken farbig (Ampel) + "Heute bereits erledigt" zeigt alle heutigen Durchgaenge
- klima game-2d/3d: GGS_LIVE_STATE liefert laufenden Punktestand (score, gleiche Formel wie Endbewertung)
- live.php summary: laufende Sessions jeder Sim bekommen Ampel-Score (nicht mehr nur Farmer) -> Tag-Ansicht faerbt laufende Balken statt grau
- teacher.html _renderInactiveToday: zeigt jetzt ALLE heute abgeschlossenen Laeufe (auch von gerade aktiven Schueler:innen); nur das laufende Spiel bleibt oben. Titel -> "Heute bereits erledigt"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 23:19:23 +02:00
Adminator 8cc48b1bc6 Klima: Aktuell-Linien gruen->rot (CO2/Temp/Geflutet/Budget/Bev.) + "Nochmal spielen" setzt wirklich zurueck
- teacher.html: LIVE_PRIMARY_FIELDS.klima Kennzahlen mit _liveBar-Ampel (absolute Skalen), _liveBar +Nachkomma-Param; tick-Spalte entfernt (redundant zu Jahr)
- game-2d/3d: beendete Laeufe (phase=end) werden beim Resume verworfen -> "Nochmal spielen"/erneutes Oeffnen startet frisch statt im Endbildschirm

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 17:17:33 +02:00
Adminator eb67cbc79f Aktuell/Fortschritt: "Heute bereits aktiv" mit allen Aktivitaeten pro Name
Umbenannt von "Heute nicht mehr aktiv"; zeigt jetzt ALLE heutigen Aktivitaeten
pro Schueler:in (group by name, eine Zeile je Aktivitaet, Trennlinie zwischen
Personen) mit Uhrzeit + Leistung.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 16:47:52 +02:00
Adminator 3c856ff5ce Aktuell + Fortschritt: zweite Tabelle "Heute nicht mehr aktiv" darunter
Unter den aktiven Schueler:innen eine eigene Tabelle (gleiche Form): wer HEUTE
schon gespielt hat, gerade aber offline ist -> Zuletzt aktiv (Uhrzeit),
Aktivitaet (Sim), Leistung (Score-Prozent, Ampelfarbe). Reusable
_renderInactiveToday(activeSet), gespeist aus dem summary-Endpoint (heute),
laufende/online ausgenommen. In renderLive und renderLiveCompare eingehaengt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 15:21:51 +02:00
Adminator 64a9a62157 Gesamt/Fleiss: laufende Sessions als blinkende Saeule im aktuellen Bucket
Statt separatem Top-Streifen (rueckgebaut): results.php view=fleiss spielt
laufende live_sessions in den aktuellen Bucket (Stunde/Wochentag/Tag/Monat) ein,
markiert ihn `live`. renderFleiss laesst diese Saeule blinken (roter Rahmen).
So sieht man Bens laufendes Spiel direkt bei "Fleiss pro Schueler*in".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 15:02:19 +02:00
Adminator ad8c6b7d7e Gesamt: "Jetzt live"-Streifen oben (laufende Spiele, blinkend)
In Ergebnisse/Gesamt fehlten laufende Sessions (Fleiss baut nur auf fertigen
Laeufen). Neuer blinkender Live-Streifen ganz oben aus live_sessions: Name +
Sim-Symbol + Laufzeit, Klick = zuschauen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 14:55:32 +02:00
Adminator 71226581fa Tag-Ansicht: laufende Spiele als langsam blinkende Bloecke
Running-Balken (isLive/running) blinken jetzt sanft (_tagblink 1.8s), damit
laufende Spiele klar von fertigen zu unterscheiden sind. Breite = Spieldauer
(waechst mit der Zeit) war bereits so.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 14:47:51 +02:00
Adminator 540b27a2bd Cockpit: "Heute in der Klasse"-Zeile ganz oben in der Klassenuebersicht
Immer sichtbare Live-Zeile ueber den Empfehlungen: wie viele gerade aktiv +
welche Sims, und wer JETZT Hilfe braucht (needsHelp) als klickbare Chips mit
Klartext-Grund (z.B. "Ben - Trefferquote 10 %"). Quelle: cockpit.students.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 14:30:07 +02:00
Adminator 5635fa26b4 Lehreransicht: Ampel Farmer=Trefferquote, Vergleich->Fortschritt+Qualitaet, Mini-Balken ueberall
- _liveMood: farmer-Fall (hit_rate >=60 gruen / >=30 gelb / sonst rot) -> Ampel
  spiegelt Qualitaet, nicht mehr nur Fortschritt.
- qualityScore: farmer hit_rate -> "Qualitaet"-Spalte im Fortschritt-View gefuellt.
- "Vergleich"-Button/Titel -> "Fortschritt (& Qualitaet)".
- "Aktuell"-Tabelle: Mini-Balken jetzt fuer ALLE Zahlenspalten aller Sims
  (relativ zum Spalten-Max, neutral), Spalten mit eigenem Balken ausgenommen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 14:26:47 +02:00
Adminator 4e38b27c64 Tag-Ansicht: echte Spieldauer + Ampelfarbe nach Erfolg + Sim-Symbol
Bug: fertig gespielte Laeufe (Farmer-Endbericht = 1 Assessment) erschienen als
kurzer "Abbruch"-Balken (Dauer 0). Fix:
- live.php summary: nutzt duration_ms -> Balken decken die echte Spielzeit ab und
  fluchten am Start; kein Falsch-Abbruch mehr. Neues Feld `score` (0-100) je
  Session (Score/Sterne); laufende Farmer-Sessions bekommen hit_rate als Live-Score.
- teacher.html Tag-Balken: einheitliche Hoehe (Hoehe raus), Farbe = Ampel nach
  Erfolg (gruen->rot), App-Farbe ersetzt durch Sim-Symbol; Klick = Details.
  Legende angepasst.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 12:56:47 +02:00
Adminator 85ee58c56d Farmer: "Braucht Hilfe" auch bei schlechter Trefferquote (nicht nur cash<0)
Ben spielt bewusst schlecht (hit_rate ~10%), Konto aber wieder im Plus -> blinkte
nicht. needsHelp (teacher.html client + live.php server) jetzt: cash<0 ODER
(attempts>=6 UND hit_rate<30). Faengt dauerhaft falsche Boden/Klima-Wahl; Clara
(hit 75, 3 Krankheiten) bleibt bewusst ruhig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 12:41:39 +02:00
Adminator fecd8bb9c9 Textsystem: Bis-Strich mit Leerzeichen (Zahl – Zahl) per Agent-Sweep
562 Ersetzungen in 50 Dateien: Halbgeviertstrich zwischen zwei Ziffern (=
"bis"-Bereich) bekommt Leerzeichen davor/danach (3–4 -> 3 – 4). Regex
/([0-9])\s*–\s*(?=[0-9])/ auf Rohtext (format-erhaltend, CRLF/Einrueckung
unveraendert), Bindestriche in Woertern und Minuszeichen unangetastet, alle
JSON weiter gueltig. Energiemanager-Zeitbloecke sind Display-Labels (kein
Logik-Key) -> safe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 12:38:19 +02:00
Adminator 9c99359d9c Farmer-Fix: Klima-Fallback laengengrad-bewusst + "Vernalisation" ersetzt
- defaultZoneByLatitude: bei 32-38 Grad nicht mehr pauschal "mittelmeer" (das
  gab kontinentalem Innerasien wie Westchina faelschlich Mittelmeerklima ->
  "Mediterraner Reisanbau" in Xinjiang). Jetzt nur in echten Mittelmeer-
  Laengenfenstern (Mittelmeerbecken/Kalifornien/Chile/Kap/SW-Australien),
  sonst "wueste". lon wird jetzt an die Funktion uebergeben.
- crops.json: Weizen-Fachbegriff "Vernalisation" durch "kuehle Winter, die der
  Winterweizen fuer die spaetere Kornbildung braucht" ersetzt (Thomas: gehoert
  nicht ins Lehrwerk).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 12:25:57 +02:00
Adminator b32097b617 Farmer-Fix: Begruendungstext passt jetzt zur echten Klimazone (nicht mehr zufaellig)
Bug: Kamel in tropischem Brasilien zeigte "Polargebiet/Permafrost", weil der
generische N2-Pool tropisch+polar mischt und pickFromPool zufaellig zog. Ebenso
"Kamele in Nordeuropa" auf einem Feld in Suedamerika (fester Regionsname).
- engine.js: pickZoneAware() waehlt per Klima-Stichwoertern (ZONE_KEYWORDS) den
  zur Zone passenden Grund-Text statt zufaellig; Fallback = alt.
- crops.json: Kamel-N1-Text regions-neutral ("viel zu feucht" statt "Nordeuropa").
S1-Erfolgstexte mit Regions-Beispielen (Reis/Kaffee/Datteln) bewusst belassen
(Vergleich auf passendem Feld, didaktisch ok).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 12:19:32 +02:00
Adminator 257b5e5099 Lehreransicht "Aktuell": Mini-Balken unter den Zahlen (Farmer-Spalten)
Kleine Balkengrafik unter jedem Zahlenwert: bounded Metriken (Jahr/Trefferquote/
Sorten absolut zur Skala) + offene Werte (Konto/Laender/Felder/Versuche/CO2)
relativ zum groessten Wert der Spalte ueber alle Schueler:innen -> auf einen
Blick "wer liegt vorn". Konto signiert (rot bei minus), CO2/Krankheiten
invers (rot = mehr). Helfer _liveMiniBar/_liveBarColor, colMax pro Spalte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 12:03:29 +02:00
Adminator 532b5471ec Lehreransicht: Farmer-Spalten in "Aktuell" uebersetzt statt roher Keys
LIVE_PRIMARY_FIELDS um farmer-Eintrag ergaenzt: Jahr /10, Markt, Konto (rot bei
minus), Trefferquote (Balken), Sorten /10, Laender, Felder, Versuche, CO2 kg,
Krankheiten (rot bei >0). Redundante/interne Keys (phase, market-id, progressPct,
simId, timestamp) fallen weg. Vorher fiel die Tabelle auf rohe englische Keys.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 11:58:45 +02:00
Adminator ff751f3a05 Farmer: Onboarding-Schritt korrigiert - keine Geraete, Sorten werden freigeschaltet
"Ertraege verkaufen, in Geraete und Sorten investieren" -> "Ernten und
verkaufen - mit den Jahren werden neue Sorten freigeschaltet". Es gibt keine
Geraete-Mechanik, und Sorten werden automatisch (nach 3 Jahren) freigeschaltet,
nicht gekauft.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 11:53:56 +02:00
Adminator 714abc026a Farmer: Intro-Satz korrigiert - "Hoefe auf der ganzen Welt"
"Du bewirtschaftest einen Hof" -> "In dieser Simulation bewirtschaftest du
Hoefe auf der ganzen Welt" (die Sim spielt weltweit, nicht auf einem Hof).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 11:52:38 +02:00
Adminator 8c316f22e8 Weltkueche: Marillenknoedel auf reinen Topfenteig (Erdaepfel raus) + Panel final
- wachauer-marillenknoedel Rezept-Schritt 1: Erdaepfel entfernt, klassischer
  Topfenteig (Topfen/Ei/Butter/Mehl/Griess). Passt jetzt zu den ings.
- Admin-Maengelliste: komplett abgearbeitet markiert; mediterrane Kraeuter
  bleiben (Thomas-Entscheidung).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 11:49:33 +02:00
Adminator 8ffc43d7b2 Weltkueche: Maengelliste abgearbeitet (14 Zutaten-Fixes) + Guanciale neu
Zutaten, die im Rezept nicht vorkamen/falsch waren, korrigiert:
- raus: Apfelstrudel(zitrone,essig), Sauerbraten(mehl), Pierogi(milch),
  Gulasch(tomaten), Guacamole(olivenoel), Groestl(kuemmel)
- ersetzt: Kirschtorte/Zuercher/Quiche milch->sahne, Pizza hartweizen->mehl
  (Weichweizen; hartweizen bleibt bei Carbonara=korrekt Durum), Gulasch
  butter->oel, Curry+PadThai olivenoel->oel, Carbonara speck->GUANCIALE (neu).
- guanciale (NEU, production-matrix): feste Herkunft Italien + Distraktoren.
Admin-Maengelliste als erledigt markiert; offen bleiben mediterrane Kraeuter
+ Marillenknoedel-Teig (Thomas-Entscheidung).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 01:17:01 +02:00
Adminator 9bc3ca7b26 Weltkueche-Admin: Maengelliste Zutaten vs. Rezept (manuelle Pruefung aller 30 Gerichte)
Panel oben im QA-Tool: Zutaten, die im Rezepttext nicht vorkommen oder falsch
sind (z.B. Sauerbraten/mehl, Kirschtorte/milch, Pizza/hartweizen, Carbonara/speck
statt Guanciale, Gulasch/tomaten+butter, Curry+PadThai+Guacamole/olivenoel).
Plus mediterrane-Kraeuter-Kandidaten und kleinere Rezept-Themen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 00:41:26 +02:00
Adminator 13f14c26a8 Weltkueche: 6 Zutaten mit fest verdrahteter Herkunft (Thomas-Vorgaben)
Kein Automatismus mehr - Zutat hat feste Laenderliste (correct) + feste
Distraktoren, die das Spiel 1:1 ausliest. Vorrang vor Consumer/Heimat.
- greyerzer (NEU, "Greyerzer & Vacherin"): Schweiz+Frankreich -> ersetzt den
  mozzarella-Hack im Fondue (Signatur entfernt, Rezept nannte Gruyere+Vacherin
  schon korrekt).
- hefe: Oesterreich+Frankreich | lamm: Neuseeland+UK (Import, nicht Griechenland)
  tomaten: Niederlande+Spanien | schmand: Oesterreich+Deutschland
  fischsauce: Thailand+Vietnam (nicht Italien).
game.html: schlanker Auslese-Zweig fuer `correct`/`distractors` (isWorldIngredient
+ ING-Build + Aufloesungstext). admin spiegelt als "Feste Herkunft".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 00:35:24 +02:00
Adminator d7dd3f9de5 Weltkueche: Cache-Busting (no-store) fuer die Daten-Fetches im Spiel
Damit das Spiel nach einem Deploy nie eine gecachte dishes/production-matrix
zeigt (gleiche Klasse Fix wie im Admin-Tool).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 23:55:33 +02:00
Adminator 8977901e89 Weltkueche: Consumer-Regel fuer ALLE DACH-Grundzutaten + Alkohol raus + Fixes
HEIMAT-REGEL-FIX (Thomas): Grundzutaten haengen nicht mehr am Gericht-Land,
sondern am User-Land, wenn dort nennenswerte Produktion. Quiche-Milch in AT =
oesterreichische Milch, nicht franzoesische.
- heimisch+market (AT/DE/CH, market DE) an: milch, butter, topfen, fleisch,
  rind-ungarn, apfel, zucker, zwiebel, champignons, wurzelgemuese, mais,
  kirschen, mehl (+ eier/kartoffel schon). = 16 Grundzutaten consumer-anchored.
- huhn-spanien (Paella): heimisch AT/DE/CH, market DE, Distraktoren
  spanien/frankreich/marokko/aegypten/tuerkei (Spanien = Gericht-Land als Lockvogel).
- BEWUSST NICHT heimisch: tomaten + lamm (Import >2/3) -> bleiben international.
  Signaturzutaten (speck/feta/mozzarella/bergkaese) bleiben am Gericht.
- Zurueckgehalten fuer Thomas-Review: kuemmel/majoran (Kraeuter-Graubereich).

ALKOHOL RAUS (Thomas: "ist fuer Kinder", alle Gerichte):
- Apfelstrudel: Rum-Ing raus. Sauerbraten: Wein-Ing raus, Marinade Rotwein->
  roter Traubensaft. Kirschtorte: Kirschwasser->Kirschsaft. Fondue: Wein-Ing raus,
  Weisswein->Apfelsaft+Zitrone, Kirschwasser->Wasser, Schnaps-Gag->Lied singen.
  Zuercher Geschnetzeltes: Wein-Ing raus, ablöschen mit Gemuesebruehe. Moussaka:
  Wein-Ing raus, einkochen mit Gemuesebruehe.
- Bayerische Brezel: Mozzarella raus (Obatzda = Camembert+Frischkaese).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 23:54:43 +02:00
Adminator f934a7f9e4 Weltkueche: mediterrane Kraeuter raus aus Maultaschen + Sauerbraten
Kommen im Rezept nicht vor und passen historisch nicht in die regionale
DACH-Kueche (Thomas). Mediterrane Gerichte (Pizza/Ratatouille/Moussaka/Paella)
behalten Kraeuter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 23:41:34 +02:00
Adminator 0c2225594b Weltkueche: Semmelbroesel zurueck in Speisekammer + Brezel-Rezept bereinigt
- semmelbroesel: heimisch/market/distractors wieder entfernt -> nicht mehr
  abgefragt, kommt aus der Speisekammer (Thomas: verarbeitete Staples = Pantry).
- bayerische-brezel: Wein + mediterrane Kraeuter aus ings raus (kommen im
  Rezept nicht vor). Bier im Obatzda-Schritt durch einen Schuss Milch ersetzt
  (fuer Kinder ungeeignet).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 23:38:38 +02:00
Adminator 7f997bff84 Weltkueche-Admin: Cache-Busting der Daten-Fetches (?v=ts + no-store)
Verhindert, dass die QA-Uebersicht eine gecachte production-matrix.json/dishes.json
zeigt. Loest "Admin zeigt noch alten Stand nach Deploy".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 18:35:52 +02:00
Adminator 423490260d Weltkueche Teil B: Consumer-Anker (Herkunft folgt dem User-Land) — Pilot Ei/Kartoffel/Semmelbroesel
Grundzutaten, die im Land des Users heimisch sind, kommen jetzt aus dem Land
des USERS statt aus dem Gericht-Land (Carbonara-Ei in AT = AT-Ei). Opt-in pro
Zutat ueber neues Feld `heimisch`; Signaturzutaten bleiben am Gericht.

game.html:
- buildTaskFor: neuer Consumer-Zweig (vor Heimat-Regel). correct = [User-Land,
  ...market], Distraktoren explizit (`distractors`) oder berechnet. Immer 5 Karten.
- _wkUser(): WK_COUNTRY (AT/DE/CH/LI) -> Laender-Slug, Fallback AT.
- isWorldIngredient: Zutat mit `heimisch` wird immer abgefragt (z.B. Semmelbroesel).
- ING-Build: heimisch/market/distractors werden aus der Matrix mitkopiert
  (wurden vorher weggelassen -> Felder kamen nie im Spiel an).
- Aufloesungstext: Consumer-Fall ("kaufst du, wo du einkaufst - im eigenen Land").

Daten (production-matrix.json), Thomas-Vorgaben:
- eier:  heimisch AT/DE/CH, market DE+NL  -> AT-User: AT+DE+NL, DE-User: DE+NL
- kartoffel: heimisch AT/DE/CH, market DE -> AT+DE (alle Gerichte)
- semmelbroesel: heimisch AT/DE, market DE, distractors GR/TR/EG/TN/IE; jetzt abgefragt

admin-weltkueche.html: Consumer-Zweig gespiegelt + AT/DE/CH-Umschalter.

Nebenbefund (NICHT angefasst): correct_for wird im Spiel nie gelesen (nur Admin
las es) -> mehl/majoran/safran/feta-lamm-Kuratierung wirkte bisher nur im Admin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 18:21:39 +02:00
Adminator b6b6c6ccce Weltkueche: Ei-Zweitherkunft AT = Deutschland+Niederlande (statt Slowakei)
- eier.correct_for.oesterreich: ["deutschland","niederlande"] (war ["slowakei"])
  -> AT-Ei-Gerichte zeigen jetzt AT + DE + NL (Thomas-Vorgabe: NL/DE sind die
  echten Import-Herkuenfte, SK spielt kaum Rolle). Gilt fuer alle 7 AT-Gerichte
  mit Ei; DE-Gerichte zeigen via Heuristik ohnehin schon DE + NL.
- buildTaskFor Heimat-Zweig: Distraktoren auf 5-correct.length gedeckelt, damit
  bei 3 richtigen Laendern trotzdem 5 Karten bleiben (Punkte-Norm).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 18:08:36 +02:00
Adminator a68c58ac9e Weltkueche Teil A: Land pro Lehrer persistent (teachers.country) + Sim-Verdrahtung
- profil.html: Länderauswahl (AT/DE/CH/LI) im Lehrer-Profil, lädt/zeigt aktuelles Land.
- profile.php: speichert country in teachers.country; gibt country zurück.
- weltkueche.php: injiziert window.WK_COUNTRY = Country::current() (Kaskade Schüler→Klasse→Lehrer→AT) — Fundament für die konsumentenland-basierte Herkunft (Teil B).
Spalte teachers.country existierte bereits, keine Migration nötig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 12:13:27 +02:00
Adminator c3e61f4da7 Weltkueche: Auflösungs-Aussage korrigiert + Admin-QA kennt correct_for
- Auflösung: „Das ist ein Gericht aus <Land> → Zutaten aus <Land>" nur noch bei
  ECHTEN Signatur-Zutaten (Tiroler Speck etc.). Grundzutaten (Butter, Mehl) bekommen
  die neutrale „aus der Nähe"-Begründung statt der falschen Gericht-Herkunft-Logik.
- admin-weltkueche.html: classify() respektiert jetzt correct_for (zeigt sonst falsche
  Herkünfte, z.B. Mehl AT+DE statt AT+Ungarn).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 11:50:07 +02:00
Adminator b353f25b07 Weltkueche Punktesystem: neue Formel (25 x verbleibende Falsche) + immer 5 Karten
- Auswahl auf IMMER 2 richtig + 3 Distraktoren (5 Karten) genormt (Welt-Zweig
  wuerfelte 2-4). Damit ist die Bewertung fair & vergleichbar.
- Punkte pro richtigem Klick = 25 x (noch verbleibende falsche Optionen).
  Frueh gefunden = mehr; jeder Fehlklick vorher senkt den Wert. Max 2x75 = 150,
  0 moeglich (alle falsch zuerst). 50er-Bonus entfaellt (150 = 75+75).
- Live-Feedback: schwebendes +75/+50/+25/+0 (gruen) bzw. "Fehlversuch -
  erreichbare Punkte sinken" (rot) bei jedem Klick.
- Banner/Interim/Onboarding-Text angepasst; trade-Abzeichen 3000 -> 2000
  (da Max jetzt 150 statt ~250 pro Zutat).
- BUGFIX: dasselbe richtige Land liess sich doppelt anklicken/zaehlen -> Guard.
Playwright-verifiziert: 5 Karten, 75+0(Guard)+75=150, 0 JS-Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 11:25:49 +02:00
Adminator 5fbbf506b5 Schulbuch: 26 Fotos aus Prod-Kollegen-Uploads + lokalen Zuordnungen eingebaut
Prod (Kollegen, echte VBG-Orte, je Slot bestes gewaehlt): kofferdetektiv komplett
(Gepaeckband Stuttgart, Koffer-Indizien, Anzeigetafel, Nachtzug, Kondensstreifen,
Hafen-Bregenz), busfahrt (Reisebus, Flixbus, Grenzschild, Pfaender-Dreilaenderblick),
energiemanager (Luenersee-Stausee, Pumpspeicher, Smartmeter, Strommast), klima
(Gletscher, Gewaechshaus), sonnensystem (Sonnenaufgang), eu-werkstatt (EU-Flagge),
farmer (Alp-Kuehe), tourismusregion (Wegweiser, Parkplatz Golm), weltkueche
(Fairtrade, Fruehstueckstisch). Colleague-Fotos ersetzen meine Trip-Platzhalter bei
fairtrade-siegel + opener-parkplatz-voll. Lokal: souvenirs, marktstand, radweg.
Alle: auto-orient, EXIF/GPS gestrippt, 1600px, WebP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 23:22:31 +02:00
Adminator d4c7fd5009 Schulbuch: 9 neue Fotos (EXIF/GPS gestrippt, WebP) in leere Buch-Slots
- weltkueche: sennerei-kaese, fairtrade-siegel, supermarkt-herkunftsschilder
- tourismustal: sommerloch-leere-terrasse (Skiort im Sommer)
- tourismusregion: opener-parkplatz-voll, standort-jausenstation (NEU Ordner)
- logistik: alpen-transit (Container-LKW)
- energiemanager: strommix-wasserkraft (Wasserkraft-Turbine)
- farmer: opener-feld-berg (Maisfeld)
Aus .humanInput/fotosNeu2; auto-orientiert, 1600px, q82.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 22:55:04 +02:00
Adminator bf2a4cd0d1 Weltkueche Herkunft: kuratierte AT-Zweitlieferanten + Distraktor-Fixes (Thomas-Review)
Neu: additives `correct_for[heimat]` in buildTaskFor — uebersteuert die Gravitations-
Schaetzung NUR bei kuratierten Zutaten (echte Handelsdaten), alles andere unveraendert.
- mehl→AT+Ungarn (Weizenimport 2023: HU 604k >> DE 41k), kartoffel→AT+Schweiz,
  eier→AT+Slowakei (Variation weg vom Dauer-AT+DE), majoran→AT+Deutschland (statt FR),
  safran→Spanien+Iran (statt IT mit 1 t), feta/lamm→Griechenland+Bulgarien (Balkan statt FR).
- Datenfixes gegen „echter Lieferant faelschlich als Distraktor": AT raus aus Mozzarella-
  climate_possible + als Produzent ergaenzt; CH raus aus Kartoffel-climate_possible +
  Produzent; SK als Eier-Produzent; Aegypten-Zwiebel local→global (echter kleiner Lieferant).
- Verifiziert: 7 kuratierte korrekt; keine Regression (DE-Gerichte nutzen weiter Algorithmus).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 21:36:48 +02:00
Adminator 4c0183f2e0 Weltkueche Zutaten-Review R2: Apfelstrudel/Sachertorte/Marillenknoedel/Schnitzel exakt nach Rezept
- Apfelstrudel: exakte Liste (Apfel, Zitrone, Butter, Mehl, Oel, Essig, Semmelbroesel,
  Zucker, Zimt, Rosinen, Rum, Staubzucker). Eier + Mandeln + Olivenoel raus.
- Sachertorte: Schokoladenkuvertuere statt Kakao, Kristall- + Staubzucker, Schlagobers
  (mit Hinweis: kommt DANEBEN, nicht in den Teig) statt Milch.
- Marillenknoedel: Topfenteig, Kartoffel raus.
- Wiener Schnitzel: Kalbfleisch, Mehl, Ei, Semmelbroesel, Butterschmalz (+ Zitrone,
  Erdaepfelsalat-Beilage). Pfeffer + Butter raus.
- 8 neue Zutaten: 3 Welt (Schokolade=Kakao-Herkunft, Kristallzucker=EU-Ruebe, Rum=Karibik),
  5 lokal (Semmelbroesel, Oel, Staubzucker, Butterschmalz, Schlagobers).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 21:23:56 +02:00
Adminator 0c4064bb58 Weltkueche Zutaten-Review (Thomas): echte kulinarische Fehler bei AT-Gerichten behoben
- Mediterrane Kraeuter RAUS bei Groestl, Kasnudeln, Wurzelfleisch, Kaesespaetzle, Schnitzel (unpassend).
- Olivenoel RAUS bei Wurzelfleisch + Apfelstrudel (untypisch).
- Mozzarella RAUS bei Kaesespaetzle -> echter neuer `bergkaese` (Vorarlberger Bergkaese; Herkunft AT + Schweiz statt faelschlich Italien).
- Hartweizen/Durum RAUS bei Marillenknoedel.
- Wiener Schnitzel Fleisch -> neuer `kalbfleisch` (ausschliesslich Kalb; Herkunft AT + Niederlande = EU-Nr.1 Kalbfleisch).
- Kasnudeln: neuer `minze` (Nudelminze, charakteristisch; Herkunft AT + Deutschland).
- 3 neue Zutaten in production-matrix mit realistischen Produzenten/Markt; Gravitation liefert plausible 2. Lieferanten (verifiziert).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 21:08:15 +02:00
Adminator 5d3cd1bdcf Fix: BASE_URL folgt dem echten Host (Glossar-Fetch auf v3) + Weltkueche „Nachbarland"-Wortlaut
- app.php: BASE_URL fuer *.geograsim.at war hart 'https://geograsim.at' -> auf
  v3.geograsim.at lud die Glossar-API cross-origin -> CORS/NetworkError
  ("Glossar konnte nicht geladen werden"). Jetzt BASE_URL='https://'.$host
  (same-origin auf jeder Umgebung; prod-neutral, geograsim.at bleibt gleich).
- weltkueche/game.html: Zweit-Lieferant hiess pauschal „Nachbarland" — Frankreich
  ist aber kein Nachbar Oesterreichs. Jetzt „europaeisches Land in unserer Naehe".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 16:30:59 +02:00
Adminator 5b0505b140 Sonnensystem-Review: Finsternis-Geometrie-Fix + Vergleichsansichten + Anleitung + Photon-Skala
Aufgaben (observations.json):
- Finsternis-Bug behoben: Mond wurde siderisch statt synodisch positioniert -> Konstellation schief. Jetzt exakt Neumond (solar)/Vollmond (lunar) via moonPhaseTimeDays(). Sim-Detektor bestaetigt (Badge) SONNEN-/MONDFINSTERNIS.
- Pol-Aufgaben (Polartag/Polarnacht): Kamera -> neue erdzentrierte Draufsicht earth-top (Sonne geht am Pol nie unter/auf sichtbar).
- Mondphasen: Start bei Neumond (moonPhaseStart) passend zur Reihenfolge Neumond->zunehmend->Vollmond->abnehmend.
- Vergleichsansichten (Strahlen-Steilheit, Tag-Nacht-Gleiche, ohne Achsneigung): neue nahe Seitenansicht earth-side + Sommer/Winter- bzw. Maerz/Sept-Phasen + klare watchHints.
- "Was bewegt sich worum": Antwort praezisiert (Mond um die Erde).
- Apfel-Vergleich: 109 x (Leerzeichen), Erde = Mohnkorn (~1 mm), lange Striche raus, Distraktor Murmel angepasst.
- Photon: schwankende 8:20 erwaehnt, watchHint "mind. 30 s zaehlen + hochrechnen", Speed watchbarer.

game.html:
- Graue/unsichtbare Fact-/Onboarding-Texte: dunkle Lesefarbe gesetzt.
- Kameras earth-top + earth-side; obsApplyState-Fallback fuer button-lose Kameras.
- moonPhaseTimeDays(new|full) (synodisch, Anker Vollmond 3.1.).
- Anleitung-Button (Intro+Aufloesung) + Overlay: erklaert das Bedienfeld Schritt fuer Schritt.
- Photon-Skala unten: 8 Segmente (1/min), fuellt mit Fortschritt; Photon pulsiert die ganze Strecke.

Getestet mit Playwright: 0 JS-Fehler, Finsternis-Badges korrekt, Photon-Skala fuellt, alle DOM-Elemente da.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 12:04:53 +02:00
Adminator c950f954d1 Admin-ToDo/Review-Seite + Staging-Self-Service-Admin-Login
- admin-todo.html: mobile-freundliche ToDo/Review-Liste (Prioritaeten, Haekchen +
  Notizfelder je Punkt, localStorage, Notizen-Export), admin-gated, noindex. In
  admin-licenses.html Nav + Backend-Werkzeuge verlinkt.
- admin.php: devPin auch bei STAGING=1 (v3, Mail bewusst tot) -> Self-Service-
  Admin-Login ohne Mailversand. Prod unveraendert (STAGING dort nie definiert,
  2FA-Mail-Pflicht bleibt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 11:31:02 +02:00
Adminator a568f81b0a vendor-Fix: PHPMailer (v7.0.2, hand-vendored) im Autoloader wiederhergestellt
Beim Hinzufuegen von firebase/php-jwt + predis war PHPMailer aus composer.json
und dem Autoloader gefallen (Dateien fehlten ganz). Folge: `new PHPMailer` fatalt
-> 500 bei register/reset/2FA/Schueler-Zugang. Auf v3 aufgedeckt und behoben:
composer.json require + installed.json + Autoloader (App/vendor) mit PHPMailer,
firebase, predis. Verifiziert: alle 3 Namespaces laden. KRITISCH fuer Prod-Deploy
(Manifest spiegelt vendor/), sonst waere dort der Mailversand gebrochen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 00:29:03 +02:00
Adminator 182a87978e Auth-Umschalter: Session.php verzweigt auf AUTH_BACKEND (jwt = JWT-Access-Cookie + widerrufbare Redis-Session)
- teacher/admin Login/Read/Logout branchen auf AUTH_BACKEND; session-Pfad unveraendert (Live-Default, prod-sicher).
- jwt: loginTeacher/loginAdmin legen Redis-Session (SessionStore) an + setzen kurzlebiges JWT-Cookie (ggs_t/ggs_a) + langes Sid-Cookie (ggs_ts/ggs_as).
- Read: Fast-Path via signiertem JWT (zustandslos), Refresh-Path via Sid->Redis + frisches JWT; Widerruf = Redis DEL bei Logout.
- Teacher/Admin getrennte Cookies (koexistieren), typ-Check gegen Rollenverwechslung. Lazy-require von Jwt/SessionStore (jeder Einstiegspfad).
- Schueler unveraendert (UUID-Cookie + DB, schon pod-unabhaengig).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 00:15:22 +02:00
Adminator 5b91a5bf84 Admin-Login lokal: 2FA-PIN ohne Mailversand (Dev) + Mailer-Fehler abfangen
Problem: lokal konnte man sich nicht als Admin anmelden — der 2FA-PIN kam nie
an (kein SMTP), und Mailer::sendAdminPin() warf sogar eine Exception, die den
Login komplett abbrach (leere Antwort → „MFA fehlt").

- admin.php: Mailversand nur auf Prod + in try/catch (Mail-Fehler darf den
  Login NIE blocken). Lokal (!IS_PRODUCTION) wird der PIN als `devPin` in der
  Login-Antwort zurückgegeben — auf Prod NIEMALS (2FA bleibt Pflicht).
- admin.html: devPin wird lokal automatisch ins PIN-Feld vorausgefüllt + als
  Hinweis angezeigt.

Verifiziert (temp Test-Admin): login → devPin → verify → authenticated:true,
admin-weltkueche.html erreichbar. Prod unberührt (IS_PRODUCTION-Gate).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 19:13:22 +02:00
Adminator 0555a2d245 Lehrer-Aktivitätsstreifen: Farbe je Sim, Höhe=Erfolg, Rahmen=Zustand
Tages-Balken neu kodiert (nach Thomas' Spec):
- Eine Farbe pro Simulation (simColor, stabil per Hash).
- Balkenhöhe = Maß für den Erfolg (mehr Sterne → höher) → Leistungen auf einen
  Blick vergleichbar.
- Rahmen kodiert den Zustand: fertig gespielt = geschlossener dünner Rahmen ·
  noch dran = rechts offener Rahmen · Abbruch = gestrichelt + ✖ am Ende.
- Beschriftung: 3-Buchstaben-Kürzel (eng) → Wort → Wort+Lektion (Platz).
- Legende entsprechend erneuert.

Datenbasis vorhanden: live.php summary liefert success (inkl. 'aborted'),
stars, completed pro Session. Offen für v2: „läuft noch"-Balken aus aktiven
live_sessions einblenden (Tag-View ist bisher assessments-basiert = Abschlüsse).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 18:14:56 +02:00
Adminator 5f603cb193 Tourismusregion: Umwelt beißt jetzt + Schüler-Erklärung (Balance-Pass 1)
Diagnose per headless-Sim-Harness (.dontDeploy/tools/tr-balance-harness.mjs):
„alles zubauen" senkte die Umwelt nur 75→50 (über der 45-Schwelle) und der
Exit-Umwelt-Malus war TOTER Flag-Code → man konnte der Umwelt kaum schaden.

- model.js: (1) Exit-Umwelt-Malus wirklich angewandt — env<60 zieht bei
  umweltbewussten Gästen Zufriedenheit ab (je tiefer, desto mehr). (2) Über-
  tourismus-Drift im 10-s-Takt: dichte Bebauung (>6 Bauwerke) + Verkehr zehren
  die Umwelt aus. (3) Einmalige Lern-Warnkarte, wenn Umwelt < 50 kippt.
- data.js: Naturschutzgebiet-Beschreibung klar (rettet die Umwelt +20, Natur-
  Attraktionen +15 %, sperrt Bauplätze — Naturschutz vs. Ausbau abwägen).
- main.js: Willkommenskarte erklärt die Umwelt-Mechanik für Schüler.

Messung nachher: „alles zubauen" → Umwelt 75→37 (unter 45), Umweltziel
verfehlt, Ø-Sterne fallen. Feintuning Sterne-Kurve/Ansprüche-Eskalation
bleibt für Thomas' Playtest (Game-Feeling headless nicht bewertbar).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 18:10:42 +02:00
Adminator 913f161502 Admin: Weltküche-Herkunftsübersicht (QA-Seite)
Neue admin-geschützte Seite admin-weltkueche.html: spiegelt die Spiel-Logik
(Heimat + Handels-Gravitation Menge÷Entfernung + Markt-Tags) und zeigt pro
Gericht × Zutat die vom Spiel akzeptierten Herkunftsländer (🟢 richtig) und
die Distraktoren (🔴). Filter nach Kategorie + Suche + „nur Auffällige".
Auffällig-Heuristik: Heimat-Zweig-Zutat mit „richtigem" Land > 1500 km
(surft echte Ausreißer, ohne Welt-Zutaten wie Pfeffer falsch zu flaggen) —
aktuell 5 Kandidaten. In Admin-Nav + Backend-Werkzeuge verlinkt.
Verifiziert: 30 Gerichte, Kartoffel/Eier → [AT, DE] (nicht Frankreich).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 18:01:50 +02:00
Adminator 665b43e069 Doku: Relevante Infos für Lehrpersonen (Zusammenfassung Lehrer-Cockpit)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 15:16:08 +02:00
Adminator d9f9f901a1 Logistik-Pause friert die Aktivzeit ein (Lehrer-Cockpit)
Problem: bei Pause lief die „Seit"-Zeit im Lehrer-Cockpit weiter, obwohl das
Spiel steht — live-client.js akkumulierte activeMs unabhängig vom Pause-Zustand.

- live-client.js: opt-in Pause-Unterstützung. window.__GGS_LIVE__.setPaused(bool)
  friert die activeMs-Buchhaltung ein (lastTickAt wird weiter aktualisiert, damit
  beim Fortsetzen nichts nachspringt). __paused zusätzlich im Heartbeat. Default
  aus → alle anderen Sims unberührt.
- logistik game.html: Pause-/Auto-Button ruft setPaused(mode==='pause').

Verifiziert (Playwright): activeMs 0→2000 (läuft) → 2000 (Pause eingefroren)
→ 3999 (Fortsetzen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 15:12:50 +02:00
Adminator e23ebd960e Weltküche: Herkunft per Handels-Gravitation + Punktefix „alle falsch = 0"
- Zweit-Lieferant im Heimat-Zweig jetzt via Handels-Gravitation (Menge ÷
  Entfernung zur Heimat, Haversine über Länder-Koordinaten) statt roher
  Weltproduktion. AT-Eier → Deutschland (468 km) statt Frankreich (916 km),
  obwohl FR mehr Eier erzeugt. Distraktoren bleiben nie echte AT-Lieferanten.
  Verifiziert: Eier/Kartoffel/Milch → [Österreich, Deutschland]; alle 70
  Zutaten bauen fehlerfrei.
- Punktefix: Strafe pro Fehlklick = 100 ÷ Anzahl Distraktoren → „alle Falschen
  zuerst" ergibt IMMER 0 (bei 4 Distraktoren −25, bei 2 −50). Task-Banner
  zeigt die tatsächliche Strafe + „alle falsch zuerst = 0".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 15:09:16 +02:00
Adminator c1f90e255d Lehreransicht aufräumen: Sim-Labels + Logistik-Spalten + assessment-Skip-Fix
- SIM_LABELS: EIN Wort + eindeutiges 3-Buchstaben-Kürzel je App (zentrale
  Quelle simWord()/simCode()). Tages-Balken nutzt jetzt Kürzel (eng) → Wort
  (mittel) → Wort+Lektion (breit) statt kryptischem "Log L1".
- LIVE_PRIMARY_FIELDS['logistik'] ergänzt (Stufe · Budget · Geliefert/Gesamt ·
  Unterwegs · Offen · Verspätet · Fortschritt) — vorher Roh-Dump aller Keys.
- Aktuell-Fallback (uncurated Sims): interne __-Heartbeat-Keys + simId/
  timestamp/screen ausgeblendet, nicht mehr als Spalten.
- assessment.php: Skip-Bug gefixt — las phase aus $body['processLog']['phase'],
  gesendet wird $body['phase']. Dadurch landeten alle 'started'/'running'-Pings
  als LEERE Zeilen in assessments und verschmutzten Ergebnisse/Tag. Jetzt
  greift der Skip; nur echte 'completed'-Abschlüsse werden gespeichert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 15:05:42 +02:00
Adminator 37f6238d05 Auth Phase 2: Session-Zugriffe kanalisieren (verhaltensneutral)
Vorbereitung für den JWT+Redis-Umschalter: alle verstreuten
$_SESSION['teacher_id'|admin_id']-Zugriffe laufen jetzt durch Session::-Helper,
damit der Backend-Tausch später EINE Datei (Session.php) ist statt 18 Stellen.

- Session.php: neue Admin-Helper adminId()/loginAdmin()/logoutAdmin()/
  requireAdmin() (mirror der Teacher-Helper). teacherId()/adminId() starten
  die Session jetzt lazy (Aufrufer müssen Session::start() nicht mehr).
- 18 Fundstellen kanalisiert (admin.php, admin-accounts, admin-modules,
  licenses, levels, waypoints, foto-upload, admin_gate, save_map).
- Country.php + app.php cockpit_href: über Session:: mit class_exists-Fallback
  (Bootstrap/Lib-Kontext, Session evtl. nicht geladen).

Verhaltensneutral: 14/14 Lint OK, Helper-Round-Trip 6/6 PASS, Live-Smoke
(admin status/modules/accounts/licenses) weist unauth. Requests wie bisher
mit 401/403 ab. AUTH_BACKEND bleibt 'session' — Live unverändert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 16:33:14 +02:00
Adminator 5d94572569 Auth-Fundament: JWT + Redis (Phase 1, non-breaking)
Vorbereitung für pod-unabhängige Sessions (k8s-Ziel). Ändert am Live-System
NICHTS: AUTH_BACKEND='session' bleibt Default, die neuen Libs sind noch
nirgends verdrahtet.

- composer eingeführt: firebase/php-jwt ^6.11 + predis/predis ^2.4 (pure PHP,
  keine Redis-Extension nötig → läuft auf XAMPP wie im Prod-Image). vendor/
  committed (kein Composer auf dem Server).
- Jwt.php: HS256-Access-Token (issue/verify), decode() IMMER mit explizitem
  Algorithmus (mitigiert Algorithmus-Confusion). 8/8 Tests grün.
- SessionStore.php: predis-gestützter Session/Refresh-Store (sess:<sid>,
  Sliding-TTL, revoke=DEL) — die widerrufbare Session hinter dem kurzen JWT.
- app.php: vendor/autoload (guarded), Konstanten AUTH_BACKEND (Default session),
  REDIS_URL, JWT_TTL, JWT_REFRESH_TTL. JWT_SECRET bewusst ohne Default.
- docker-compose.dev.yml: Redis (+ redis-commander UI) für lokale Dev-Parität.
- .env.example um Auth-Block ergänzt.

Nächste Phasen: $_SESSION-Zugriffe (293) durch Session:: kanalisieren →
Backend-Umschalter in Session.php → doppelgleisiger Cutover → Reporting auf
student_id-FK ziehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 16:21:27 +02:00
Adminator db4b75c5e6 Tourismusregion Quick-Fixes: Startflow, Auto-Save, Lift-Talstation, iPad-Responsive
- Startbildschirm: Name/Klasscode-Felder entfernt (Kinder sind über die
  Plattform-Session identifiziert). Klick auf eine Wegkarte startet die
  Partie sofort — separater „Partie starten"-Button entfernt.
- Abschluss: „Ergebnis speichern"-Button entfernt; Speichern passiert schon
  automatisch am Partie-Ende (submitAssessment). Hinweis „automatisch
  gespeichert" ergänzt. btnSave-Listener null-sicher gemacht.
- Seilbahn: Talstation (Baukörper + Satteldach + Umlenkrad) ergänzt — der
  Lift-Anfang wirkte vorher „transparent".
- Responsive/iPad: Panels bekommen Höhen-Deckel + internes Scrollen (decken
  nie den ganzen Schirm zu). Neue Media-Query für iPad-Landscape (901–1200px
  / niedrige Höhe): kompaktere Panels, Graphen nach rechts-unten aus dem
  Info-Panel-Bereich. Sehr flache Screens blenden die Graphen aus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 14:15:41 +02:00
Adminator 9937fbf62a Fix: Länder-Filter im Lehrplan-Bezug (modul_detail.php) wirkungslos
Der Filter-JS liest a.dataset.anchorType, aber die Anker-<div>s setzten
kein data-anchor-type (nur data-country). Dadurch war `type === 'fach'`
nie wahr → für ein gewähltes Land wurde nie ein Anker ausgeblendet.
Fix: data-anchor-type am Anker-div ergänzt. Gilt für alle Modul-Seiten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 13:46:42 +02:00
Adminator 5655af81bf Lehrer-Observability + Weltküche-Inhalt + Wrapper-Fix + Passstraße
teacher.html: Menü „Live" → „Ergebnisse" mit Unteransichten
Tag·Aktuell·Vergleich·Gesamt (alte Ergebnisse-Ansicht ist jetzt „Gesamt").
Neue Kennzahl Qualität (grafischer richtig/falsch-Balken) in Aktuell +
Vergleich + Weltküche-Zeile, Gerichte-Zähler (Fleiß), Labels entschärft
(„Kritisch" → Stark/Solide/Viele Fehler).

Weltküche: korrekte Artikel (der/die/das) für alle 70 Zutaten in
production-matrix.json; verdrehten Herkunfts-Satz + Heimat-Banner neu und
marktbewusst (Ukraine-Distraktor ehrlich erklärt), „man"-Konstruktionen
für Singular/Plural.

_scripts.php: renderPage() injizierte Favicon per strrpos ins letzte
</head> — das lag in teacher.htmls printBatchTickets-JS-String → SyntaxError
zerschoss das ganze Inline-Script auf /teacher. Fix: strpos (erstes </head>).

tourismusregion: neue Karte passstrasse in maps-custom.json (Route bergauf
136 Punkte, 41 Bauslots) aus Editor-Export.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 13:32:24 +02:00
Adminator 4bfb6539cb Weltkueche Fundament: geschaffte Gerichte persistent (localStorage, Schueler-Reload) + Backend-Submit pro Gericht (Lehrer-Historie: score/correctClicks/wrongClicks/completed) + Fehler-Zaehler
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 12:38:03 +02:00
Adminator f6df2eb79f Teacher: Triage 'Nachfassen'->'Nicht aktiv'; Weltkueche live-state phase-Bug gefixt (zeigte immer 'forschen', jetzt 'Gericht X · Zutat Y/Z')
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 12:21:24 +02:00
Adminator ec102b7c36 Weltkueche Punkte-Regel: richtig +100, jeder Fehlklick -25 (progressiv, statt Flat 100/60); Regel im Task-Banner sichtbar; Heimat-Zweig 2 Korrekte (Heimat + 1 realistische)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 12:08:45 +02:00
Adminator 9ec7ad67f1 Weltkueche Heimat-Regel ehrlich: regionale Gerichte haben Heimat + realistische EU-Nachbar-Produzenten als korrekt (immer >=2 Optionen), ferne/klimafremde bleiben Distraktoren
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 12:02:22 +02:00
Adminator b9bcbf9198 Backend-Editoren admin-geschuetzt (admin_gate) + Admin-Hub auf Admin-Seite; save_map admin-oder-lehrer; foto-upload admin-bypass (PIN bleibt)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 11:54:08 +02:00
Adminator 2bdaa0f6cc Admin-Accounts-Editor (CRUD inkl. 2FA-Mail) + Regel: Lehrer-Username = E-Mail (Maske + register), Schueler frei
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 11:43:04 +02:00
Adminator b0fe3fdf86 Tourismusregion: neues Passstrassen-Hintergrundbild (Photoshop, 1536x1024)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-17 19:40:26 +02:00
Adminator ad627331f0 Fix: save_map.php ruft Session::start() vor requireTeacher (Editor-Speichern schrieb nie in Datei, fiel immer auf Copy-Fallback zurueck)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-17 19:16:09 +02:00
Adminator 75511630c8 Tourismusregion: Talschleife-Startpunkt korrigiert (Route beginnt weiter links)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-17 19:14:09 +02:00
Adminator 81ba268ea8 Tourismusregion: Custom-Karte kreuzung ergaenzt (Routen + Bauslots aus Editor)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-17 16:33:45 +02:00
Adminator 1950542ae5 Tourismusregion: Custom-Karten talschleife + seerunde (Routen + Bauslots aus Editor)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-17 15:57:41 +02:00
Adminator eba9e54c70 Review-ToDo aktualisiert: Batch 2 deployed; Heli-Navi gestrichen; neue Liste
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-17 12:02:09 +02:00
Adminator 8b1088a070 Balancing: sonnensystem (Erst-Treffer-Bewertung, Loesung nicht aufdecken, Level-Badge) + energiemanager (echter Blackout an Netzfrequenz, Schwierigkeitsrampe)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 21:59:32 +02:00
Adminator fb44ec7afe student_id-FK-Migration Stufe 1+2: Name-Joins -> student_sessions.student_id
- student_sessions: neue Spalte student_id (+Index), backfilled aus (class_id,display_name)
- Session::createStudent setzt student_id (per Lookup, falls nicht uebergeben)
- alle Joins students<->student_sessions von display_name auf student_id umgestellt:
  Benchmark.php, live.php (5x), results.php (4x + runs), modules.php, profile.php, progress.php (answers+runs)
- verlustfrei verifiziert (alt-Join == neu-Join, class 99/102)

Robuster gegen Umbenennung/Doppelnamen, indexierbare Joins.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 19:16:41 +02:00
Adminator 2984d93e7e Review-Batch P0: Sicherheit + Schueler-Ergebnisse + Assessments-Aufblaehung
Sicherheit:
- progress.php: IDOR geschlossen (student_id-Zweig verlangt Lehrer-Login + Klassenbesitz)
- licenses.php: ?admin=1 und generate nur noch fuer echten Admin; Collision-Retry mit Cap
- .htaccess: .env/.sh/.sql/.bak Deny (Defense-in-Depth)

Schueler-Ergebnisse (kritisch):
- sim-metrics.js + .ivm-CSS in schueler.html; renderOwnRunCard-Fallback zeigt jetzt
  Sterne + grafische Kennzahlen fuer die 9 bisher leeren Sims
- sim-metrics.js: klima final_budget als "Mio EUR" statt "EUR" (Faktor 10^6)

Skalierung:
- assessment.php: Zwischen-Pings (phase running/started) nicht mehr persistiert
- heli: 30-s-assessmentPing entfernt (Live-Zustand laeuft ueber Heartbeat)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 16:21:25 +02:00
Adminator a3c5753cd7 Aufgabe beenden: class_modules zuruecksetzen (Bug: Schueler sah Aufgabe weiter)
stop setzt class_modules teacher_started -> free (paused=0, started_at=NULL),
sonst liest die Schueler-Ansicht (class_modules) die beendete Aufgabe weiter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 02:45:35 +02:00
Adminator 031ca87daf Aufgabe: Freigabe/Tempo entfernt (sofort startbar); Beenden als Dialog
- keine Vorbereiten/paused/Start-freigeben/Pause mehr — Aufgabe ist ab Vergabe
  sofort startbar (paused:false), Card zeigt nur "Laeuft" + Beenden
- Schueler-Card immer "Jetzt starten" (kein Warte-Zustand)
- "Aufgabe beenden" jetzt ordentlicher Dialog (#modal-stopassign) statt confirm()

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 02:41:43 +02:00
Adminator 405fcc2b69 Aufgabe: Ab-wann + Bis-wann mit gutem Picker; neue Klasse nur Weltkueche frei
- Modal: "Ab wann" (Sofort/Zeitpunkt) + "Bis wann" (Presets + date/time statt
  datetime-local), Live-Zusammenfassung; zukuenftiger Start => vorbereitet
- assignments.php: startsAt-Param (Default NOW()), T->Space
- modules.php: neue Klasse ohne Eintrag -> nur Weltkueche frei (_defaultMode),
  Rest gesperrt; Freigabe-Matrix bleibt (getrennt von der Aufgabe)
- schueler.html: Hinweis "Deine Lehrperson kann weitere Simulationen freischalten"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:39:28 +02:00
Adminator 7f774c7b2a Aufgaben-Dropdown: geplante Module nicht vergebbar (nur laufende)
stadt (Stadt & Raumplanung) auf 'geplant' gesetzt (Prod war 'aktiv').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 13:53:37 +02:00
Adminator bbef4856d0 Klassenaufgabe: Modal mit Frist-Schnellwahl + Live-Bestaetigung; Start/Tempo auf aktiver Card
- Modal "Aufgabe erteilen": Frist-Buttons (Heute/Morgen/+1 Woche/Kein Limit),
  Default +1 Woche, Tempo-Checkbox "Vorbereiten", Live-Zusammenfassung als
  Bestaetigung, Button "Auftrag erteilen"
- Aktive Klassen-Aufgabe-Card: Sim-Bild + Status (Vorbereitet/Laeuft) +
  Steuerung "Start freigeben" / "Pause" / "Beenden" direkt auf der Card
  (Tempo-Start nicht mehr in der individuellen Freigabe)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:58:04 +02:00
Adminator 3a2e65ebd9 Schueler: Auftrags-Card im Marken-Stil (Sim-Bild + Orange + 📋 Auftrag-Badge)
- renderAuftragCard nutzt card_image + .mod-card-Muster statt Verlauf+Emoji
- Warte-Zustand bei pausierter (Tempo-)Freigabe: " Wartet auf Freigabe"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:49:34 +02:00
Adminator 97144495a1 Live/Vergleich an Klassenaufgabe gekoppelt
- aktive Klassenaufgabe wird automatisch als Vergleichs-Modul vorgewaehlt
  (Chip mit 📋 markiert, immer vorne)
- Aufgaben-Banner: begonnen X/N (aus matrix) · gerade aktiv A · Oe Fortschritt
- zeigt Aufgabe auch wenn noch niemand aktiv ist (mit Hinweis)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 01:27:36 +02:00
Adminator 5f6abeeb42 Live/Aktuell: allgemeine Erfolgs-Spalten Fortschritt + Lage
- _liveProgressPct/_liveMood: aus dem Live-State jeder Sim ableitbarer
  Fortschritt (%) und Lage-Ampel (health/heuristik) -> modueluebergreifend
  vergleichbar
- weltkueche: redundante "Gerichte" ersetzt durch Fortschritt-Balken + Lage-Ampel

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 01:05:19 +02:00
Adminator ebdd1ce438 Live: weltkueche-Spalten kuratiert; pausierte Sessions bleiben sichtbar
- LIVE_PRIMARY_FIELDS.weltkueche: Gericht/Herkunft/Zutaten/Gerichte/Fehlklicks
  (statt Rohdaten-Dump aller State-Keys) — 5 Spalten, kein Quer-Scroll
- Idle/Pause: live.php Fenster 30s->2h (LIVE_IDLE_WINDOW_SEC); Aktuell zeigt
  laenger inaktive Sessions als "pausiert" (⏸, grau) statt sie zu verlieren;
  braucht-Hilfe + aktiv-Zaehler nur fuer wirklich aktive Sessions

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 00:43:40 +02:00
Adminator 07010f2302 Live: Cockpit entfernt; braucht-Hilfe + Reflexionen in Aktuell
- Live-Modi jetzt nur Tag / Aktuell / Vergleich (Cockpit-Toggle raus)
- Aktuell: Live-"braucht Hilfe"-Banner + rote Zeilen-Hervorhebung (Heuristik
  gespiegelt aus live.php), Spectator per Zeilenklick bleibt
- Reflexionen als Block am Ende von Aktuell, gecacht gegen 4s-Refresh-Flackern

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 00:32:59 +02:00
Adminator 05ace93318 Live-Tag: Sterne nach Abschluss immer am Balkenende (gold, gefuellt+leer)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 20:31:21 +02:00
Adminator 25faca0819 Lehrer-Menue: Live an 2. Stelle; Live-Default-Ansicht = Tag
- Live-Tab direkt nach Uebersicht (tabs-Array + DOM synchron)
- Live-Modus-Default 'uebersicht' (Tag), Toggle-Reihenfolge Tag zuerst

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 20:16:53 +02:00
Adminator 532956fd7d Modul-Datumsfilter: Picker auch im Detail-Modal + global persistent
- Datums-Picker (von/bis) jetzt auch im Modul-Detail-Modal; Aendern laedt die
  Ansicht mit neuem Zeitraum und haelt Uebersichts-Cards synchron
- _modFilter global in localStorage persistiert (bleibt bis Aenderung)
- resetModDateModal ↺ setzt auf Schuljahresbeginn

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 19:07:38 +02:00
Adminator 52e9fafd92 Uebersicht: Wochengrafik mit Navigation, Triage-Cards immer, Modul-Datumsfilter
Wochen-Saeulengrafik:
- Pfeile Woche vor/zurueck (Kalenderwoche, view=activity&week_offset)
- Datum unter jedem Wochentag, halbierte + condensed Schrift
- Y-Skala links mit Anzahl, skaliert auf Max-Saeulenhoehe
- rendert auch bei leerer Woche (mit Hinweis) fuer die Navigation

Triage: alle 5 Cards immer sichtbar, leere mit "Aktuell niemand betroffen".

Module — Stand der Klasse:
- von/bis-Datumsfilter (Default bis=heute, von=Schuljahresbeginn 1. August)
- neuer view=mod_stats speist die Cards datumsgefiltert
- view=module auf student_results umgestellt (Datum+Score+JSON), from/to-Filter
  wirkt auf Tabelle, Highlights, Champions, classStats; Zeitraum im Modal-Header

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 11:43:12 +02:00
Adminator e750402799 Uebersicht: Top Performers heute + neue Abzeichen heute; Wochengrafik-Labels; Spielzeit sortierbar
- results.php view=today: heutige finale Laeufe (topToday) + vorher/nachher-Progress
  je Schueler:in x Sim (aus student_results rekonstruiert) fuer Badge-Erkennung
- teacher.html: Sektion "Top Performers heute" (heutige Wertung, Sterne + Erfolg)
  ersetzt Allzeit-Top; darunter "Neue Abzeichen heute" (badges.js, jetzt erfuellt
  aber vor heute nicht)
- Wochen-Saeulengrafik: Sim-Titel klein in die Balkenabschnitte, hoehere Grafik
- Modul-Tabelle: Spielzeit-Spalte sortierbar (Quoten-Spalten schon sortierbar)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 22:43:41 +02:00
Adminator 56d8a3217b Uebersicht: Abstand vor Top-Performer-Karte
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 22:32:48 +02:00
Adminator bfc90b78de Modul-Uebersicht: relevante Kennzahlen pro App korrigiert + Quoten
Alle $MODULE_HIGHLIGHTS gegen echte Daten (Klasse 102) verifiziert und auf die
tatsaechlichen Feldnamen umgestellt (viele waren veraltet/leer, z.B. farmer,
sonnensystem, energiemanager, busfahrt, logistik, heli, tourismus*).

- kofferdetektiv NEU: Geloest-Quote, Erstversuch, Fehlversuche, Oe Hinweise
- weltkueche/fluss NEU
- fluggesellschaft NEU (aus Submit-Schema)
- Berechnete Quoten via num/den -> Prozent (Erfolgsquote statt Rohzahl):
  kofferdetektiv, weltkueche, busfahrt, logistik, energiemanager, eu-werkstatt,
  sonnensystem, tourismustal, fluggesellschaft
- heli: Planung/Start/Landung/Level; sim-metrics: Level + Oe Hinweise ergaenzt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 21:22:50 +02:00
Adminator ff285e9af3 Verlauf-Grafik aus Sim-Schema (mehrere Linien) + Ampelbalken in allen Spalten
- renderRunsLineChart nutzt wieder SIM_METRICS statt highlights -> deckt auch
  Module ohne Highlight-Def ab (weltkueche/fluss zeigten sonst nur Sterne);
  Snapshot bei einem Durchgang via renderSimMetrics-Kacheln
- Modul-Tabelle: Ampelbalken auch fuer Durchgaenge, Spielzeit, Zuletzt
  (mehr/aktueller = gruen) -> alle Spalten konsistent grafisch

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 16:14:16 +02:00
Adminator d1a01915af Modul-Klassenuebersicht: Ampelbalken pro Zelle + Verlauf als Liniengrafik
- renderModDetail: kleiner Ampelbalken je Kennzahl-Zelle, Werte spaltenweise
  normiert, Richtung aus h.agg (gruen=positiv, weniger-ist-besser invertiert)
- Sterne-Zelle mit Ampelbalken (n/5)
- Verlauf-Button zeigt statt JSON eine Liniengrafik ueber die wichtigsten
  Kennzahlen (eine Linie je Highlight-Spalte + Sterne, oben=besser, Legende
  mit letztem Wert + Trendpfeil); Snapshot-Kacheln bei nur einem Durchgang
- sim-metrics.js: dir:'lo' fuer Kennzahlen wo weniger besser ist

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 16:00:44 +02:00
Adminator 5bf8f0fe03 Drilldown: volle Modalbreite + durchgaengige Ampelfarben (gruen->rot)
- Modal max-width 640->1120px, Kachel-Grid dichter -> Kennzahlen einzeilig
- Werte UND Balken ampelgefaerbt (pct/pctInv/ratio), Sterne mit Ampelbalken
  je n/5 -> Vergleich auf einen Blick

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 15:51:33 +02:00
Adminator c83da66325 Lehrer-Drilldown: grafische Kennzahlen, Avatar-Bild, Sterne & Abzeichen
- sim-metrics.js: kuratiertes Feld-Schema pro Sim (Sterne/Prozentbalken/
  Verhältnis/Geld/Temperatur), technische Felder ausgeblendet, sauber benannt
- Drilldown zeigt Avatar als Bild (avatar:<slug>), Erfolg als Sterne,
  Parameter als grafische Kacheln, Abzeichen-Sammlung pro gespielter Sim
- live.php student_runs liefert player_progress fuer die Abzeichen

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 15:35:29 +02:00
Adminator 2db99c37ee Übersicht: Schüler-Drilldown (Klick → aktuelle Simulationsparameter, cockpit-artig)
live.php: student_runs=<id> liefert letzte Läufe (detail_json = Parameter) + Live-State
+ Recency. teacher.html: Klick auf Triage-Zeile öffnet Modal mit Erfolg/Zuletzt, Live-
Tätigkeit und den letzten Durchläufen samt geparsten Sim-Parametern (CO₂, Fehlklicks …).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 15:20:40 +02:00
Adminator 4cd6302f0a Übersicht: Erfolg-Balken-Fix (Farbe auf Füllung statt Track)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 15:16:58 +02:00
Adminator 1f00d83232 Übersicht: Empfehlung nur aus AKTUELLER Leistung (Recency)
Denkfehler behoben — eine Empfehlung reagiert auf das Zuletzt-Gesehene, nicht auf
einen Allzeit-Rückblick. live.php cockpit liefert jetzt recentErfolg (Ø letzte 3
Läufe), earlyErfolg (frühere Läufe), daysSince, recentPlays. Triage rechnet darauf:
>14 Tage weg → Nachfassen; recent schwach → Fördern; recent+viel → Begleiten; recent
stark, kaum gefordert → Fordern; recent > früher → Bestätigen. Neue Spalte „Zuletzt",
Legende erklärt: Erfolg = letzte Läufe, Verlauf = nur Historie. 4b-Daten mit
kontrollierter Recency neu (jede Gruppe besetzt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 15:11:35 +02:00
Adminator 84386e93d0 Lehrer: Default-Klasse = zuletzt gewählte (persistent), Fallback älteste
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 14:51:48 +02:00
Adminator 504b0098a6 Abzeichen: thematische Sim-Abzeichen + saubere Icons + Lehreransicht
- badges.js: 5 Basis-Abzeichen + je Sim 1 thematisches (Vier-Jahreszeiten,
  Sterne-Koch, Alpen-Pilot …), 19 sauber generierte Flat-Emblem-Icons (keine Emojis)
- Erfolge-Tab: Sammlung mit Bild-Icons, gesperrte grau = Ziele; alte Emoji-Badges raus
- Lehrer-Matrix: neue Spalte „Abzeichen" (freigeschaltet von gesamt) pro Schüler:in

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 14:35:43 +02:00
Adminator 65ca8692cb Abzeichen: gemeinsame Bibliothek + Sammlung im Erfolge-Tab
Neue badges.js: 5 einheitliche Abzeichen pro Sim (Einstieg/Fortgeschritten/
Könner/Meister/Ausdauer), Bedingungen aus allgemeinen Leistungsdaten (plays/
best_stars/level) → gelten sofort für ALLE Sims. Gesperrte Abzeichen sichtbar
als Ziele. ggsErfolgToStars() für Sterne aus normiertem Erfolg. Erfolge-Tab in
schueler.html zeigt die Sammlung pro Simulation (auch ungespielte = Ziele).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 14:24:55 +02:00
Adminator 3d1b8e49a3 Übersicht-Feinschliff + Idle-Fairness
- Überschrift „Klassenübersicht", Sektion „Empfohlene Aktivität für dich."
- Volle Legende (Gradient-Balken, Ampelpunkte, Beispiel-Sparkline)
- 4b-Testdaten: jede Triage-Karte besetzt (Beteiligen/Begleiten/Fördern/Fordern/
  Bestätigen), Verlauf in den letzten 10 Wochen inkl. steigender Fälle
- live-client.js: Idle-Fenster 90s→5min (Lesen/Denken nicht bestrafen) +
  Tab-Sichtbarkeit blendet echtes Weg-Sein aus

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 13:36:03 +02:00
Adminator 1c2fa2e1b4 Übersicht: Cap grafisch (rote Kappe über gedeckelten Verlauf-Balken)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 13:20:19 +02:00
Adminator 15ac35a7ad Lehrer-Übersicht: Interventions-Block (Erfolg/Aktiv/Verlauf + Triage) live
Neuer Block oben im Übersicht-Tab: 5 Stat-Karten (Erfolg-Klassenschnitt, Aktuell
aktiv als Streifen, Verlauf-Klassenschnitt mit Skala, Schüler, Simulationen) +
Interventions-Triage (Fördern/Fordern/Begleiten/Bestätigen/Beteiligen) mit
Erfolg-Ampelbalken, blinkender Live-Aktiv-Ampel und Verlaufs-Sparkline pro Person.
Ampelfarben nur für Leistung, Triage-Karten neutral. Alte 5 Stat-Cards raus,
Top-Performer bleibt. Wochen-Cap gegen Zwischenspeicher-Log-Ausreißer.

- results.php: neue fleiss-range 'weeks' (letzte 10 ISO-Wochen)
- 2. Testklasse „3b Klasse" (3 Schüler); Klasse 99 → „4b Klasse"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 13:17:22 +02:00
Adminator 69888445c7 Handbuch: Fachtext formative Leistungsbeurteilung + Literaturverzeichnis
Neuer Abschnitt in „6. Bewertung" (Anker #leistungsbeurteilung): formativ vs.
summativ, drei Bezugsnormen (kriterial/ipsativ/prozess statt normativ), Wirkungs-
belege mit ehrlicher Effektstärken-Einordnung, Leitplanken (Butler, Kluger&DeNisi,
keine Bestenliste), Fleiß als produktives Engagement, Diagnose→Intervention. Volles
Literaturverzeichnis (27 Quellen). Lehrer-Cockpit verlinkt an der Benchmark-Box
darauf. Normative Reflexionsaufgabe entschärft.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 00:36:05 +02:00
Adminator cb16c3742e Testklasse: realistische Leistungsdaten (12 neue Schüler + Ben/Clara)
Damit die Lehrer-Leistungsübersicht sinnvolle Werte hat. 14 Schüler:innen mit
Skill-Spread um Anna (Referenz): David 95 … Hannes 34. Pro Schüler:in ~16
Sim-Durchläufe über ~10 Module (Hochrechnung: ~70 GW-Std/Jahr, 1/3 Sims → ~20/Jahr).
Konsistent über player_progress (Matrix), assessments+student_sessions (Benchmark)
und student_results. Benchmark-Scores auf ±10 zum Ziel verifiziert. id-sicher
(username-Referenz) + idempotent (Cleanup-Block), Login gesperrt, Anna unberührt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 23:38:29 +02:00
Adminator 7915512195 Weltküche: DACH/EU-Priorität wiederhergestellt + Beschreibungen
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>
2026-07-19 16:54:39 +02:00
Adminator e7453d9072 Weltküche: echte Hauptlieferanten statt Zufall + Einheiten-Fix
- Welt-Zutaten: die 2 tatsächlichen Hauptlieferanten nach Menge (Pfeffer →
  Vietnam+Brasilien, Kakao → Elfenbeinküste+Ghana) statt log-gewichtetem Zufall,
  der kleine Produzenten (Sri Lanka, Kambodscha) fast gleich oft zog
- Reihenfolge: „beliefert unseren Markt" (eu/global) vor „selten hier" (world-far)
- fmtTonnes: „220.000 t/Jahr" statt „220 Tsd t" (kindgerecht, kein SI-Kürzel kt),
  Mio mit deutschem Dezimalkomma („1,5 Mio t")

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 16:47:35 +02:00
Adminator 1c5356422c Weltküche: Heimat-Regel – Zutaten folgen dem Gericht statt Zufall
Löst "warum kommt Deutschland nicht vor?": In regionalen Gerichten kommt jede
Zutat, die das Herkunftsland selbst erzeugt, von dort (Tiroler Gröstl → Tiroler
Speck/Kartoffeln aus Österreich). Nur was hier nicht wächst (Pfeffer, Olivenöl)
reist von weit her – die eigentliche Lektion. Datengetrieben: Test "erzeugt das
Heimatland die Zutat?" filtert automatisch, kein Länder-Bias (hängt am Gericht).

- dishes.json: heimat (25 Gerichte) + signature-Labels (5); exotische ohne heimat
- buildTaskFor: Heimat-Regel sticht Zufall aus, wenn Heimatland Produzent ist
- Klick-Feedback beantwortet "warum nicht Deutschland?" direkt (showWrong/explainChoice)
- Auflösung erklärt Heimat-Logik + zeigt Antwort auch wenn kein Top-5-Produzent

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 15:56:55 +02:00
Adminator 7d3b3693db Glossar-Sweep: 39 Einträge auf ganze Sätze (kein Stichwortdeutsch)
- 38 Einträge: Bullet-Listen/Pfeile/Tilden → ganze Sätze; nummerierte Listen
  nur mit vollständigen Sätzen; hub-flughafen: verbotenes „Sim-Spieler" ersetzt
- norroena-faehre: zerschossene Kodierung komplett neu (Island/Färöer/Irland-Fakten)
- Quellen [^n] und Glossar-Links integritätsgeprüft (nichts verloren)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 15:22:57 +02:00
Adminator 305e3a62f8 Glossar-Review: CO2-Eintrag ganze Sätze + Tabelle, Geviertstrich→Gedankenstrich, Weltküche-Onboarding
- Glossar-Renderer: Markdown-Tabellen (|…|) + .gl-table CSS
- co2-fussabdruck-lebensmittel: telegrafische Liste → ganze Sätze + saubere
  Portions-Tabelle, Avocado-Vergleich, Malpunkt statt ×
- Programmweit „—" (Geviertstrich) → „–" (Gedankenstrich): Glossar-DB,
  glossar_examples, module_info, alle 13 Buch-Kapitel
- Weltküche-Onboarding: Blatt 2 umformuliert (zweites Land nacheinander),
  Blatt 3 Lehrplan-Satz entfernt, Blatt 4 fehlendes markt.webp generiert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 15:15:29 +02:00
Adminator 0357cd188e Buch: 3 echte Fotos in neue Plätze (Schiff/Venedig/Flughafen)
Logistik: Containerschiff auf der Verkehrsträger-Seite (LKW/Zug/Schiff).
Klima: Venedig Acqua alta auf der Meeresspiegel-Seite (europäisches Beispiel).
Kofferdetektiv: Großraumflugzeug an der Fluggastbrücke (Flughafen/Flugverkehr).
Jeweils Standard + Leichte Sprache, EXIF/GPS gestrippt. Fotos von Thomas.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 12:39:37 +02:00
Adminator 6e3de69b3c Buch Weltküche: echtes Gewürzmarkt-Foto (Basar Jerusalem) in gewuerzregal-tropen
Foto von Thomas; Bildunterschriften (Leicht + Standard) an das Marktmotiv angepasst,
Herkunftsfakten (Zimt/Pfeffer/Vanille) behalten. EXIF/GPS gestrippt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 13:27:17 +02:00
Adminator c64cfda66d Buch Klima: neuer Abschnitt "Klimawandel weltweit – Nakuru-See in Kenia"
Echte Fotos (Thomas, Egerton/Kenia Feb 2024): versunkenes Ranger-Haus im gestiegenen
Nakuru-See + Klimabücher-Regal der Universitätsbibliothek. Zeigt: Klimawandel ist nicht
nur Meeresspiegel — auch ein 1.700 m hoher See in Afrika steigt und verschluckt Wald/Häuser
(40→68 km² 2010–2020, ~80.000 Familien vertrieben; UNEP/Egerton University). Standard + Leichte Sprache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 13:22:47 +02:00
Adminator e65e938b7f Schulbuch: 15 echte Fotos aus dem Foto-Backend eingebaut (EXIF/GPS gestrippt)
busfahrt (Flixbus, Grenzschild, Reisebus, Pfänder-Dreiländerblick), energiemanager
(Smart Meter, Strommast, 2× Lünersee als Silvretta/Pumpspeicher), farmer (Alm-Kuh),
klima (Gewächshaus; Lünersee → energie-wasserkraft statt Gletscher, da es ein See ist),
kofferdetektiv (Vintage-Koffer, Nachtzug-Bahnsteig, Abflugtafel Zürich), weltkueche
(Frühstückstablett). Bindestrich-Fix Lüner-See→Lünersee.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 03:02:24 +02:00
Adminator c5307017e4 Schulbuch: einheitliches App-Menü + rollenabhängiger Modus
- Kapitel-Kopf: alte Links (Modulseite/Simulation/Glossar/Lehrplan) raus, stattdessen
  einheitliches App-Menü je Rolle (Schüler: Module/Erfolge/Schulbuch/Profil; Lehrer:
  Cockpit/Schulbuch; öffentlich: Simulationen/Schulbuch), Schulbuch aktiv
- Modus je Rolle: Schüler:in → Schüler:innen/Leichte Sprache (Default Schüler:innen,
  Lehreransicht gesperrt, auch bei ?lehrer=1); Lehrperson → +Lehreransicht (Default Lehrperson)
- teacher.html: Menüpunkt "📖 Schulbuch" ergänzt
- buch.php-Übersicht: gleiches rollen-Menü statt "← Zu den Simulationen"

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 02:53:23 +02:00
Adminator 685b4700f8 Glossar: 4 erklärende Widgets + 2 Einträge anschaulicher
Widgets: Ernährungspyramide (US-1992 vs DACH-2024 vs Denkfehler), Kakao-Bohne
(Schoko-Fließband + Mischverhältnis→Sorte/kaputte Masse), Klimazone (Bänder Äquator→Pol
mit Beispielpflanzen), Monsun (Monatsregen-Balkendiagramm Mumbai + Wien-Vergleich, Quelle IMD).
Texte: off-season-import (Winter-Erdbeere ~10× CO₂, Schiff vs Flug) + lebensmittelverschwendung
(70 kg = ~6 Wochen Essen; 700 Mio. Hungernde) — greifbare Vergleiche.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 01:42:20 +02:00
Adminator 283780994b Schüler-Ansicht: Menü ohne "Meine" (Module/Erfolge/Schulbuch/Profil), Schulbuch-Links
- Top-Menü: "Meine Module"→"Module", "Meine Erfolge"→"Erfolge", neu "Schulbuch" (→ /buch),
  switchTab auf data-tab umgestellt (robust gegen zusätzliche Menüpunkte)
- Pro Modul-Karte ein "📘 Schulbuch"-Link zum passenden buch-<id>-Kapitel (klima-3d→klima)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 01:34:00 +02:00
Adminator 108053d5da Glossar: Blackout mit echten Beispielen + Strom mit privater Solar-Erzeugung
Blackout: reale Fälle (Iberien 28.4.2025 ~60 Mio./10 h · Indien 2012 ~620 Mio. ·
USA 2003 Baum→Kettenreaktion 50 Mio. · AT 8.1.2021 Beinahe-Netztrennung), Quellen ENTSO-E/APG.
Strom: Erzeugung nicht nur in Kraftwerken, auch privat per Photovoltaik/Balkonkraftwerk
(AT Ende 2024 ~500.000 PV-Anlagen, ~11 % des Strombedarfs; PV Austria/IEA-PVPS).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 01:29:28 +02:00
Adminator 954461c1b9 Buch: 51 neue Fachbegriffe im Text mit dem Glossar verlinkt (Tooltips)
Erste Nennung je Begriff im Standard-Zweig per g() verlinkt (blockabfertigung,
verordnung/richtlinie, deich, eisschild, strommix, kerosin, halbschatten, taiga,
fruchtfolge, synergie … über 10 Kapitel). Kein g() in Leichter Sprache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 23:26:05 +02:00
Adminator f8e77910ea Glossar: 51 neue Einträge komplett — Bilder + 12 interaktive Widgets
Bilder (Flat-Scandinavian, gpt-image-1) für alle 51 neuen Begriffe; 12 Widgets in
neu1.js (strommix, eisschild, klimabilanz, megawattstunde, niederschlag, fruchtfolge)
und neu2.js (halbschatten, blockabfertigung, grundfreiheiten, synergie, aufenthaltsdauer,
grundwasser). image_path + module-Mappings, key_slug-basiert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 23:17:59 +02:00
Adminator f9a5c84ab9 Buch (5 Kapitel): Sek-I-Sprache — heli, sonnensystem, eu-werkstatt, farmer, klima
Schachtelsätze auf, Jargon erklärt (Sperrwerk, Geo-Engineering, Vegetationsperiode,
gebundene Rotation, Glyphosat), jeder Fachbegriff per g() verlinkt. Sammel-Fix:
Boden-See→Bodensee, Formarin-See→Formarinsee (falsche LS-Bindestriche).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 18:26:42 +02:00
Adminator 6e23265dbc Buch (3 Kapitel): Sek-I-Sprache — kofferdetektiv, fluss, busfahrt
Schachtelsätze auf, Jargon erklärt (Kerosin, gesättigt/versiegelt, Lenkzeit/Zollunion),
jeder Fachbegriff per g() verlinkt. Fakten/Quellen/Struktur unverändert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 18:23:59 +02:00
Adminator e06681d522 Buch (4 Kapitel): Sek-I-Sprache — Schachtelsätze auf, Jargon erklärt, Begriffe verlinkt
tourismustal, tourismusregion, weltkueche, energiemanager: Nominalstil/Passiv → Aktiv,
jeder Fachbegriff eingeführt + per g() verlinkt (Achillesferse/Dumping-Preis/Dunkelflaute-
Schachtelsätze etc. entschärft). Fakten/Quellen/Struktur/Fotos unverändert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 18:22:59 +02:00
Adminator c00b535b1f Buch Logistik: Sprache auf Sek-I-Niveau (Alpentransit entjargont, Begriffe verlinkt)
Schachtelsätze aufgelöst, Aktiv statt Nominalstil, jeder Fachbegriff erklärt +
per g() ans Glossar verlinkt. Blockabfertigung/sektorales Fahrverbot/Nachtfahrverbot
kindgerecht erklärt (Thomas' zitiertes Negativbeispiel). Fakten/Quellen/Struktur unverändert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 18:21:35 +02:00
Adminator 237c3c4d82 Buch: Glossar-Tooltips reparieren — GlossarTooltip.init() fehlte in allen 13 Kapiteln
Die Kapitel banden glossar-tooltip.js ein, riefen aber nie init() auf → alle
Fachbegriffe waren unterstrichen, aber ohne funktionierenden Tooltip (tote Links).
Init auf DOMContentLoaded ergänzt (lädt alle Glossar-Begriffe, apiBase=BASE_PATH/php/api).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 18:13:48 +02:00
Adminator 1f9d52bd2b Foto-Backend: Löschfunktion pro Foto (✕-Knopf am Thumbnail, mit Bestätigung)
- POST-Handler aktion=loeschen: entfernt Upload-Datei + Thumbnail-Cache
- Schutz: kein Pfad-Ausbruch, .note.txt nicht löschbar, hinter PIN, PRG-Redirect
- ✕-Overlay-Button auf jedem Thumbnail, JS-confirm vor dem Löschen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 15:53:11 +02:00
Adminator 2e50aaf49f Glossar: 12 neue interaktive Widgets (Tourismus + Geo/Energie/Reise)
tourismus.js: beschneiung (Temp→Schnee), diversifizierung, kapazitaet, saisonalitaet,
uebertourismus, bevoelkerungsdichte
geowelt.js: gletscher, lawinenwarnstufe, lichtgeschwindigkeit, gigawattstunde,
netzspannung, kartenmaßstab
Einbindung in glossar.php (Registry via Object.assign).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 02:36:55 +02:00
Adminator aae0c82c64 Glossar: 132 dünne Einträge angereichert (Fakten, Beispiele, Quellen, kindgerecht)
Ø-Textlänge ~450 → ~994 Zeichen. Je Eintrag: Lebenswelt-Einstieg, echte Zahlen mit
web-verifizierter Quelle, einfache Wörter (kein "ressourcenintensiv"), DIN-Bis-Strich
"5 – 9", Leichte Sprache mit korrekten Bindestrichen. key_slug-basierte UPDATEs
(base64), lokal+Prod per MD5 identisch verifiziert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 02:29:08 +02:00
Adminator 0febe51685 Glossar-Qualität Welle 1: Angebot&Nachfrage neu (Kino-Beispiel) + Markt-Widget, DIN-Striche, Fixes
- angebot-nachfrage: kindgerechter Text (20-€-Taschengeld/Kino), kein Lehrplan-Jargon,
  neues interaktives Markt-Widget (10 Äpfel, Preis-Regler → Gleichgewicht)
- beduerfnisse, raumnutzungskonflikt: Lehrplan-Sätze raus, kindgerechter
- DIN 5008: Zahlenbereiche programmweit als "5 – 9" (71 Einträge)
- Leichte Sprache: falsche Bindestriche gefixt (Infrastruktur, Grundausstattung, …)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 02:17:54 +02:00
Adminator c4276083a2 Glossar visuell komplett: 162 Bilder, 13 Kapitel-Icons, 12 interaktive Widgets
- 162 neue Glossar-Bilder (Flat-Scandinavian, gpt-image-1) → alle 402 Einträge bebildert;
  image_path-Update: App/Don_t_Deploy/2026-07-16-glossar-bilder.sql
- 13 Kapitel-Icons (icon-<id>.webp) ersetzen die Emojis in Buch-Übersicht + Kapitel-h1
  (buchKapIcon-Helper mit Emoji-Fallback)
- 12 interaktive Glossar-Experimente in 2 Modulen:
  natur (Mondrotation, Kernschatten, Keeling-Kurve, Pegel/HQ100, Wärmeausdehnung,
  Klimaträgheit) + wirtschaft (Preiselastizität, Engpass, Skaleneffekt, Modal Split,
  Netzfrequenz/Hertz, Lageenergie/Pumpspeicher); Registry via Object.assign,
  Script-Einbindung in glossar.php

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 00:59:30 +02:00
Adminator 78a1cdca24 Buch ↔ Glossar verbunden: 80 neue Glossar-Einträge, ~390 Tooltips, Rückverlinkung
- 13 Kapitel: alle Fachbegriffe per g()-Tooltip verlinkt, fehlende Begriffe als
  vollwertige Glossar-Einträge angelegt (short/text + Leichte-Sprache-Felder,
  glossar:-Querverweise, ohne Bild — Bild-Batch folgt)
- Modul-Mappings vervollständigt: jeder Buch-Begriff erscheint auf glossar?module=<sim>
  (kofferdetektiv erstmals gemappt, 31 Begriffe; klima auf beide module_ids)
- Ergänzende Erklärsätze im Buch, wo Begriffe bisher nur genannt wurden
- glossar.php: Chip „📖 Zum Schulbuch-Kapitel" bei aktivem Modul-Filter (BUCH_LINK-Map,
  klima-3d → buch-klima)
- SQL idempotent (INSERT IGNORE, Mappings per key_slug-Subquery, keine harten IDs):
  App/Don_t_Deploy/2026-07-15-buch-glossar-*.sql

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:40:56 +02:00
Adminator f91f3f26de Foto-Backend: Upload-Thumbnails + Overlay-Lightbox
- Hochgeladene Fotos erscheinen als Thumbnail-Raster beim Slot (110px, GD-generiert
  mit EXIF-Orientierung, gecacht in uploads/buch-fotos/.thumbs)
- Klick öffnet Overlay in groß (Esc/Klick/✕ schließt); auch eingebaute Slot-Fotos klickbar
- Auslieferung nur über die Seite hinter der PIN (?img=/?thumb=); Ordner bleibt gesperrt
- Notizen werden als 📝-Zeile beim Slot angezeigt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:14:13 +02:00
Adminator a3d81b8983 Foto-Backend: PIN-Schranke 0815 (Cookie 1 Jahr, im Browser gespeichert)
Uploads und Slot-Liste nur nach PIN-Eingabe; falsche PIN mit Meldung,
POST-Uploads ohne Cookie werden ebenfalls von der Schranke abgefangen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 16:48:03 +02:00
Adminator 4e3ff6447f Foto-Backend /foto-upload: alle Schulbuch-Foto-Slots mit Upload-Formular
- Parst buchBild()-Slots live aus allen buch-*.php (92 Slots, bleibt automatisch synchron)
- Pro Slot: Beschreibung, Status (eingebaut/wartend/gesucht), Thumbnail, Multi-Upload
  (JPG/PNG/HEIC/WebP, max 40 MB) + Notizfeld; Sonstiges-Slot für freie Fotos
- Uploads nach uploads/buch-fotos/<sim>/, per .htaccess gesperrt (Originale mit GPS
  nie öffentlich); PRG-Redirect, Dateityp-/Größen-Validierung
- App/uploads/ in .gitignore

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 13:59:54 +02:00
Adminator f282c79a25 Schulbuch komplett: 13 Kapitel, je 3 Versionen (Schüler / Leichte Sprache / Lehrperson)
- Gemeinsame Infrastruktur: assets/css/buch.css + pages/_partials/buch_helpers.php
  (Foto-Slots, Glossar-Tooltips, Nav mit 3-Modi-Umschalter ?leicht=1/?lehrer=1)
- Neu: Leichte-Sprache-Version in jedem Kapitel (inklusiver Unterricht):
  kurze Sätze, eine Aussage pro Zeile, Bindestrich-Komposita, einspaltig, größere Schrift
- 12 neue Kapitel: tourismusregion, kofferdetektiv (Themenfeld Reise/Transport/
  Flugverkehr/Nachhaltigkeit), busfahrt, logistik (Alpentransit), weltkueche, farmer,
  klima (2D+3D), fluss (Alpenrhein/Rhesi), energiemanager (Illwerke), heli, sonnensystem,
  eu-werkstatt — alle mit verifizierten Quellen, echten Sim-Mechanik-Kästen,
  D-A-CH-Lehrplan-Chips und 7 Foto-Slots mit konkreten Vorschlägen
- Pilot tourismustal: geschichte-1948 halb so groß (b-klein), Spielsprache-Fixes
- buch.php: 13 Kapitel, Status automatisch aus Dateiexistenz

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 17:12:19 +02:00
Adminator e9870843e8 Schulbuch Tourismustal: 6 echte Fotos eingebunden (GPS-verortet, EXIF gestrippt)
- Opener: Warth am Arlberg (Dez 2025); neuer Slot geschichte-1948 (Dornbirn 1948, Privatarchiv)
- Standort: Sommer-Sessellift (Juni 2017); Wirtschaft: Seiser Alm Hüttenterrasse (Okt 2025)
- Klimawandel: aperer Hang Bödele (Feb 2020); Zielkonflikt: Lünersee-Stausee (Mai 2016)
- Bildunterschriften mit echten Orten/Daten aus EXIF-GPS; Web-Exporte ohne EXIF/GPS
- Sommerloch-Slot bleibt offen (Motiv fehlt noch); VERWENDET.md in .humanInput (lokal)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 01:32:38 +02:00
Adminator e7e0f922c7 Tourismustal: Kosten-Check - Realitaets-Quiz mit Bonus-Budget
Kostenwahrheit als Lernmoment statt Fussnote: Nach dem Erst-Bau von
Lift/Gondel/Hotel/Beschneiung/Bushaltestelle erscheint (Event-Pacing)
je einmalig ein Kosten-Check: Info mit echtem AT-Beispiel + Schaetzfrage
(mehr/weniger). Richtig = +2.000 Euro Bank-Bonus, falsch = nur die
Aufloesung. Verifizierte Zahlen: Leppleskofel-6er 12 Mio. (Dolomiten-
stadt), Patscherkofelbahn 58 geplant -> 83,2 Mio. bezahlt (Stadt Ibk),
WKO-Hotelrichtwerte 220-270k/Zimmer, Schneekanone 27-35k + 1 Mio./
Pistenkm, Ski Arlberg Tageskarte 81,50. Erster Check traegt den
Modell-Hinweis (Preise verkleinert, Verhaeltnisse stimmen).
Antwortrichtungen gemischt (3x mehr, 2x weniger). Quiz-Karten sind
Entscheidungen -> immer modal, auch im Audio-Modus.
Schulbuch Seite 2: Daten-Box 'Was kostet das wirklich?' + Meta-Aufgabe D
(Modellkompetenz) + Quellen 8-12. Modulseite: Vereinfachungs-Hinweis
in long_desc (SQL lokal+prod).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 01:14:55 +02:00
Adminator dbfb5be5a0 Schulbuch/Tourismustal: interner Pfad raus + oesterreichische Rechenzeichen
- Schulbuch-Quellenverzeichnis: interner Dateipfad (QUELLEN.md) entfernt -
  gehoert nicht in ein Schuelerdokument.
- Tagesbericht: Malpunkt (Mittelpunkt) statt x als Multiplikationszeichen -
  oesterreichische Schulschreibweise (neue Dauerregel: +, -, Malpunkt,
  : fuer Division in allen schueler-sichtbaren Rechnungen).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:40:28 +02:00
Adminator 31f1744730 Schulbuch: Scrollbar-Fix (design-system.css sperrt body-Scroll)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:23:37 +02:00
Adminator 7d4e97c0bc Schulbuch: Konzept + Pilot-Kapitel Tourismustal (2 Versionen)
Neues Format /buch: pro Sim ein Schulbuch-Kapitel, lehrplankonform,
druckbar (A4), mit Glossar-Tooltips, echten Daten aus QUELLEN.md und
Sim-Mechanik-Kaesten. Zwei Versionen aus einer Quelle: Schueler:innen
(Sachtext + Mechanik-Referenz + gestufte Aufgaben A/B/C) und Lehrperson
(?lehrer=1: Verlaufsvorschlag Doppelstunde, didaktische Hinweise,
Stolpersteine, Erwartungshorizonte, Cockpit-Tipps). Foto-Slots mit
konkreten Vorschlaegen: echtes Bild unter assets/img/buch/<sim>/<slug>.jpg
ablegen ersetzt den Platzhalter automatisch. Pilot: Kapitel Tourismustal
(Opener + 5 Seiten + Quellenverzeichnis).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:03:46 +02:00
Adminator 3d85b7a13c Tourismusregion: Altersangabe 9-15 (einfacher Einstieg)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 16:56:12 +02:00
Adminator cf0afc5c02 Tourismusregion: Karten-Editor reaktiviert (mit Lehrer-Guard)
editor.html + js/editor.js aus der Quelle uebernommen. save_map.php
schreibt js/maps-custom.json (mit Auto-Backups) - jetzt abgesichert:
nur eingeloggte Lehrpersonen duerfen speichern. Das Spiel laedt die
Overrides beim Start (applyMapOverrides). Thomas fixt damit Routen-
Punkte und Bauplaetze; die Datei wird danach ins Repo uebernommen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 16:55:36 +02:00
Adminator 6ca643f628 Tourismustal: iPad-Runde 3 - Performance, Touch-Bauvorschau, breite Overlays
- Performance ohne Sichtverlust: Retina-Faktor auf 1.5 gecappt (~45 %
  weniger GPU-Pixel), Framerate auf ~30 fps (Pause ohne Interaktion
  ~10 fps) - Simulation exakt, uebersprungene Zeit wird akkumuliert.
  iPad sollte deutlich kuehler bleiben.
- Touch-Bauvorschau: erster Tipp heftet die volle Vorschau (gruen/rot,
  Pisten-/Strecken-Korridor, Einflusskreise) an die Kachel, zweiter Tipp
  auf DIESELBE Kachel baut. Maus behaelt Hover + Sofortbau.
- Info-Overlays breiter (680px statt 440px) - weniger Scrollen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 16:55:35 +02:00
Adminator d60bc2caff Tourismusregion: Hintergrundmusik (15 Tracks, 43 MB)
Getrennter Commit wegen Push-Groesse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:14:13 +02:00
Adminator 8047778a5b Tourismusregion: Komplett-Integration (zweite neue Sim, beta)
Wellenbasierte Routen-Sim (DefensegeograSimMac) nach dem Tourismustal-
Playbook integriert: Sim nach App/sims/tourismusregion (game.html,
Sprites 47MB->9.5MB, ohne Eigen-Backend/Editor/Dashboard/Keys),
Plattform-Hooks (GGS_LIVE_STATE mit Welle/Budget/Umwelt/Sterne/Lenkung,
Submit bei Partie-Ende mit fertigem 0-100-Score, GGS_TUTORIAL, Home-
Button, Musik leise). Wrapper + Modul-Detailseite. SQL: module_info
(beta, Karte), Glossar-Kernbegriff Besucherlenkung (+Bild, Leichte
Sprache) + 12 Zuordnungen, Lehrplan-Mapping auf tourismus-
raumentwicklung (primaer) + verkehrserziehung/systemisches-denken.
LEHRPLAN.md mit Schwerpunkt Besucherlenkung & Mobilitaet. Lehrer-
Backend: Benchmark (Sim-Score direkt), Live-Cockpit-Spalten,
needsHelp, Highlights.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:14:12 +02:00
Adminator 778634fab6 Tourismustal: Editor-Export final - alle Gebaeude justiert
Letzte Runde: Panoramaweg, Bikepark-Station, Hochseilgarten,
Fahrradverleih (+Mega), Aussichtsplattform, Spielplatz, Mega-Spielplatz
(verkleinert auf 90/20) + 36 Solar-/Muelleimer-Positionen pro Stufe
(inkl. Rodelbahn). Komplette Sprite-Justierung damit fest im Code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:59:19 +02:00
Adminator 41ecb4d5fe Tourismustal: Oeko-Marker Hochseilgarten ebenfalls Muelleimer
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:50:32 +02:00
Adminator ecc70a333d Tourismustal: Oeko-Marker Panoramaweg = Muelleimer statt Solarzelle
Ein Weg hat kein Dach - der gepflegte Muelleimer am Wegrand (rechte
Ecke) erzaehlt sanften Tourismus besser. Position weiter ueber den
Editor-Solar-Modus justierbar (gleiches SOLAR_POS-System).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:48:31 +02:00
Adminator acde652ee7 Tourismustal: Editor-Export Runde 3 (Badesee)
Badesee 47/22 + Solar-Positionen L1-L4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:31:47 +02:00
Adminator 24ad893dd5 Tourismustal: Editor-Export Runde 2 + Mega-Wachstum vom Zentrum
- Justier-Werte bis Badesee: Personalhaus (47/17), Mega-Personalhaus
  (101/32), Mega-Badesee (84/31) + 12 Solar-Positionen pro Stufe.
- Mega-Stufen-Skalierung waechst jetzt vom MOTIV-ZENTRUM aus statt vom
  Boden-Anker (centerScale in _sprite) - die Mitte bleibt fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:30:25 +02:00
Adminator cff7c11853 Tourismustal: Editor-Export Runde 1 fest eingebacken
Thomas' Justier-Werte (2026-07-12) in SPRITE_DEFS + SOLAR_POS uebernommen:
Gasthaus/Rettungsstation/Bushaltestelle-Anker korrigiert, Mega-Hotel/
-Gasthaus/-Schneizentrale/-Spielplatz neu gesetzt, 40+ Solar-Positionen
pro Gebaeude UND Ausbaustufe (inkl. Lift/Gondel/Bus). Lift/Gondel-
Sprite-Werte bewusst ausgelassen (Vektor-Zeichnungen, wirkungslos).
Gilt jetzt fuer ALLE Geraete, nicht nur das Justier-iPad.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:25:13 +02:00
Adminator 6d00a762bc Tourismustal: Megabauwerke wachsen mit der Ausbaustufe
Mega-Sprites skalieren jetzt je Stufe: I=90%, II=100%, III=112%,
IV=124% (vorher fix 100%). Gilt fuer alle Mega-Typen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:13:38 +02:00
Adminator b90abb31a4 Tourismustal: Mega nur fuer sinnvolle Gebaeude
Panoramaweg aus der Merge-Liste entfernt (Linien-Bauwerk). MERGEABLE
exportiert; Editor sperrt die Mega-Checkbox fuer alle nicht merge-
faehigen Typen (Bahnen, Bus, Bikepark, Rodelbahn, Panoramaweg,
Rettungsstation, Aufforsten, Plattform).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:11:03 +02:00
Adminator 20974b8b3b Tourismustal: Soziale Verantwortung (Personalhaus) + Editor-Fixes
- Personalhaus-Ausbau = Wohnqualitaet: zufriedenes Personal, freundlicher
  Service, +2/+4/+6 Gaeste-Zufriedenheit je Stufe (nur bei Beschaeftigten).
  Report-Zeile, Wissensbaustein (reale Saisonkraft-Unterkuenfte),
  BUILD_INFO + Audio neu, spezieller Ausbau-Toast. Glossar-Begriff
  soziale-verantwortung mit Bild + Modul-Zuordnung (SQL).
- Editor-Bugfix: _sprite las w/bottom aus der beim Laden eingefrorenen
  Kopie - Justierungen wirkten nicht. Jetzt live aus SPRITE_DEFS.
- Export-Button klargestellt: exportiert ALLE justierten Gebaeude auf einmal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:48:47 +02:00
Adminator 250532f9a8 Tourismustal: Editor v2 (Ziehen statt Slider, Solar pro Stufe) + Bikepark-Vorschau
- Editor: Zieh-Modi direkt auf der Karte (Gebaeude: horizontal=Breite,
  vertikal=Boden-Anker; Solar: frei ziehen) + Pixel-Feinjustage per
  Plus/Minus-Tasten, grosse Live-Wertanzeige, Footprint-Hilfslinien.
- Solar-Position wird PRO AUSBAUSTUFE gespeichert (<key>@L<stufe>,
  Vererbung Stufe -> Sprite -> Default) - Renderer-Lookup erweitert.
- Bikepark zeigt beim Bauen die komplette Strecke talwaerts (5 Felder,
  pro Kachel gruen/rot je nach Belegbarkeit) - wie die Lift-Piste.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:43:32 +02:00
Adminator 272c5a7169 Tourismustal: Biker fahren richtig herum (Flip statt Vollrotation)
Bildschirm-Winkel bergab ist ~140 Grad - volle Rotation stellte die
Fahrer auf den Kopf bzw. aufs Vorderrad. Jetzt: Spiegelung nach
Fahrtrichtung + leichte Bergab-Neigung (Faktor 0.3).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:38:43 +02:00
Adminator cfdfa4bb28 Tourismustal: Sprite-Editor, Bikepark-Umbau, Animationen, Ambiente, Baumenue-Layout
- Justier-Editor (editor.html): jedes Gebaeude in allen Stufen, 1x1 und
  Mega-2x2, Oeko an/aus - Sprite-Breite, Boden-Anker und Solarpanel-
  Position (dx/dy/s pro Sprite-Key) live auf echten Tiles tunen. Werte
  wirken sofort via localStorage-Override, Export-JSON zum Einbacken.
- Bikepark umgebaut aufs Rodelbahn-Prinzip: 1x1-Station, Flowtrail-
  Strecke 5 Felder talwaerts mit Schwuengen, Platz wird SCHON BEIM BAU
  geprueft und reserviert (tile.bikeTrail blockiert Bebauung), fahrende
  Raeder mit Kurvenneigung (mehr Biker je Ausbaustufe), Ziel-Bogen.
- Animationen: Spielplatz (Schaukel + Rutscher), Hochseilgarten
  (Zipline-Gleiter), Aussichtsplattform (Foto-Blitz), Fahrradverleih
  (drehendes Werkstatt-Rad), Gasthaus (Kamin-Dampf).
- Ambiente: Wolkenschatten ziehen uebers Tal; Schafe auf Almen und
  Kuehe auf Talwiesen (grasen, deterministische Herdenplaetze, nicht
  im Winter).
- Baumenue-Karten: oben Titel+Info links / Preis rechts, darunter
  zweispaltig Icon und Beschreibung.
- Solar-Position pro Gebaeudetyp justierbar (SOLAR_POS).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:32:55 +02:00
Adminator d195b085d9 Tourismustal: Naturwert aktiv zurueckgewinnen (Oeko-Umbau + Aufforsten)
Antwort auf die Design-Frage 'Naturwert zurueck ausser weniger bauen':
- Oeko-Umbau pro Gebaeude (Button im Gebaeude-Dialog): einmalig, 40 % des
  Bauwerts, dafuer -20 % Betriebskosten, +2 Naturwert, Gebaeude belastet
  die taegliche Natur-Regeneration nicht mehr. Sichtbar: Solardach-Overlay
  (auch auf Megabauwerken, 1,6x). Didaktik: Investition vs. laufende
  Kosten, nachhaltiges Wirtschaften (GW 4.10).
- Aufforsten als wiederholbare Aktion im Baumenue (1.500 Euro): pflanzt
  Wald auf freie Wiese/Alm/Feld, +3 Naturwert, Werkzeug bleibt aktiv fuer
  Serien-Pflanzung. Wissen: Renaturierung + Schutzwald (1/3 des
  oesterreichischen Walds).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 18:16:40 +02:00
Adminator 328d542fdc Tourismustal: 2x2-Gebaeude korrekt im Painter (vorderste Kachel zeichnet)
Grosse Gebaeude wurden von davorliegenden Kacheln uebermalt (Grasbueschel
ueber dem Hotelfuss). Jetzt zeichnet die VORDERSTE Footprint-Kachel das
Sprite, Terrain-Details entfallen auf bebauten Kacheln, Sprite-Mitte =
Mitte der 4 Kacheln, w/bottom so dass der Fuss die vordere Spitze deckt.
SQL-Migration audio_info MySQL-kompatibel (information_schema-Guard statt
MariaDB IF NOT EXISTS - Prod ist echtes MySQL).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 18:10:46 +02:00
Adminator 55a83d5daf Tourismustal: iPad-Feedback Runde 2 (5 Punkte)
- Bau-Ablehnung erklaert jetzt immer auch die Bauregeln des Gebaeudes
  (Toast: Grund + Beschreibung, 7 s).
- Lehrer-Schalter fuer den Vorlese-Modus: class_modules.audio_info
  (Migration, Default AN) + modules-API set_audio_info + Toggle auf der
  Modul-Karte im Cockpit (Vorlesen an/aus) + Wrapper injiziert
  TT_FEATURES.audioInfo + Sim blendet den Umschalter aus und erzwingt
  Lese-Modus, wenn gesperrt.
- Bus zeigt bei Rueckfahrt sein Heck: Heckscheibe + rote Ruecklichter,
  Tuer/Spiegel richtungsabhaengig.
- Bikepark-Flowtrails: 3 Stufen hangAUFwaerts (gleiche Richtung wie
  Lift-Pisten).
- Info-Button an jedem Bauwerk im Baumenue: erklaert Wirkung, Kosten,
  Lage-Regeln und Lehrplan-Wissen VOR dem Bau.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 18:07:03 +02:00
Adminator cb60a8c16d Tourismustal: Aussichtsplattform-Baubedingungen entknotet
Vorher drei verschraenkte Regeln (Hoehe >=6 + Talstation <=4 + Hochlagen)
-> fast nirgends baubar. Jetzt: Hoehe >=5 UND Naehe zu Tal- ODER
BERGstation (oberes Pistenende) einer Bahn - thematisch korrekt, die
Bahn bringt die Ausflugsgaeste ja hinauf. 52 baubare Felder um zwei
Bahnen statt Handvoll. Klare Fehlermeldungen je Bedingung.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 17:59:12 +02:00
Adminator 32793d0bca Tourismustal: Vorproduzierte Sprach-Audios statt Browser-TTS
52 MP3s (OpenAI gpt-4o-mini-tts, Stimme coral, warme deutsche
Erzaehlstimme) fuer alle Info-Karten: Erst-Bau-Erklaerungen, Meilensteine,
Events, Szenen-Intros, Auftraege, Megabauwerk. Texte automatisch aus
data.js gezogen (Generator im Scratchpad-Muster). Abspiel-Logik:
assets/audio-info/<id>.mp3, Browser-TTS nur Not-Fallback. Intro-IDs je
Szene eindeutig, Onboarding bleibt immer modal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 12:47:46 +02:00
Adminator 7e3158aede Tourismustal: Audio-Modus fuer Meldungen + eigene Mega-Motive
- Umschalter Topbar (Lese-/Audio-Modus, persistiert): Im Audio-Modus
  laeuft das Spiel weiter, Meldungen werden per Browser-TTS (deutsche
  Stimme) vorgelesen und erscheinen als antippbare Info-Zeile oben;
  Klick oder 🔔-Log oeffnet die volle Karte. Entscheidungen (A/B) und
  wichtige Karten (Sieg, Zwischenbilanz, Bank) bleiben immer modal.
- Megabauwerke: 9 eigene Sprites (mehr Elemente statt vergroesserter
  Fenster/Tueren - glaubwuerdige Proportionen), zentriert, ~80 % der
  bisherigen Darstellungsgroesse; Fallback normales Motiv x1,4.
  Rodelbahn aus der Merge-Liste (dynamische Vektor-Bahn).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 11:05:38 +02:00
Adminator 2591f37191 Tourismustal: Megabauwerke + Figuren-Feinschliff
- Merge-Mechanik: 4 gleiche 1x1-Gebaeude im Quadrat verschmelzen zum
  2x2-Megabauwerk. Skaleneffekt als Lernmoment: 4-fache Leistung, nur
  3-fache Betriebskosten (Erklaerkarte beim ersten Mal). Level = Maximum
  der Teile, Abriss-Erloes auf 4-fachem Bauwert, Bahnen/Bus/Bikepark
  ausgenommen. Rendering zentriert und 1.75x skaliert.
- Gaeste stehen nicht mehr im Wasser (dwell auf Wasser -> sofort ans Ufer).
- Skifahrer schimpft "#*!", wenn ihm der Schnee unterm Ski ausgeht.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 10:43:06 +02:00
Adminator 727e82c8fe Tourismustal: 14 Glossar-Illustrationen + Musik-Standard leiser
Alle 14 neuen Glossar-Begriffe bebildert (gpt-image-1, Flat-Scandinavian-
Stil, 1600x914 webp wie Bestand) + image_path-SQL. Hintergrundmusik-
Standardlautstaerke auf 1/4 (0.09statt 0.35), gespeicherte Nutzerwerte
bleiben unangetastet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 09:40:06 +02:00
Adminator 16c562ed1d Tourismustal: iPad-Test-Feedback Runde 1
- Bikepark: echtes 2x2-Footprint (canPlace/place/demolish/upgrade/Report
  ankerbasiert, buildingRef auf Teilflaechen), Sprite ohne Schatten neu
  generiert, zentriert gross gezeichnet. Baukriterium gelockert: Bahn-Naehe
  ersetzt die Zonen-Regel (wie Hotel im Bahn-Umkreis).
- Zeit halbiert: 1 Tag = 4 s bei 1x (Absturz-Rate entsprechend angepasst).
- Problem-Sprechblasen alle ~3 min am Gebaeude (Personal gesucht / kein
  Schnee), erster Hinweis nach 15-35 s.
- Heli: sichtbares Abheben/Landen am Pad (aus vorigem Commit) bleibt.
- Test-Harnesse (model-test, strategien) wandern in App/sims/tourismustal/tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 09:20:06 +02:00
Adminator 2b4a92e614 Tourismustal: Heli hebt sichtbar ab und landet am Pad
Senkrechter Start/Landung mit geglaetteter Hoehenkurve, Rotor laeuft
an/aus (spin-basiert statt Echtzeit), Rotor-Abwind-Wirbel am Boden,
Schatten skaliert mit Flughoehe. Kein Teleport-Erscheinen mehr.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 09:13:09 +02:00
Adminator 7453bebbc3 Tourismustal: Hintergrundmusik (16 Suno-Tracks, 48 MB)
Getrennter Commit wegen Push-Groesse (Gitea, stueckweise pushen).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 02:45:09 +02:00
Adminator 0a2ebf46b4 Tourismustal: Komplett-Integration (Sim + Wrapper + SQL + Lehrer-Backend)
Sim nach App/sims/tourismustal umgezogen (Source of Truth), game.html mit
Injection-Marker. Neu in der Sim: 5 Sommergebaeude (Bikepark mit Flowtrails,
Hochseilgarten, Fahrradverleih als Verstaerker, Aussichtsplattform,
Erlebnisspielplatz), Rettungsstation mit Notarzthubschrauber, Ausbau-
Bauanimation, Event-Pacing, Schulden-Notbremse (Bank-Zwangsverkauf),
Berater-Tipps (Ruecklagen), Sommer-Trend-Mechanik (Serfaus-Effekt) und
Wissens-Bausteine mit echten DACH-Tourismusdaten (QUELLEN.md).

Plattform: PHP-Wrapper mit base-Tag + Session-Mode, Modul-Detailseite,
module_info (beta) + Glossar (14 Begriffe inkl. Leichter Sprache + Quellen)
+ Lehrplan-Anker AT/DE/CH an neuer Kompetenz tourismus-raumentwicklung.
Lehrer-Backend: Benchmark-Formel, Live-Cockpit-Spalten, needsHelp-Heuristik,
Modul-Highlights. Strategie-Testharness (tests/strategien.mjs) belegt:
Panzer-Rush wird liquidiert, Ruecklagen zahlen sich aus.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 02:45:08 +02:00
Adminator eb565d4c01 Tourismustal: Modul-Karte (Flat-Scandinavian, 16:10 webp)
Erste Grafik der Sim-Integration. Generiert mit gpt-image-1 im
verbindlichen GeoGraSim-Bildstil, auf Karten-Verhaeltnis 16:10
zugeschnitten und als webp (69 KB) abgelegt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 03:04:47 +02:00
Adminator 1e51ef7def Nachtrag: alle bisher untracked Ordner + hängende Änderungen mit-committen
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/
- 12 code-workspace-Files
- STATUS-*.md
- viele M/D/R-Änderungen an bereits getrackten Files
- .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-08 02:27:02 +02:00
Adminator 027831a89c Lehrplan: Speicher-Snapshot — 5 Modul-Wrapper + Status
Vor Abschalten der Lehrplan-Instanz: 5 Modul-Detail-Wrapper, die
bisher untracked waren, werden gespeichert. Alle 3-Zeilen-Wrapper
auf den gemeinsamen Partial _partials/modul_detail.php:
- modul-busfahrt.php
- modul-energiemanager.php
- modul-eu-werkstatt.php
- modul-farmer.php
- modul-sonnensystem.php

_status.md mit vollem Crash-Recovery-Snapshot:
- 16 Module (11 aktiv, 4 geplant, 1 archiv)
- 14 Kompetenzen, 39 kompetenz_modules
- 144 Lehrplan-Anker (76 AT, 28 DE, 24 CH/LI)
- 130 Glossar-Begriffe (10 astronomie + 12 EU + 108 andere)
- Alle 11 aktiven Sims haben Lehrplan-Bezug
- Letzte 6 Commits dokumentiert

Inbox: keine offenen Nachrichten an lehrplan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 13:23:40 +02:00
Adminator 1659b439a9 Klima: Glossar-DB-Set auf 18 neue Slugs erweitert + Status-Update
- GLOSSAR_DB_KNOWN in 2D + 3D um 4 Aliasse (windpark, solaranlage,
  erneuerbar, kohlekraftwerk) und 14 Volleinträge (budget, strom,
  mangrove, klimafolgen, kuestenschutz, wartung, tourismus, blackout,
  steuern, temperatur, meeresspiegel, bevoelkerung, ueberflutung,
  co2_filter) ergänzt — Logistik-Migration vom 02.05. eingespielt.
- KLIMA_TO_DB_KEY-Mapping bleibt als Robustheits-Schicht erhalten.
- Logistik-Inbox 02.05. 22:40 als gelesen markiert.
- _status.md spiegelt jetzt den tatsächlichen Stand wider.
2026-05-07 01:34:16 +02:00
Adminator 2c3ca76be5 Klima: CRLF-Normalisierung Inbox-Datei 2026-05-07 01:33:58 +02:00
Adminator 9ada666ff6 Klima: Status-Update vor Abschaltung
Live-State-Hook für 3D ist heute committed (e354a30). Status-Datei
überschrieben mit aktueller Bestandsaufnahme + Backlog (B-F).
Glossar-DB-Inbox-Nachricht von Logistik (02.05.) jetzt mit ge-trackt.

Klima-Instanz darf abgeschaltet werden.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 01:33:29 +02:00
Adminator 33fb423967 Atlas: Live-View-Feature + Submit-Bug-Fixes + Admin-Styleguide
- Neuer API-Endpoint /api/live (heartbeat / spectator) + DB-Tabelle
  live_sessions + class_modules.paused-Spalte
- Plattform-Live-Client (assets/js/live-client.js): Heartbeat,
  Pause-Overlay, Spectator-Mode bei ?view=teacher
- ggs_inject_live() in _scripts.php als Helper, in alle 11 Sim-Wrapper
  integriert
- Lehrer-Cockpit: tabellarische Klassen-Live-Ansicht pro Sim,
  Pause-Toggle, blinkender Tab-Indikator wenn aktive Sessions
- Submit-Bug erschlagen: progress.php hat jetzt submit_assessment-
  und reflection-Action; saves.php akzeptiert beide Key-Konventionen;
  alle Direkt-Pfad-API-Files mit require_once-Bootstrap
- Avatar-Default: zufaelliger DALL-E-Avatar fuer neue Schueler:innen
- Admin-Styleguide-Seite mit Bildstil, Farb-Tokens, Layout, iPad-Pattern
- Klima 2D + 3D: GGS_LIVE_STATE-Hook, Klima 2D zusaetzlich skipResume
  fuer Klassenaufgaben-Reset
- Test-Lehrer Jakob + 3 Schueler:innen lokal + auf Live angelegt
- Rundbriefe an alle Sim-Instanzen + Submit-Briefe an sonnensystem,
  busfahrt, energiemanager + Glossar-Anfrage zu Grundriess
- Status-Uebergabe in _inbox/zentrale/_status.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 01:20:53 +02:00
Adminator e354a30a44 Klima 3D: Live-State-Hook für Lehrer-Spectator
Atlas-Auftrag aus _inbox/klima/2026-05-05-0140 — analog zum 2D-Hook,
plus 3D-spezifische Felder.

- game-3d.html: window.GGS_LIVE_STATE liefert {variant:'3d', tick, year,
  level, phase, budget, population, co2, temp, floodedPct, seaLevelCm,
  speed, measures, levelWon, tourism, hasShield, hasBeach, placing}.
  Aufruf alle ~4s durch live-client.js, gepostet an /api/live.
- klima-3d.php: Live-Client-Script-Tag nachgezogen (war nur in
  klima-2d.php). Wird wie üblich vor </body> injiziert.

Heartbeat → DB live_sessions, Lehrer-Cockpit zeigt Karten an mit
Live-Kennzahlen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 03:42:58 +02:00
Adminator 2378075915 Lehrplan: Simulationen-Liste filtert auf aktive Sims
Auf der Lehrplan-Seite wurden in den "Simulationen zu diesem Bereich"-
Cards auch geplante Module (Energiemix, Erdbeben, Lieferketten) und
das archivierte Staustufen gelistet. Stimmt nicht mit den
existierenden Sims ueberein.

modStmt-SQL erweitert um Status-Filter:
  AND mi.status IN ('aktiv','beta')

Verifiziert: Lehrplan-Seite zeigt jetzt 11 Module statt 15.
Sichtbar bleiben alle aktiven Sims (Busfahrt, EU-Werkstatt,
Energiemanager, Fluss, Heli, Klima, Klima-3D, Landwirtschaft,
Logistik, Sonnensystem, Stadt). Geplant + archiv sind raus.

Anker-Anzeige bleibt unveraendert: alle 144 Lehrplan-Anker bleiben
sichtbar, weil sie an Kompetenzen haengen, nicht an Modulen.
Auch wenn ein Modul geplant/archiv ist, bleibt der didaktische
Lehrplan-Bezug seiner Kompetenz wertvoll.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 02:23:48 +02:00
Adminator 95e19818fd Lehrplan: Country-Default überall AT, übergreifend land-agnostisch
Auftrag von Thomas: Default-Filter überall auf Österreich, andere
Länder weiterhin wählbar.

Modul-Detailseiten (gemeinsamer Partial):
- Filter-Chip mit data-country=Country::current() bekommt Klasse
  active server-seitig (statt "🌐 Alle Länder")
- Country.php in Partial eingebunden
- JS-Filter-Logik überarbeitet: bei konkretem Land werden NUR
  anchor_type='fach' Anker streng nach Land gefiltert; übergreifende
  Themen und andere Fächer bleiben sichtbar (sind primär aus dem
  AT-Lehrplan abgeleitet, didaktisch land-agnostisch nutzbar)
- applyFilter() wird beim Page-Load mit dem aktiven Chip ausgeführt

Lehrplan.php:
- gleiche Filter-Logik gespiegelt (war vorher streng auch für
  übergreifend/andere_fach)
- Default war schon Country::current(), bleibt unverändert

Verifiziert: alle 10 aktiven modul-*.php-Seiten zeigen AT als
Default-active, Lehrplan-Seite ebenfalls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 02:16:04 +02:00
Adminator 306ec97cb5 Lehrplan: Farmer/Busfahrt/Energiemanager + Heli-Vertiefung
Vier Module ohne (oder mit dünnem) Lehrplan-Bezug erschlossen.

Neue Kompetenz "landwirtschaft-klimazonen" (Köppen-Geiger,
FAO-Eignungsklassen, Sek I 10-14, Kategorie natur).

13 neue kompetenz_modules:
- energiemanager: energieversorgung-speicherung primär + klima sek.
- busfahrt: raeumliche-orientierung-europa primär,
  kartenkompetenz-logistik + wirtschaft sek.
- farmer: landwirtschaft-klimazonen primär, klima + erdmechanik
  + wirtschaft sek.
- heli (Vertiefung): naturgefahren-hochwasser + verkehrserziehung-
  sicherheit + systemisches-denken alle sekundär

20 neue Anker:
- 3 für Energiemanager (Lastausgleich, Physik Leistung/Arbeit,
  Mobilität+Energie)
- 4 für Busfahrt (Hauptstädte/Staaten, Maßstab/Distanzen, Mathe,
  Interkulturelle Bildung)
- 8 für Farmer (3 Fach GW 1.8/3.6/4.10, 2 übergreifend Umwelt+Wirt,
  3 andere Fächer Bio/Chemie/Mathe)
- 5 für Heli (1 Fach Bergrettung/Naturgefahren, 2 übergreifend
  Gesundheits- + Mobilitätsbildung, 2 andere Fächer
  Bewegung+Sport / Physik Aerodynamik)

Render nach Seed:
- heli: 4 Kompetenzen, 50 Anker (vorher 1/18)
- farmer: 4 Kompetenzen, 43 Anker (vorher 0/0)
- busfahrt: 3 Kompetenzen, 16 Anker (vorher 0/0)
- energiemanager: 2 Kompetenzen, 33 Anker (vorher 0/0)

DB-Bilanz: kompetenzen 14, kompetenz_modules 39,
lehrplan_anchors 144 (vorher 12/27/124)

Quellen:
- _inbox/farmer/2026-05-03-0040-konzept-vorschlag.md
- _inbox/busfahrt/2026-05-02-2355-kickoff-briefing.md
- _inbox/energiemanager/_status.md
- BMBWF GW-Lehrplan Sek I 2023

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 01:45:36 +02:00
Adminator d9fc30103f Lehrplan: EU-Werkstatt — Glossar zentralisiert + 8 AT-Anker
Thomas: das modul-interne Glossar der EU-Werkstatt soll auf das
zentrale Glossar umgelinkt werden, doppelte Pflege ist Quatsch.

Neue Kompetenz "eu-politische-beteiligung" (EU und politische
Beteiligung), Sek I 13-16, sort_order 130.

8 AT-Anker:
- 3 Fach (GW Sek I 4.4 EU-Vielfalt / 4.5 EU-Werte und
  Grundfreiheiten / 4.6 EU-Integration)
- 3 uebergreifend (Politische Bildung Nr. 7 / Wirtschaftsbildung
  Nr. 13 / Medienbildung Nr. 6)
- 2 andere Faecher (Geschichte und politische Bildung — EU-Geschichte;
  Deutsch — Texte verstehen, argumentieren)

kompetenz_modules: 2 fuer eu-werkstatt (politische Beteiligung
primaer + raeumliche-orientierung-europa sekundaer als
Logistik-Bruecke).

12 Glossar-Begriffe aus dem internen CONCEPT-Modal von
App/sims/eu-werkstatt/game.html zentralisiert (Kategorie
'gesellschaft'):
- eu-kommission, eu-parlament, rat-der-eu, europaeischer-rat
- europaeische-buergerinitiative, eu-petition
- initiativrecht, mitentscheidung
- eu-gesetz, eu-zustaendigkeit
- lobbyismus, freiwillige-vereinbarung

Jeder Begriff: short, text (3-6 Saetze), short_easy, text_easy
(Leichte Sprache), unit. Sprachregel-Sweep ueber alle Texte.
Plus 12 glossar_modules-Verknuepfungen zu eu-werkstatt.

Neues Seed-Skript App/php/seed_glossar_eu_werkstatt.php (idempotent).

Inbox-Notiz an EU-Werkstatt-Instanz mit Mapping-Tabelle und
optionalem Code-Snippet fuer "Im Glossar nachschlagen"-Link in
den Konzept-Karten — internes Modal soll bestehen bleiben (SVGs
sind erstklassig), nur Querverlinkung.

DB-Bilanz nach Seed:
- kompetenzen 13 (vorher 12)
- kompetenz_modules 27 (vorher 25)
- lehrplan_anchors 124 (vorher 116)
- glossar 130 (vorher 118)
- glossar_modules eu-werkstatt 12 (vorher 0)

Quellen:
- App/sims/eu-werkstatt/game.html (CONCEPTS-Objekt)
- BMBWF GW-Lehrplan Sek I 2023

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 00:37:23 +02:00
Adminator fbe59103c1 Lehrplan: Atlas + Glossar-Meldungen (Sonnensystem fertig) 2026-05-03 20:22:09 +02:00
Adminator 30cc844a2c Lehrplan: Sonnensystem komplett — Lehrplan + 10 Glossar-Begriffe
Neue Kompetenz "erdmechanik-klimazonen": Astronomie und Klimazonen.

8 AT-Anker:
- 2 Fach (GW Sek I 1.4/1.6: Beleuchtungszonen, Wendekreise,
  Polarkreise; 2.1/2.3: Klima im globalen System)
- 2 übergreifende Themen (Umweltbildung, Informatische Bildung)
- 4 andere Fächer (Physik: Himmelsmechanik/Licht/Skalen;
  Mathematik: Skalen/Verhältnisse/Größenordnungen; Geschichte:
  heliozentrisches Weltbild; Biologie: Tag-Nacht/Jahreszeiten)

kompetenz_modules: 3 Zuordnungen für sonnensystem (erdmechanik
primär; karten-navigation-alpen + klimawandel sekundär).

10 Glossar-Begriffe (neue Kategorie 'astronomie'):
- Achsneigung (23,5°)
- Sonnenwende (Solstitium)
- Tag-und-Nacht-Gleiche (Equinox)
- Wendekreis (23,5° N/S)
- Polarkreis (66,5° N/S)
- Ekliptik
- Mondphase
- Sonnenfinsternis
- Mondfinsternis
- Astronomische Einheit (AE)

Jeder Begriff mit short, text (3-5 Sätze), short_easy, text_easy
(Leichte Sprache) und unit. Plus 10 glossar_modules-Verknüpfungen
zu sonnensystem.

Neues Seed-Skript App/php/seed_glossar_sonnensystem.php,
idempotent. Ausnahmsweise pflegt Lehrplan direkt im Glossar
(Atlas-Konvention: Modul-Erstbestueckung ist okay, wenn die
Glossar-Instanz parallel beschäftigt ist).

DB-Bilanz nach Seed:
- kompetenzen 12 (vorher 11)
- kompetenz_modules 25 (vorher 22)
- lehrplan_anchors 116 (vorher 108)
- glossar 56 (vorher 46)
- glossar_modules sonnensystem 10 (vorher 0)

Quellen:
- _inbox/sonnensystem/2026-05-03-0022-kickoff-briefing.md
- .humanInput/sonnensystem/sonnensystem.html
- BMBWF GW-Lehrplan Sek I 2023

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:20:56 +02:00
Adminator 5bf27e215d Lehrplan: Klima 2D + 3D teilen Lehrplan + Glossar
- kompetenz_modules: klima-3d bekommt klimawandel primaer
  (Spiegelung von klima)
- Neuer Block 5b im Seed: glossar_modules wird via INSERT IGNORE
  gespiegelt (klima → klima-3d, 46 Begriffe). Idempotent.
- Auf modul-klima-3d sichtbar: 22 Anker, 1 Kompetenz, 46 Glossar-Chips
  — identisch zu modul-klima

DB-Bilanz: kompetenz_modules 22 (vorher 21).
Auftrag von Thomas (2026-05-03).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:12:58 +02:00
Adminator 1b1e5b0a69 Glossar: Status-Snapshot vor Session-Ende — 4 offene Anfragen
_status.md aktualisiert mit kompletter Übergabe-Beschreibung:
- Punkt 0: Sprachregel-Sweep (querschnitt, nebenbei)
- Punkt 1: Klima — 18 Begriffe (4 Aliasse + 14 neu) — höchste Priorität, Bug-Fix
- Punkt 2: Fluss — 9 Begriffe — Lehrplan-P1
- Punkt 3: Staustufen + Logistik + Heli/Stadt — 38+ Begriffe, geringere Dringlichkeit

Plus zwei noch ungetrackte Inbox-Mails ins Repo aufgenommen
(Audit-Trail).

Wiederaufnahme: _status.md → drei neueste Inbox-Mails →
module-interface.md Abschnitt 4a → "Hier Glossar, Stand rekonstruiert".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 22:29:56 +02:00
Adminator f6ff4122e2 Klima: Achievement-Tap-Tooltip (Touch hat keinen Hover)
Tap auf eine Erfolgs-Badge öffnet einen kleinen Tooltip mit Titel +
Beschreibung. Verschwindet nach 4 s oder beim nächsten Tap außerhalb.
Position bevorzugt unter der Badge, mit Fallback nach oben falls unten
kein Platz.
2026-05-02 21:19:48 +02:00
Adminator 80c487d463 Klima: Atlas-Mail mit Glossar-Begriffsliste zur Weiterleitung 2026-05-02 21:17:40 +02:00
Adminator e941293d93 Klima: Glossar-Links via DB-Alias-Map + Graph-Overlay iPad-Höhe
- KLIMA_TO_DB_KEY mappt Klima-Slugs auf DB-key_slug
  (windpark→windenergie, solaranlage→photovoltaik,
  erneuerbar→erneuerbare-energie, kohlekraftwerk→fossile-brennstoffe)
- GLOSSAR_DB_KNOWN-Set für alle bekannten DB-Slugs
- resolveGlossarDbKey: liefert den DB-Key oder null; tote Links
  werden ausgeblendet statt ins Leere zu führen
- ggs-graph-zoom-card iPad: 78dvh statt max-height 400 px,
  damit SVG + Header reinpassen + safe-area-padding-bottom
2026-05-02 21:16:05 +02:00
Adminator 9c8d12c3cd Klima: iPad-Polish korrigiert — schmale Spalte mit Toggle (Modal raus)
- Default-Spalte 76 px, nur Icons 60x60 + grüner Ring für owned
- ℹ-Toggle oben → Spalte schiebt sich als Absolute-Overlay auf 280 px
  (Desktop-Card-Variante mit Titel/Desc/Badges)
- Tap auf Karte: Bau-Flow + Auto-Collapse zur Icon-Spalte
- Querformat-Hinweis bei Portrait + Touch (📱 Bitte drehen)
- kw-card-detail-overlay (Center-Modal) entfernt — Thomas wollte das nicht
- viewport-fit=cover + dvh + safe-area schon vorher drin
2026-05-02 15:54:57 +02:00
Adminator d6d646ff05 Klima: iPad-Polish (5 Aufträge) + Glossar-Tote-Links gefixt
- Bau-Karten als Icon-Spalte am iPad mit Tap-to-Expand (kw-card-detail-overlay)
- Top-Bar slim: geograsim-Schriftzug ausgeblendet, Logo 32 px
- Sim-Titel einzeilig (font-size 13 px, ellipsis, max-width 200 px)
- Toaster-Cards ~50% kleiner am iPad
- Playlist als Icon-only (Select auf 38 px gestaucht)
- ggs-overlay-card lokal mit max-height 92dvh + safe-area-padding
- Owned-Cards bekommen grünen Ring (kw-card-owned)
- GLOSSAR ergänzt: ppm + megawatt (waren tote Links im Tutorial)
- Atlas-Mail raus für DB-Eintrag + glossar.php Anker-Logik
- Quittung an Logistik (kann deployen)
2026-05-02 15:25:37 +02:00
Adminator e61af43f9b Klima: Beitrag zur Lehrer-Ergebnisseite an Atlas
Thomas möchte teacher.html-Tab "Ergebnisse" um Modul-Filter, Modul-
spezifische Spalten, Game-Over-Indikator und Fleiß-Übersicht erweitern.

Cross-modulare Lehrer-Seite → Atlas-Territorium. Ich liefere den
Klima-Block fertig:
- 12 entscheidende Erfolgs-Parameter mit JSON-Pfad in `results`
- Game-Over-Erkennung + Icon-Mapping (🏆/💸/🌊/🌫/🌡/⏸)
- Hinweis auf 2D/3D-Varianten-Trennung (optional)
- Fleiß-SQL-Skizze für duration_ms-Aggregation

Engine sendet alle Felder bereits — Atlas kann die JSON direkt parsen,
keine Modul-seitige Änderung nötig.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 13:17:07 +02:00
Adminator f1b6fc0373 Lehrplan: Speicher-Snapshot — viewport-fit=cover + 100dvh + Status
- iPad-Verbesserungen vom Linter:
  meta viewport bekommt viewport-fit=cover (Safari Notch)
  body min-height nutzt 100dvh statt 100vh (dynamic viewport)
- _status.md: Save-Punkt fuer Crash-Recovery dokumentiert
- Inbox-Check: keine offenen Nachrichten an lehrplan

DB-Bilanz live: module_info 11, kompetenzen 11,
kompetenz_modules 21, lehrplan_anchors 108

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:57:56 +02:00
Adminator 0950f6fd4b Klima: Status-Datum aktualisiert (Instanz ruhend, abschaltbar)
Keine pending Klima-Datei-Änderungen mehr. Mailbox seit 23.04. nicht
neu beschrieben. Atlas-Antwort liegt verarbeitet vor, Integrations-
Umbau wartet weiter auf Thomas-Go.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:57:02 +02:00
Adminator 8a8cbd1dfd Klima: Snapshot — pages/klima-2d.php nachgezogen + WIP-Cleanup
Sicherheits-Commit, damit der Stand vor möglichem Shutdown gesichert
ist. App/pages/klima-2d.php war noch untracked, jetzt im Repo.
Restliche modified-Markierungen waren reine Line-Ending-Normalisierung.
2026-04-27 07:24:41 +02:00
Adminator fb8877cdf3 Klima: iPad-Polish übernommen + Status-Update
Externer Plattform-Sweep hat in allen Klima-HTML-Files iPad-spezifische
Polish-Änderungen ergänzt:
- viewport-fit=cover in 2D / 3D / preview-sounds (Notch-Handling)
- 100dvh statt 100vh in plane-test + preview-sounds (dynamic viewport)
- Cockpit-Link in 3D-Endscreen auf '../../schueler' präzisiert

Inhaltlich keine Modul-Änderung von mir, nur Sicherung der externen
Edits, damit nichts verloren geht.

_status.md auf bereit-warte-auf-go gesetzt — Atlas-Antwort vom 23.04.
liegt vor (alle 6 Fragen beantwortet, End-Screen-Komponente verfügbar,
2D+3D parallel umbauen, Sammel-Commit-Subject vorgegeben). Klima-
Instanz ist arbeitsbereit, wartet auf Thomas-Go fürs Loslegen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 07:23:11 +02:00
Adminator 45d810bc08 Lehrplan: Status + Atlas-Meldung (Staustufen-Anker fertig) 2026-04-26 01:33:27 +02:00
Adminator 23e1aee579 Lehrplan: Staustufen-Modul — neue Kompetenz + 8 AT-Anker
- Neue Kompetenz: energieversorgung-speicherung
  ("Energieversorgung und Energiespeicherung", category=klima,
   Alter 10–14, sort_order 110)
- 8 neue AT-Anker:
  - 3 Fach (GW Sek I 2.1 / 3.6+3.13 / 4.1+4.3+4.10) mit
    Original-Zitaten zu erneuerbaren Energieträgern, alpiner
    Wasserkraft als Wirtschaftsstandort und Mensch-Umwelt-Beziehung
  - 3 Übergreifende Themen: Umweltbildung (BNE), Wirtschaftsbildung
    (Wirkungsgrade, Effizienz), Informatische Bildung (Smart Grids,
    Lastausgleich-Algorithmen)
  - 2 Andere Fächer: Physik (Energieerhaltung, Höhenenergie,
    Wirkungsgrad), Mathematik (Bedarfskurven, Bilanzen, %-Füllstände)
- kompetenz_modules: staustufen primär (energie), 3 sekundär
  (klimawandel, systemisches-denken, karten-navigation-alpen);
  energiemix bekommt rückwirkend energieversorgung-speicherung primär
- modul-staustufen.php Wrapper hinzugefügt (3-Zeilen-Pattern)
- Inbox-Status: gelesen

DB-Bilanz: module_info 11, kompetenzen 11, kompetenz_modules 21,
lehrplan_anchors 108

Quelle: .humanInput/StaustufenSimulator/Pflichtenheft.txt (Pumpspeicher-
Kaskade, 5-Tage-Prognose, Sek I, 10–14 J)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 01:32:38 +02:00
Adminator 79ac9dad84 Atlas: Deploy-Buendel vor Staustufen-Sprint
- Logistik: Musik-Player Playlist-Modus (5 Gruppen, 20 Tracks)
- Heli: End-Screen auf .ggs-endscreen, Mission-Bilder, Voice-Lines, Briefing-Audio
- Logistik: Layout-Tausch Auftraege+Fahrzeuge links, Karte rechts
- Logistik: Tier-1-Staedte ausgebaut, Auto-Timescale-Badge
- Logistik: Roadnet/Railnet Dijkstra-Routing, Balance-Updates
- Atlas: 5 Atlas-Inbox-Nachrichten (Cards-Pattern, DALL-E-Key, Musik-Playlists)
- Status-Updates Heli + Logistik

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 01:05:13 +02:00
Adminator f811437a35 Lehrplan: Status + Atlas-Meldung (Modul-Filter in lehrplan.php) 2026-04-24 09:26:51 +02:00
Adminator d37857f12a Lehrplan: ?module=X-Filter in lehrplan.php (Modul-Fokus-Banner)
- lehrplan.php liest jetzt den Query-Parameter ?module=<id> und
  filtert die Kompetenz-Cards auf die dem Modul zugeordneten
  Eintraege (via kompetenz_modules)
- Modul-Fokus-Banner ueber dem Hero: Icon, Titel, "Zurueck zur
  Simulation"-Link, "Alle Module anzeigen"-Link
- Hero-Titel + Meta-Description + <title>-Tag spiegeln den Fokus
- Ungueltige Modul-Slugs fallen stillschweigend auf Full-View zurueck
- Alle bestehenden Filter (country, theme) wirken weiterhin
  gleichzeitig

Use-Case: Atlas' Template-Button 📚 Lehrplan in den Sim-Wrappern
zeigt aktuell auf /modul-<id>#lehrplan-bezug. Diese Seite dient als
Alternative/Fallback, falls jemand direkt auf lehrplan.php?module=X
landet.

Verifiziert:
- lehrplan?module=logistik      6 Kompetenzen (von 10)
- lehrplan?module=klima         1 Kompetenz
- lehrplan?module=fluss         2 Kompetenzen (Naturgefahren + Stadt)
- lehrplan?module=unknown       10 Kompetenzen (Full-View Fallback)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:26:03 +02:00
Adminator e300b12c38 Lehrplan: Status + Atlas-Meldung (Logistik-Anker + Klima-Toggle fertig) 2026-04-24 09:19:36 +02:00
Adminator 13833ef410 Lehrplan: modul-klima.php 2D/3D-Doppel-Launcher + Geraete-Hinweis
- Sonderfall im Partial fuer MODULE_ID=klima: zwei CTAs
  "2D starten" (primary) + "3D starten" (secondary), beide mit
  level=1 als Default
- WebGL-Hinweistext unter den CTAs (dezent, iPad Gen 8 als Schwelle,
  2D didaktisch identisch empfohlen)
- .md-device-hint CSS: fjord-Akzent, max-width 560px, 14px

Andere Module unveraendert — Single-CTA-Pfad bleibt Default.
Atlas' 19.04.-Toggle-Auftrag erledigt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:18:29 +02:00
Adminator c80f23a40c Lehrplan: 6 Logistik-Kompetenzen + 22 AT-Anker
- Neue kompetenzen: raeumliche-orientierung-europa, kartenkompetenz-
  logistik, wirtschaft-logistikverstaendnis, planungs-entscheidungs-
  kompetenz, verkehrserziehung-sicherheit, systemisches-denken
- kompetenz_modules: 6 Primaer-Zuordnungen zu logistik + 2 Sekundaer
  zu lieferketten (wirtschaft + systemisches-denken)
- lehrplan_anchors: 22 neue AT-Anker (13 Fach-GW-Zitate aus Lehrplan
  2023 Kompetenzen 1.3/1.6/2.6/2.8/2.3/3.6/3.13/3.14/3.17/4.4/4.6/
  4.8/4.10 + 5 uebergreifende Themen + 4 andere Faecher)
- Alle 23 Kernziele (lg-orient, lg-karte, lg-wirt, lg-plan, lg-verk,
  lg-sys) in den Anker-Titeln referenziert
- DB-Bilanz: module_info 10, kompetenzen 10, kompetenz_modules 16,
  lehrplan_anchors 100

Quelle: App/sims/logistik/kompetenzen.json (Pflichtenheft Kap 3.4),
integration-konzept.md Paragraph 7.4

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:18:18 +02:00
Adminator 239ddfb95a Atlas: Landing-Sprachregel + Waypoints + Platzhalter + klima-3d fix
- index.html: 'Sofort probieren' -> 'Simulation starten' (Lehrplan-Request)
- Landing-Klima-3D-Button auf klima-3d (statt game-3d.html)
- waypoints.sql aus lokaler DB regeneriert (152 Waypoints/15 Basen)
- Prod-Migration: easy_language-Spalte in students-Tabelle (additiv)
- Prod-Sync: geo_waypoints 70 -> 152, alle Heli-Missions-Ziele jetzt da
- module_info.klima-3d.page_url = 'modul-klima-3d' (Lehrplan-Hinweis)
- Lehrplan-Platzhalter-Detailseiten deployed: modul-klima-3d,
  modul-erdbeben, modul-energiemix, modul-lieferketten, modul-regenwald

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 01:14:26 +02:00
Adminator 2bd6a84435 Atlas+Instanzen: Phase B Integration — Wrapper-Mode-Check + Assessment
Logistik + Heli Wrappers von den Instanzen umgebaut:
- Mode-Check (free/teacher_started/locked) direkt aus DB
- Forced-Level-Start bei teacher_started
- Easy-Flag aus students.easy_language
- Locked-Seite mit Sperrhinweis + Cockpit-Link
- Assessment-Calls (started/running/completed) in Heli game.html

Atlas-Arbeit:
- teacher.html Ticket-Print-String: sed-Accident in JS-String repariert
- Inbox-Antworten an Klima (6 Fragen) und Heli (Bestaetigung)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:21:57 +02:00
Adminator c7e280c836 Klima: Instanz reaktiviert, Integrations-Auftrag quittiert
- Postfach geprüft: Atlas-Integrations-Auftrag (23.04.) + Level-Class-
  Rename (übersehen 19.04.) + Drama-Konvention (alt, erledigt).
- _inbox/zentrale/2026-04-23-klima-reaktivierung-integrations-frage.md:
  Rückfrage an Atlas mit Anweisungs-Punkten (Reihenfolge, End-Screen-
  Komponente, Demo-Session-Pattern, Session-Mode-Var, Rename-Sammel-
  Commit, 2D/3D parallel?).
- _status.md: Status re-aktiviert-warte-auf-atlas, Blocker-Liste
  mit den 5 offenen Fragen.

Instanz wartet auf Atlas-Rückmeldung bevor Integrations-Umbau startet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:19:55 +02:00
Adminator e9bcf983d8 Lehrplan: Status + Atlas-Meldung (Sprachregel + Platzhalter) 2026-04-23 23:18:28 +02:00
Adminator 3fe3864f68 Lehrplan: Platzhalter-Detailseiten fuer 6 weitere Module
- modul-regenwald.php, modul-erdbeben.php, modul-energiemix.php,
  modul-lieferketten.php, modul-logistik.php (alle status=geplant)
- modul-klima-3d.php (aktiv, Atlas' Admin-Eintrag)
- Alle als 3-Zeilen-Wrapper auf dem gemeinsamen Partial
- Status-'geplant' zeigt automatisch Vorschau-Banner
  ("Modul ist noch in Entwicklung, Inhalte werden ergaenzt")
- Lehrplan-Bezug + Glossar-Chips + Metadaten sind sofort verfuegbar,
  sobald Atlas' Admin-UI die Prosa befuellt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:17:17 +02:00
Adminator 478df47d9f Lehrplan: Sprachregel Lernarbeit durchgaengig
- CTA "Jetzt probieren" -> "Simulation starten" (modul_detail.php)
- simulationen.php: "Sofort probieren" -> "Simulation starten",
  Hero "ausprobieren" -> "direkt starten"
- Seed: Klima-Easy "Du spielst" -> "Du bist", "Das Spiel dauert" ->
  "Die Simulation geht"; Stadt long_desc "Spielmodul" ->
  "Simulationsmodul"; DB-Kommentar "Link zum Spielen" -> "Link zur
  Simulation"
- Hinweis: glossar.php hat noch "ausprobieren" (Glossar-Instanz)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 23:17:05 +02:00
Adminator 8455a96674 Atlas: Versions-Tag rechts unten auf allen Hauptseiten
- App/version.json mit Commit-SHA + Build-Datum
- App/assets/js/version-tag.js — rendert kleines Tag rechts unten
- Eingebunden in schueler, teacher, index, login, admin, profil
- Ermoeglicht Vergleich lokal vs. geograsim.at auf einen Blick

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:06:42 +02:00
Adminator b356c79134 Atlas: Phase A Integrations-Konzept + Cockpit + End-Screen
- Integrations-Konzept dokumentiert (App/docs/integration-konzept.md)
- Module-IDs vereinheitlicht: sim-XX -> semantische IDs (klima/heli/fluss/logistik/...)
- class_modules um current_level + quiz_enabled Spalten erweitert
- modules.php liefert dynamisch aus module_info, inkl. forcedLevel, quizEnabled,
  play_url, dueDate; neuer Filter ?module_id=X fuer einzeln
- schueler.html Cockpit: komplett API-first, teacher_started-Sektion als
  Auftrags-Block, neue IDs, Sprachregel 4a (Starten statt Spielen)
- teacher.html: Hardcoded ALL_MODS entfernt, laedt dynamisch
- Design-System: .ggs-endscreen-Komponente fuer einheitliche Modul-Abschluesse
- index.html Landing-Links gefixt: App/modul-X -> modul-X, V1-HTMLs auf
  Front-Controller-Routen (fluss, heli-game, stadt)
- 6 Inbox-Auftraege an Instanzen (Klima/Heli/Fluss/Logistik/Lehrplan/Glossar)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 11:42:19 +02:00
Adminator b94658f57c Atlas: .htaccess RewriteBase entfernt — Multi-Domain-tauglich
Hart gesetztes RewriteBase /geograsim/App/ funktioniert nur lokal,
fuehrt auf der eigenen Domain geograsim.at zum Endlos-Redirect-Loop
(AH00124: 10 internal redirects). Ohne RewriteBase nutzt Apache den
Request-Pfad automatisch — funktioniert fuer alle drei Deploy-Ziele:
localhost/geograsim/App, staatsgeheimnis.at/geograsim, geograsim.at.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:31:07 +02:00
Adminator fc8996f064 Atlas: DSGVO-Tile-Proxy + Logistik-Phase-bis-8 + Sounds-Anleitung
Platform:
- OSM-DE als dritter Tile-Proxy-Provider (deutsche Beschriftungen)
- logistik.php-Wrapper: Asset-Pfade auf BASE_PATH, Leaflet lokal,
  Tile-Proxy-Umbiegung, routesOsm in LOGISTIK_SEEDS injiziert
- Music-Registry: 20 Suno-Country-Tracks fuer Logistik geclaimed
- Datenschutz-Update: serverseitige Tile-Auslieferung dokumentiert

Logistik (Instanz-Lieferungen, parallel):
- Phase 0 bis 8 inkl. Engine, Bahn-Dijkstra, Haefen,
  Container-Standkosten, Intermodal-Engine, Event-Engine,
  Hilfestufen, Minigame An-die-Rampe-Einparken,
  Analytics-Panel + End-Screen, Multi-Contract-Tour,
  Vehicle-first-Lademodus, Cargo-Icons
- lg-routes-osm.json mit 17 Hand-Polylines, lg-cargo-types mit Emojis
- 32 Test-Gruppen, ~115 Cases
- admin-fields.json fuer Admin-UI-Integration

Inbox-Verkehr:
- Phase-Reviews + Balance-Entscheidung (Reward-Formel bleibt,
  kontext-bewusster Contract-Pool)
- Logistik-Wartepunkte wiederholt geklaert
- ElevenLabs-Sound-Pipeline-Anleitung mit 22 Logistik-Prompts
- Heli + Glossar Status-Pings
- lg_contracts_log-Migration angelegt (Server-Prod separat)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 08:15:00 +02:00
Adminator eac686ee1a Atlas: Tile-Proxy + Leaflet lokal — DSGVO-Sanierung fuer Kartendienste
- Leaflet 1.9.4 (CSS + JS + 5 Marker-Images) lokal unter
  App/assets/vendor/leaflet/ statt via unpkg.com/Cloudflare
- Neuer PHP-Endpunkt App/php/tile-proxy.php mit Disk-Cache, liefert
  OSM- und CARTO-Kacheln serverseitig aus; Browser kontaktiert keinen
  Drittanbieter mehr
- Whitelist (osm, carto), Zoom 3-18, 30 Tage Browser-Cache, ETag +
  If-Modified-Since, sauberer User-Agent fuer OSM-Policy
- Cache-Pfad: APP_ROOT/data/tiles lokal, via .env TILE_CACHE_DIR
  auf Produktions-Volume /var/www/html/geograsim/data/tiles
- Heli (heli.php + heli-game.php), Logistik (logistik.php), Routes-
  Dev-Tool und admin-levels.html alle auf Proxy umgestellt
- Datenschutzerklaerung: alter Google-AdSense-Passus (falsch, nicht
  im Einsatz) entfernt, neuer Abschnitt zur serverseitigen
  Tile-Auslieferung mit Rechtsgrundlage Art. 6 Abs. 1 lit. f DSGVO
- .gitignore: App/data/tiles ausgeschlossen
- .env.example: TILE_CACHE_DIR + OpenAI/ElevenLabs dokumentiert

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 00:53:43 +02:00
Adminator 3885c83294 Atlas: Logistik-Gerüst + Heli-Fix + Waypoints-Regen + Level-Picker
- Neues Modul 'Logistik Europa': module_info-Eintrag, Landing-Card,
  PHP-Wrapper (logistik.php, modul-logistik.php), Engine-Skelett mit
  Enums + Helpern (travelCost, latePenalty, Bonus, Dijkstra,
  Polyline-Interpolation), Test-Harness, Kompetenzen-Draft,
  5 Seed-Dateien (locations, vehicle-types, cargo-types, railnet,
  contract-templates), 3 Level-Einträge in game_levels, Phase 0 durch
  die Logistik-Instanz geliefert und Atlas-Review bestanden
- Heli-Fix: heli-game.php mit BASE_PATH (production-sicher) +
  Asset-Pfad-Injection, geo_waypoints-Tabelle per Seed-Script
  auffindbar gemacht
- Waypoints-Regen-Tool: regen-waypoints-sql.php synchronisiert
  waypoints.sql aus DB (70 Einträge)
- Design-System: .ggs-level-grid / .ggs-level-card als Standard
  gepromotet (Klimas Muster, iPad-hover-safe)
- Inbox-Nachrichten: Kickoff-Briefings für Heli und Logistik,
  Asset-Map für Heli, DALL-E-Pipeline-Anleitung, Koordinations-
  nachrichten an Fluss, Klima, Glossar, Lehrplan

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:43:48 +02:00
Adminator 3931a701e1 Klima 3D V2: Abschlussprotokoll — Session geschlossen
- _inbox/zentrale/2026-04-19-2200-klima-3d-v2-abschlussprotokoll.md:
  vollständiges Abschlussprotokoll an Atlas mit Feature-Liste,
  Commit-Kette, offenen Punkten (nicht von mir abhängig) und
  Ideen-Ausblick für spätere Weiterentwicklungen.
- _status.md: session-geschlossen-Marker gesetzt, Verweis auf
  Abschlussprotokoll.
- _inbox/glossar/2026-04-19-2130: status auf gelesen aktualisiert
  (Atlas hat den Hinweis gesehen).

Klima-Instanz wartet bis zum nächsten Aktivierungs-Impuls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:45:38 +02:00
Adminator af9ecf6b22 Klima 3D: Strandleben tourismus-reaktiv + Radweg-Dashes durchgehend
- beachLifeCount(): base 8 + 14 wenn state.tourismMode + 4 pro Airport,
  gedeckelt bei 40. Skaliert die Anzahl der Strandleben-Elemente an
  die Tourismus-Aktivität (mehr Tourismus = mehr Menschen am Strand).
  syncBeachLife() wird pro Frame gerufen, rebuildt aber nur, wenn sich
  die Zahl wirklich geändert hat (O(1) Check bei Stabilität).
- Radwege: zurück zu gestrichelter Mittellinie (5 Striche × Periode
  0.28). BIKE_SPACING auf exakt BIKE_LENGTH (1.4) gesetzt (vorher
  0.92·BIKE_LENGTH), damit die Dashes nahtlos über Segment-Grenzen
  weiterlaufen — Strich des nächsten Segments landet genau dort, wo
  die Dash-Periode des vorherigen weitergeht.
- Ausrichtung bleibt via coastRotation: Radwege folgen weiter der
  gleichen Ellipsen-Tangente wie das grüne Zone-Overlay beim
  Platzieren, sind also parallel zur Küstenkurve.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:53:20 +02:00
Adminator ce0164264f Klima 3D V2: Session-Abschluss — Strandleben komplett + Berichte
- Strandleben: 22 Stellen (vorher 12), fünf Typen statt drei:
  Figur, Sonnenschirm, Handtuch, Liegestuhl (neu), Hund (neu).
  Positionen über gleichmäßiges Winkel-Raster mit Jitter, damit die
  Elemente rundum verteilt sitzen statt zufällig zusammen zu klumpen.
- makeBeachChair: angeschrägte Rückenlehne + Sitz + vier Beine in
  wechselnder Stoff-Farbe.
- makeBeachDog: Körper + Kopf + Schnauze + Schwanz + vier Beinchen +
  Ohren in zufälliger Fell-Farbe.

- _status.md: auf finalen V2-Stand überschrieben
- _inbox/zentrale/2026-04-19-2130-klima-3d-v2-abschluss.md: Bericht an
  Atlas mit vollständiger Feature-Liste
- _inbox/zentrale/2026-04-19-2135-klima-3d-v2-lehrplan-nachtrag.md:
  Nachtrag an Lehrplan (Modul-Detailseite um 2D/3D-Launcher erweitern)
- _inbox/glossar/2026-04-19-2130-klima-3d-v2-info-topics.md: Info an
  Glossar (17 view-lokale INFO_TOPICS, keine DB-Änderung nötig)

3D V2 ist einsatzbereit — Thomas schließt heute ab, strukturierter
Browser-Regression-Test folgt in späterer Session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:49:04 +02:00
Adminator 59d45e419d Klima 3D: Runway senkrecht zum Vulkan + Plane bleibt sichtbar + korrekte Höhe
- Runway-Ausrichtung: coastRotation(airport.x, airport.z) statt
  atan2(-dz, dx). Runway läuft jetzt parallel zur Küste an der
  Airport-Position, also im rechten Winkel zur Radialen Richtung
  Vulkan. Flug geht parallel am Vulkan vorbei, nicht durch ihn.
  Greift in finalizeBuy und im syncMeasureMeshes-Force-Override.
- RUNWAY_Y von 0.50 auf 0.78 korrigiert: Plane-Räder stehen jetzt
  exakt auf der Asphalt-Oberkante (airport.y + 0.56 Runway-Top +
  0.22 Wheel-Offset = 0.78). Versank vorher sichtbar in der Bahn.
- Plane-Zyklus: neue Phasen, damit das Flugzeug nach der Landung
  sichtbar bleibt statt zu verschwinden.
    0-14:  Parken am -X-Ende, Nase +X (sichtbar)
    14-18: Takeoff +X
    18-26: Climb-Out +X, Banking am Ende
    26-76: unsichtbar (50 s Flugrunde)
    76-84: Approach aus +X-Ferne, Nase -X
    84-88: Landing-Roll +X → -X
    88-92: Wenden am -X-Ende (Yaw-Interpolation π → 0)
    92-110: Parken am -X-Ende, Nase +X (sichtbar, steht bereit)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:38:42 +02:00
Adminator b77a453306 Klima 3D: Strandleben winzig + Radweg-Mittellinie durchgehend + Airport-Sync
- Strandleben (beachLifeGroup): 12 winzige Figuren / Sonnenschirme /
  Strandhandtücher am Küstenband (Terrain 0.20..0.55), Scale 0.55..0.90.
  makeBeachFigure / makeBeachUmbrella / makeBeachTowel; buildBeachLife
  wird von startGame und deserializeState gerufen.
- Radwege: 5 einzelne Stripes durch EINE durchgehende weiße Mittellinie
  ersetzt (Box 1.4×0.006×0.05). Bei snap-Spacing 0.92 × BIKE_LENGTH
  überlappen sich die Mittellinien benachbarter Segmente zu einer
  visuell kontinuierlichen Linie entlang der Küstenbiegung.
- syncMeasureMeshes erzwingt für Airport-Meshes jetzt IMMER die
  away-from-volcano-Rotation (auch für alte Saves, die noch mit
  zufälliger Rotation gespeichert wurden). inst.rotation wird dabei
  korrigiert, damit spätere Saves konsistent sind.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:33:09 +02:00
Adminator e221f14f5c Klima 3D: Flugzeug fliegt nase-vorwärts + Banking in der Kurve
Aus plane-test.html-Kalibrierung bestätigt: Flugzeug soll Nase in
airport-lokalem +X halten, Bewegung in +X. updateSinglePlane
komplett neu:

- Parken am -X-Ende der Runway (vorher +X)
- Takeoff rollt -X → +X (vom Vulkan weg, da airport.rotation.y =
  atan2(-dz, dx) das +X in die Away-Richtung dreht)
- Climb-Out in +X-Richtung
- Ab der zweiten Climb-Hälfte bankt es leicht (rotation.z wächst)
  zeigt Seitwärts-Kippen in die Kurve bevor es verschwindet
- Approach kommt aus -X-Ferne zurück, Banking löst sich auf
- Landung in +X-Richtung (wie Takeoff)
- Taxi zurück zur Parkposition: unsichtbar (Teleport OK)

Nase-Ausrichtung: rotation.y = airport.rotation.y (kein π-Offset mehr
— entspricht der Test-Seite). Pitch für Start/Landung, Roll für
Kurvenmanöver.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:29:12 +02:00
Adminator 96fb75ffdf Klima 3D: Flugzeug-Test-Seite + Airport-Ausrichtung vom Vulkan weg
- Neue Datei: App/sims/klima/plane-test.html
  Three.js-Sandbox mit dem makePlane-Mesh, Slidern für rotation.x/y/z,
  Achsen-Helper (X rot, Y grün, Z blau), großem roten Pfeil als
  Bewegungsrichtung (+X), "Flug simulieren"-Button und "Werte kopieren".
  Thomas kann damit die korrekte Flug-Rotation finden und zurückmelden.
- Airport-Ausrichtung: finalizeBuy überschreibt die zufällige
  placementRotation bei id='airport' mit atan2(-dz, dx) zum Vulkan —
  Runway (lokales +X) zeigt nun IMMER vom Vulkan weg, Start und
  Landung über Ozean statt Bergflanke.
- airportLocalToWorld: Vorzeichen an Three.js-Konvention angeglichen
  (x_w += lz·sinR, z_w -= lx·sinR), damit rotierte Airports ihre
  internen Offsets korrekt in Welt-Koordinaten übertragen.
- Plane-Zyklus: +60 s Grund-Offset entfernt, Flugzeug ist bei
  Airport-Bau sofort sichtbar (Phase 0 = parken auf der Runway).

Test-URL: http://localhost/geograsim/App/sims/klima/plane-test.html

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:22:34 +02:00
Adminator 665e86d485 Klima 3D: Musik-Reset, Szene 10% tiefer, Drohnen-Toggle
- ggsMusicResetToDefault: neuer Helper springt auf den ersten Track im
  Dropdown. Wird in startGame aufgerufen, damit Drama-Musik aus dem
  Endscreen eines vorherigen Durchgangs nicht ins neue Level hinüber-
  läuft (erklärt, warum 'Drama im laufenden Spiel' lief).
- camera.lookAt: von (0, 0.5, 0) auf (0, 2.8, 0) angehoben → Insel
  rutscht im Bild ~10 % nach unten, oben mehr Himmel/Dunst-Wolken.
- Drohnen-Toggle 🚁 im Header: neuer droneEnabled-Flag. Aus =
  Auto-Orbit ist dauerhaft gestoppt (nur Maus-Drag bewegt Kamera).
  Ein = Idle-Timeout resumed wie bisher. Button zeigt aktiven
  Zustand über opacity + .active-Klasse.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:13:47 +02:00
Adminator 21cc33e36b Klima 3D: Wolken kleiner + deutlich mehr Variation
- makeCloud: Opacity pro Wolke gewürfelt (0.45..0.80), Bump-Anzahl
  2..6 statt 3..5, Bump-Radien 0.45..1.15 statt 0.9..1.8 (halb so
  groß). Spacing zwischen Bumps variiert stärker.
- 5 Wolken-Instanzen (vorher 4), Parameter-Ranges breiter:
  - Höhe 5..9 (vorher 6.5..8.3)
  - Scale 0.35..0.85 (vorher 0.75..1.20) — kleiner + mehr Streuung
  - Speed 0.5..1.9 (vorher 0.8..1.4)
  - Z-Start 2..12, Z-End -14..-22 (stärker versetzt)
Fühlt sich jetzt mehr wie natürliche Dunst-Schwaden an, weniger
synchron.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:10:46 +02:00
Adminator 0fe4377deb Klima 3D: Heli pro Airport, Flugzeug, Flut-Tönung, Graph-Top-Verlust
- Airport-Fahrzeuge: pro Flughafen eigener Heli + Flugzeug, in
  airportVehicles-Map verwaltet, über syncMeasureMeshes mit-synchronisiert.
  Phasen-Offsets per Airport-Index, damit mehrere Flughäfen nicht
  synchron laufen.
- Heli: landet jetzt korrekt auf dem H (padLocal aus userData statt
  hardcoded, inkl. LIFT-Korrektur). Alle ~3 Zyklen (deterministisch)
  Vulkan-Runde statt Airport-Kreis — Radius 9 um VOLCANO_X/Z.
- Flugzeug (makePlane, low-poly Cessna): 110-s-Zyklus — parkt am
  Runway-Anfang, Takeoff-Roll, Climb-Out nach -X, 50 s unsichtbar
  (Reise), Approach, Landing-Roll, Taxi. Propeller dreht.
- Wolken: Höhe auf ~7 abgesenkt (Vulkan-Gletscher-Höhe), fühlen sich
  jetzt wie Dunst um den Kegel an statt Himmels-Schleier.
- Flut-Tönung: applyFloodTintAll lerpt Material-Farben aller Mesh-
  Füße, die unter ocean.position.y liegen, Richtung Dunkelgrau und
  kippt sie bis 0.12 rad. Wirkt auf Maßnahmen + Dorf + Citizen-Strand.
- Graph-Top = verloren: neu endLevel(false, 'co2-top' | 'temp-top')
  wenn state.co2Ppm ≥ 700 oder state.currentTemp ≥ 19. Endscreen
  bekommt passende Icons (🌫 / 🌡) und Titel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:08:04 +02:00
Adminator c7b3636815 Klima 3D: Baumfäll-Animation + Solar-Kauf-Hinweis
- Blackout fällt Bäume jetzt sichtbar: Baum kippt um (easeOut), Totenkopf
  erscheint nach 1.8 s darüber, nach 3.2 s fadet alles weg. Animation
  hängt an state.animMs → Pause friert ein. Totenkopf als Canvas-Texture
  auf einem Billboard-Plane.
- simulateTick vergleicht Forest-Count vor/nach engine.tick; orphaned
  Meshes werden aus placedMeshes gepullt und an fallenTrees übergeben,
  damit syncMeasureMeshes sie nicht voreilig entfernt.
- Toast-Card pro Tick mit Holz-Fäll-Meldung (🪓 'Bewohner:innen fällen N
  Bäume zum Heizen'), klickbar auf INFO_TOPICS.blackout.
- Solaranlage-Kauf: Toast mit Strom-Leistung (+1 MW) und Wartung
  (2 Mio €/Jahr), analog zum Gründach-Hinweis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:01:13 +02:00
Adminator f0fd54f458 Klima 3D: Wolken, kleineres Airport-Plateau, Pause-Zeichen zentral
- Wolken am Himmel: 4 Dunst-Gruppen (je 3-5 Kugeln, transluzent), ziehen
  vom +X Rand über die Insel Richtung Vulkan (−X, −Z-Drift). Eigene
  Phase, Höhe, Zyklusdauer pro Wolke. updateClouds in updateAmbient.
- Airport-Plateau: von 3.4×1.55 auf 2.55×1.25 geschrumpft, nach +Z
  versetzt (0.28). Endet knapp innerhalb der Runway, deckt aber
  Terminal + Heli-Pad weiter ab.
- Pause-Zentrum: neue Card mit ▶-Symbol in der Mitte der Szene, sichtbar
  bei state.speed === 0. Klick startet 1×-Speed. Glas-Optik, animation
  bei Einblendung. Sync via updatePauseCenter in renderUI und bei jedem
  Speed-Klick.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:58:22 +02:00
Adminator 9a8ce64d09 Klima 3D: Gründach-Kauf erklärt Wohnraum-Effekt
Jede Maßnahme hebt engine-seitig die Population-Obergrenze um 5 % der
Start-Bevölkerung (computePopulationGrowth.capBonus). Beim Gründach
machen wir das didaktisch explizit sichtbar: Toast-Card beim Kauf weist
auf neue Wohnungen, zuziehende Einwohner:innen und Touristen + höhere
Steuer-Einnahmen hin. infoKey=green_roof_info macht die Karte klickbar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:55:08 +02:00
Adminator 3836b29c5d Klima 3D: Radweg-Snap, Start-Bäume, Krisen-Toasts, Airport-Plateau, Sand-Fixes
- Radwege: snappen an bestehende Pfade an (Distanz 2.2, Spacing 92 %,
  leicht überlappend). Rotation am NEUEN Mittelpunkt via coastRotation,
  Kette folgt so der Küstenbiegung statt starre Linie.
- Start-Wald: 3 Bäume bei startGame pre-seeded (forest.instances mit
  starter-Flag). Engine-Blackout-Logik fällt sie bei Stromausfall.
  Toast 'Du startest mit 3 Bäumen ...' am Level-Start, klickbar auf
  INFO_TOPICS.blackout.
- updateCrisisMusic: Edge-Detection pro Kriterium (Budget/Temp/Flood),
  jeweils Toast-Card beim Übergang ins Kritische. Merker in startGame
  zurückgesetzt.
- Airport: Plateau-Sockel (3.4×0.20×1.55 + dünne helle Oberkante), alle
  Bauten um LIFT=0.24 angehoben. Hebt Airport sichtbar aus dem Terrain,
  kein Versinken am Hang mehr. Heli-Pad-Offset folgt nach.
- Sand-Aufschüttung (Bau-Maßnahme): zurück zum reinen Lathe-Kegel
  (Plattform + Gras raus). Kleiner, zurückhaltender Sand-Hügel wie
  ursprünglich V1.
- Citizen-Event 'Strand aufschütten' (citizen-tourism): neuer
  syncCitizenBeach → breiter flacher Sand-Streifen vorne am Ufer,
  parallel zur Küste. Nur sichtbar wenn state.hasSandBeach gesetzt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:53:48 +02:00
Adminator 64bb9356b0 Klima: 3D V2 fertig + PHP-Wrapper + Sprachregel + Lehrplan-Notiz
- App/sims/klima/game-3d.html neu (~4800 Z.): Three.js-Szene via KlimaEngine.
  Phasen A-D durch (Gerüst, Terrain + Vulkan + Ozean + OrbitControls,
  12 Mesh-Factories, Raycaster + Placement-Ghost + Zonen-Overlay).
  Ambient: Flughafen 2x mit Heli-Landezyklus auf H-Pad, drei Boote + Yacht,
  Start-Dorf mit Rauchfäden, Vulkan-Schutzwall, Mangroven-Sterben grau,
  Radwege am Küstenband, Ketten-Bau, Experten-Modus mit 17 Info-Cards.
  Sprachregel 4a angewandt (Arbeitsstand statt Spielstand etc.).
- App/pages/klima-3d.php neu: PHP-Wrapper analog zu klima-2d.php, teilt
  sich module_info und game_levels, separate Save-Slots (ggs-save-klima-3d).
- _inbox/klima/_status.md überschrieben.
- _inbox/zentrale/2026-04-19-1700-klima-3d-v2-fertig-modul-toggle.md:
  Bitte an Lehrplan um 2D/3D-Launcher auf modul-klima.php.

V1 (App/game-3d.html) bleibt unberührt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:46:49 +02:00
Adminator 7c95a99612 Atlas: Landing-Diff eingebaut (Card-Split "Mehr erfahren" + "Sofort probieren")
- FREE-Sektion: 4 Cards mit Zwei-Button-Muster
  (Klima-3D, Fluss, Heli, Stadt — Reihenfolge nach Masstab abnehmend)
- LOCKED-Sektion: 5 Cards analog mit "Mehr erfahren" + "Anmelden"
- Neues CSS: .sim-btn-primary/secondary/disabled, .sim-card-cta
- Heli aus LOCKED in FREE verschoben (hat module_info aktiv)
- Quelle: Lehrplan-Diff 2026-04-19 09:45

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:57:19 +02:00
Adminator 9a61f55cb1 Atlas: Infrastruktur + Team-Konventionen + Sprachregel Lernarbeit
- Design-System (assets/css/design-system.css) mit 21 Komponenten,
  iPad-Responsive-Breakpoints, Touch-Ziele 36px, Music-Player,
  Glossar-Tooltips
- Templates (sims/template.html + student/teacher-Dashboard)
- Docs: module-interface.md (inkl. 4a Sprachregel, 4b Leichte Sprache,
  4c iPad), content-architecture.md, crash-recovery.md, music-registry.md
- Admin-Infrastruktur: admin-modules.html + api/admin-modules.php
  (Titel, Emoji, Bild, Status, Dauer, Alter pro Modul)
- Inbox-System: _inbox/README.md + _status.md fuer Atlas + Briefings
  an Klima, Glossar, Lehrplan, Fluss
- Zentrale SFX-Pipeline (scripts/generate-sounds.py)
- DALL-E-Bilder: 8 Badges + 5 Glossar-Repraesentationsbilder (Querformat)
- Logo + Inter-Font lokal
- PHP-APIs: admin, glossar, levels, licenses, progress, waypoints,
  assignments, profile, tickets
- Spielsprache entfernt (admin-modules, admin-levels, schueler)
- Landing-Page-Bearbeitungen (Boote sichtbarer, Button-Hintergrund)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:51:14 +02:00
Adminator 2136bbbe62 Glossar: Windrad-Experiment entfernt (redundant mit SVG-Grafik)
Der Windrad-Höhenslider war redundant mit der bestehenden
windrad-groessenvergleich.svg, die denselben Sachverhalt als
statische Vergleichsgrafik bereits zeigt. Entsprechend aus der
Registry und dem Code entfernt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 01:14:16 +02:00
Adminator 22f5e9b686 Glossar: 3 weitere Experimente + Watt-Leiter-Infografik
- Treibhauseffekt-Slider (CO2-ppm 200..800 -> Temperatur-Anomalie via IPCC-Faustformel, Moleküle füllen Atmosphäre dynamisch, Thermometer visualisiert ΔT)
- Windrad-Höhenslider (0..260 m, 7 Vergleichsobjekte Mensch..modernes Windrad leuchten je nach Schieberstellung auf)
- Methan-GWP-Waage (CO2-Schale variabel 1..60 kg, Waage kippt physikalisch bis bei 28 kg Balance erreicht ist)
- kWh-Rennen: Fahrzeuge fahren jetzt von rechts nach links (plattformunabhängige Emoji-Orientierung), Animation auf 6,5s verlangsamt
- Watt-Leiter: neue gemeinsame Infografik (log-Skala 1 W..1 GW mit 6 Haushalts- und Kraftwerks-Beispielen), verknüpft mit watt/kilowatt/megawatt
- CSS für Slider-Experimente (.ge-gh-*), Waagen-Animation, Windrad-Highlighting

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 00:52:39 +02:00
Adminator a425ce31e6 Glossar: Komplettpaket (28 Eintraege, 19 SVGs, 5 DALL-E-Bilder, iPad, Experimente)
- Mindmap mit Force-directed Layout (pages/mindmap.php) + Cross-Links
- 28 Eintraege im Fachartikel-Stil mit 113 Quellen + Inline-Referenzen [^n]
- Leichte-Sprache-Version fuer alle 28 Eintraege (short_easy, text_easy, examples.text_easy)
- 19 Infografiken (12 neu): co2-Keeling, klimawandel-Temp, kohlenstoff-Kreislauf, fotosynthese, energiemix-AT, methan-GWP, photovoltaik, wasserkraft, kipppunkt, biodiversitaet-LPI, permafrost, treibhausgas-GWP + bestehende (treibhauseffekt, ppm, windrad, albedo, fossile, kwh, co2-fussabdruck)
- 5 DALL-E-Reprasentationsbilder im Querformat (nachhaltigkeit, klimaneutralitaet, erneuerbare-energie, emissionen, pariser-abkommen)
- Alphabetische Sprungnavigation A-Z mit Buchstabenbannern
- Lightbox-Vollbild auf allen Infografiken mit expliziten SVG-Dimensionen (Safari/iPad-Fix)
- Easy-Sprache-Toggle pro Card mit localStorage-Persistenz
- 4 interaktive Experimente: kWh-Rennen, ppm-Finder, albedo-Slider, CO2-Fussabdruck-Rechner
- iPad-Optimierung: Touch-Ziele >=38px, Hover in @media (hover), touch-action
- Modul-Metadaten aus DB-Tabelle module_info (keine Hardcodes)
- Cache-Buster auf JS-Asset
- Schema-Erweiterung: glossar_sources, examples.text_easy

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 00:48:29 +02:00
Adminator 003eeeb9f7 Lehrplan: Landing-Diff an Atlas + Glossar-Bedarf + Status
- Nachricht an Atlas: kompletter Code-Block pro FREE-Sim-Card mit
  Card-Split "Mehr erfahren"/"Sofort probieren" inkl. CSS, plus
  Muster für LOCKED-Cards
- Nachricht an Glossar: Begriffs-Wunschliste für Fluss (Priorität 1),
  Heli (Priorität 2), Stadt (Priorität 3), später geplante Module
- _status.md: Arbeitsblock-Abschluss dokumentiert
2026-04-19 00:12:20 +02:00
Adminator 7deec2a26d Lehrplan: Country-Helper + Landeinstellung live
- App/php/lib/Country.php: Kaskade session → classes → teachers →
  cookie → 'AT', Helper current()/flag()/label()/allCountries()
- App/php/api/country.php: GET + POST /api/country
- country-Spalten in teachers, classes, student_sessions (idempotent)
- Native-<select>-Picker im Header aller öffentlichen Seiten
  (modul-*.php, lehrplan.php, simulationen.php)
- Footer-Links "Andere Länder: 🇨🇭 🇩🇪 🇱🇮"
- lehrplan.php liest Default-Filter jetzt aus Country::current()
- iPad-freundlich: Native Select gibt Wheel-Picker auf iOS,
  Touch-Ziel >=40px

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 00:09:43 +02:00
Adminator db9ed43407 Lehrplan: 4 Modul-Detailseiten + lehrplan.php + simulationen.php + 78 Anker
- Gemeinsamer Partial App/pages/_partials/modul_detail.php
- modul-klima/fluss/heli/stadt als 3-Zeilen-Wrapper
- lehrplan.php: Filter Land/Thema/Typ, TOC, URL-Persistenz
- simulationen.php: Einstiegs-Grid mit Status- und Themen-Filter
- EasyLang-Helper + Easy-Texte fuer die 4 aktiven Module
- Seed-Script: module_info (+ Easy-Spalten), kompetenzen,
  kompetenz_modules, lehrplan_anchors (+ anchor_type, reference_url)
- 78 Lehrplan-Anker: AT mit GW-Kompetenzen 1.9/2.1/4.2 etc.,
  13 uebergreifende Themen, andere Faecher (Bio, Chemie, Physik,
  Mathematik, GSP, Bewegung+Sport); DE 10 Bundeslaender-Quellen +
  DGfG; CH Lehrplan 21 (RZG, NT, MA, MI, WAH, ERG, BNE); LI LiLe
- iPad-First: Touch-Ziele >=40px, :active-Feedback, Breakpoint 900px
- Inbox: Konventionen (Instanz-Name, iPad, Leichte Sprache,
  _status.md, Git-Commits) umgesetzt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 00:05:12 +02:00
Adminator dfb9e80664 Klima: Engine extrahiert + 2D refactored + Drama-Track + Bugfixes
- engine.js als headless Single Source of Truth (KlimaEngine)
- game-2d.html nutzt engine (-515 Zeilen Duplikation)
- rising-pressure.mp3 als Drama-Slot, Auto-Switch bei kritischem State
- state.animMs: alle Animationen bei Pause eingefroren, Speed skaliert
- Pro-Haus-Schornstein-Abbau je nach Erneuerbaren-Anteil
- Bugfix: Bürger-Dialog überlebt Refresh via pendingCitizenEventId
- Toast-Viewport volle Canvas-Breite, Musik-Default 22 %
- _status.md Konvention eingeführt, Inbox-Nachrichten erhalten
2026-04-18 23:50:17 +02:00
Adminator b2721d759c Klimawaechter 2D: Canvas-Fix + alle Massnahmen sichtbar + Vulkan flacher
- Canvas-Fix: min-height:450px + overflow:visible (Canvas war unsichtbar)
- Neue Renderer: Radwege (Fahrraeder), Mangroven, Sand-Aufschuettung,
  Kohlekraftwerk, Flughafen (mit fliegendem Flugzeug), Wolkenimpfung
- Vulkan: flacher (120→95), breiter, weiter im Hintergrund
- Eiskegel: breiter, deckt braune Raender komplett ab

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:29:37 +02:00
Adminator 00df599197 fluss.html: Performance-Fixes (distToRiver Cache, eventLog Cap)
- distToRiver: Distance-Cache pro Tick statt O(ROWS) pro Tile pro Frame
- eventLog: Gekappt auf 50 Eintraege
- Reduziert CPU-Last von ~300k Berechnungen/sec auf ~5k/sec

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:38:43 +02:00
Adminator b06aa025c1 Login-Frontend + Lehrer-Dashboard + Stadt-Simulation komplett
- login.html: Schueler-Login (Klassencode+User+PW) + Lehrer-Login/Register
- teacher.html: Vollstaendiges Dashboard (Klassen, Schueler, Module, Stats)
- stadt.html: Komplette Simulation mit 27 Kachel-Typen + Kennwerten
- tile-test.html: Spital-Sackgassen gefixt
- PHP-Pages fuer alle neuen HTML-Seiten

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:37:08 +02:00
Adminator 33fae37838 User Management + Didaktisches Handbuch + Stadt-Editor + Emoji-Kacheln
User Management (Backend):
- schema-v2.sql: students, licenses, student_results, activity_log Tabellen
- auth.php: Lehrer-Registrierung/Login, Schueler-Login, Status-Check
- classes.php: CRUD fuer Klassenverwaltung (mit Join-Code-Generator)
- students.php: Schueler erstellen (einzeln + Batch), bearbeiten, loeschen
- modules.php: Modulfreigabe pro Klasse (locked/free/teacher_started)

Didaktisches Handbuch:
- handbuch.html: Vollstaendiger Lehrpersonen-Guide
- 6 Module detailliert beschrieben (Lernziele, Kompetenzen)
- Vor/Nach-Fragen fuer jede Simulation
- Empfohlener Unterrichtsablauf (45-min-Einheit)
- Differenzierungshinweise, Bewertungsempfehlungen

Stadt-Editor:
- stadt-editor.html: Funktionierendes 8x8 Raster mit Kachel-Platzierung
- 15 Emoji-Gebaude-Kacheln (Kirche, Moschee, Wohngebiet, Fabrik, etc.)
- Spielplatz: Rutsche+Karussell statt schwebende Koepfe
- Hochhaeuser: 6 Gebaeude in 2 Reihen

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 01:52:55 +02:00
Adminator 32ed869f23 Simplify: PHP page dedup, secure UUID, input validation, remove redundant headers
- pages/*.php: 11 files reduced to 1-liners via shared renderPage() helper
- Session: UUID generation uses random_bytes() instead of mt_rand()
- Session: logout cookie uses same security options as create
- API saves: size limits on key (100) and data (500KB)
- API sessions: displayName capped at 64 chars
- API: removed redundant Content-Type headers (Response::json handles it)
- API dashboard: replaced SELECT * with explicit columns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:54:43 +02:00
Adminator f22c5ebbfe Stand 2026-04-13: PHP/MySQL Infrastruktur, Flussmanagement, Stadt-Prototyp
- PHP/MySQL Backend (XAMPP + Produktionsserver)
- Front-Controller, API-Endpunkte, Session-Management
- Flussmanagement-Simulation (Echtzeit, Punkt-basierter Fluss)
- Stadt & Raumplanung (Prototyp, Top-Down Kachelsystem)
- Klimawaechter 3D: Deiche kleiner, Baeume kippen, Budget angepasst
- persistence.ts: Dualer Speicher (localStorage + Server-API)
- 6 Unit-Test-Dateien fuer bestehende Simulationen

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:43:42 +02:00
5817 changed files with 1472757 additions and 3 deletions
+482
View File
@@ -0,0 +1,482 @@
# Briefing · Wal-Modul „Die große Wanderung"
**Für:** Claude Fable 5 (externe Sim-Instanz)
**Auftrag-Datum:** 2026-06-14
**Auftraggeber:** Thomas (Plattform-Owner) + Atlas (Plattform-Instanz)
---
## 0. Worum es geht
Du baust ein neues GeoGraSim-Simulationsmodul für die Lernplattform **geograsim.at**. Sim-ID: **`wal`**. Titel: **„Die große Wanderung"**. Du folgst dem Buckelwal *Megaptera novaeangliae* auf seiner ~6.000 km Migration von Alaska nach Hawaii — als Mutter-Kuh, die in den hawaiianischen Brutgebieten ihr Kalb zur Welt bringen wird.
Zielgruppe: Sek I (1116 Jahre, AT/CH/DE/LI). Dauer ein Durchgang: ca. 35 min. Lehrpersonen setzen es ein in Fächern **Geografie und wirtschaftliche Bildung**, **Biologie / Naturwissenschaft**, **Politische Bildung** und im **Querschnitt BNE** (Bildung für Nachhaltige Entwicklung).
Die Plattform stellt: Wrapper (existiert: `App/pages/wal.php`), DB-Eintrag (existiert: `module_info`), Plattform-Header, Glossar-Tooltip-Library, Quick-Intro-Library, Live-Cockpit-Anbindung, Submit-Endpoint, Persistenz-Pattern, Lehrplan-Anbindung, Design-System.
**Pflicht-Lektüre vor Start:**
1. [`App/docs/sim-spec-2026-06-12.md`](../App/docs/sim-spec-2026-06-12.md) — aktuelle Plattform-Conventions (Window-Globals, Submit, Header-Standard)
2. [`App/docs/module-interface.md`](../App/docs/module-interface.md) — Sprach-Konventionen (Lernarbeit statt Spiel — **kritisch**), iPad-Pattern, Leichte Sprache, Persistenz-Pflicht
3. Diese Datei — Wal-Spezifika und Konzept
---
## 1. Ausgangsmaterial — Thomas' Skizzen
Zwei HTML-Files liegen in `c:\xampp\htdocs\geograsim\.humanInput\wal\` als **Konzept-Skizze**:
- **`wal-simulator-v2(3).html`** — die narrative Rahmensimulation mit Leaflet-Karte und Choice-Mechanik (4 Etappen, 11 Begegnungen, Energy/Health/Stress, Endet mit Geburt in Maui). **Das ist die Basis.**
- **`wal-frachtroute(4).html`** — ein 3D-Action-Minispiel, das die Containerschiff-Begegnung detailliert ausspielt. **Wir bauen das in 2D nach.**
Du darfst und sollst diese Files **als Inhaltsquelle plündern** — Texte, Fakten, Fact-Boxes, Stage-Definitionen sind didaktisch gut und fachlich verifiziert. Plattform-Integration und Welt-Varianten kommen NEU hinzu.
---
## 2. Konzept-Erweiterung — Welt-Varianten (Pflicht)
Statt einer einzigen Reise gibt es **drei Welt-Varianten** entlang derselben Pazifik-Route. Lehrperson kann eine vorgeben (`window.WAL_FORCED_ERA = 1850|1990|2026`), sonst wählt Schüler:in im Sim-Intro.
### Variante 1850 — Vor dem Schutz
- Walfänger-Stationen auf den Aleuten und in Lahaina (Hawaii). Harpunenboote als Antagonisten.
- Krillschwärme dicht, Heringe massenhaft. Wassertemperatur stabil.
- Kein Plastik, kein Sonar, kaum Containerschiffe (Dampfschiff-Frühphase).
- Population global: ~125.000 Buckelwale.
- Begegnungen statt Frachter/Plastik/Sonar: **Harpunenboot · Walfang-Station · Verlorene Walgruppen · Eismeer-Sturm**.
### Variante 1990 — Erholung beginnt
- Walfangmoratorium 4 Jahre alt (seit 1986). Population ~10.00015.000.
- Begegnungen mit anderen Buckelwalen seltener — „Einsamkeit" als didaktisches Element.
- Erste Containerschifffahrt, aber dünn.
- Marine-Sonar gibt's, Geisternetze ja, Plastik wenig.
- Erholungsstory erlebbar — andere Wale gehören zum „seltenen Glück".
### Variante 2026 — Heute
- Population ~80.000, fast erholt.
- Dichter Schiffsverkehr, Mikroplastik, Klimaerwärmung verschiebt Krill polwärts.
- Schutzgebiete + IMO-Routen helfen.
- = das `wal-simulator-v2(3).html` wie es jetzt ist (mit Plattform-Integration und 2D-Minispiel).
**Didaktischer Mehrwert:** Lehrperson lässt Klasse aufteilen — Gruppe A 1850, B 1990, C 2026. Vergleichen in der Reflexion. Sehen: **wir kommen aus einer schlimmen Zeit, wir hatten Erfolg mit Schutz**. BNE-Erfolgsstory ohne 2050-Spekulation.
### Sprache 1850
Darf dramatisch sein, aber Sek-I-tauglich. **Kein Splatter, keine Tier-Quälszenen.** Pattern:
> „Am Horizont, Schwarz auf Grau, ein Walfänger-Mast. Du hörst das Geräusch ihrer Ruder — schwächer als heutige Frachter, aber gezielt. Sie suchen dich."
Konsequenzen wie heute: Stats-Effekte + Fact-Box mit historischem Kontext. Kein Tod auf Bildschirm; Game-Over heißt „Verlorene Reise" wie in 2026 auch.
---
## 3. Technik-Vorgaben
### 3.1 Stack
- **Vanilla HTML/JS/CSS.** Kein Build-Step, kein React, kein TypeScript.
- **2D, nicht 3D.** Begründung: Tiefenachse ist die spannende Dimension (Tauchen unter Schiffen, Krill in der Tiefe) — die zeigt 2D klarer. iPad-Touch ist 2D natürlicher. Three.js-Bundle/WebGL-Abhängigkeiten entfallen. Flat-Scandinavian-Plattform-Ästhetik passt 2D besser.
- **Leaflet** ist OK für die Welt-Karte (siehe wal-simulator-v2 — verwendet das bereits).
- **Canvas 2D** für die Action-Minispiele (Schiffsquerung, Sonar-Ausweichen, evtl. Bubble-Net-Timing).
- **localStorage** für Autosave (Pflicht — siehe module-interface.md §7b).
### 3.2 Plattform-Header (Pflicht — Standard)
```html
<header class="ggs-header">
<a href="/schueler" class="ggs-header-logo">
<img src="/assets/img/bildLogo.png" alt="" class="ggs-header-logo-icon">
<img src="/assets/img/textlogo_geograsim.svg" alt="GeoGraSim" class="ggs-header-logo-text">
</a>
<div class="ggs-header-separator"></div>
<span class="ggs-header-module">Die große Wanderung</span>
<span class="ggs-header-badge" id="era-badge">2026</span>
<div class="ggs-header-spacer"></div>
<div class="ggs-header-actions">
<button class="ggs-btn-ghost" id="btn-info"
title="Anleitung nochmal zeigen"
onclick="if(window.GGS_TUTORIAL&&typeof window.GGS_TUTORIAL.replay==='function')window.GGS_TUTORIAL.replay();"></button>
<a class="ggs-btn-ghost" id="ggs-lehrplan-link" href="#" target="_blank" title="Lehrplan-Bezug">📚</a>
<button class="ggs-btn-ghost" id="btn-mute" title="Ton aus/an">🔊</button>
<button class="ggs-btn-ghost" id="btn-reset" title="Fortschritt zurücksetzen">🔄</button>
<a href="/schueler" class="ggs-btn-ghost" title="Zurück zum Cockpit">🏠</a>
</div>
</header>
```
Stylesheet einbinden: `<link rel="stylesheet" href="/assets/css/design-system.css">`.
### 3.3 Plattform-Globals (was vom Wrapper kommt)
```js
window.WAL_BASE // '/sims/wal/' — Basispfad für deine Assets
window.WAL_SESSION_MODE // 'free' | 'teacher_started'
window.WAL_FORCED_LEVEL // 1|2|3 wenn teacher_started, sonst null
window.WAL_FORCED_ERA // 1850 | 1990 | 2026 | null
window.WAL_SESSION_ID // string oder null
window.WAL_API_BASE // '/php/api'
window.STUDENT_EASY // true wenn Leichte Sprache aktiv
```
**Verhalten:**
- Wenn `WAL_FORCED_ERA` gesetzt → Sim startet direkt in dieser Welt, kein Era-Picker.
- Wenn `null` → Era-Picker als erstes Screen-Element (3 Karten: 1850 / 1990 / 2026).
- Wenn `STUDENT_EASY === true` → vereinfachte Sprache überall (siehe module-interface.md §4b).
### 3.4 Plattform-Globals (was du setzen musst)
```js
// 1) Live-Snapshot fürs Lehrer-Cockpit (alle 4s wird das gepollt)
window.GGS_LIVE_STATE = function () {
if (!gameReady) return null;
return {
simId: 'wal',
era: 1850|1990|2026,
stage: 1|2|3|4, // aktuelle Etappe
stageName: 'Frederick Sound', // Anzeigename
day: 42, // Tage seit Aufbruch
energy: 75,
health: 90,
stress: 30,
hasCalf: false,
progressPct: 45, // 0-100 (Etappe + Index)
health_status: 'good'|'ok'|'struggle', // Cockpit-Status-Punkt
};
};
// 2) Tutorial-Replay-Hook (für -Header-Button)
window.GGS_TUTORIAL = {
simId: 'wal',
title: '🐋 Die große Wanderung',
replay: function () { /* deine startTutorial() */ },
};
```
### 3.5 Submit am Reise-Ende (Pflicht)
```js
fetch(window.WAL_API_BASE + '/progress.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
credentials: 'same-origin',
body: JSON.stringify({
sim_id: 'wal',
action: 'submit_assessment',
data: {
level: eraToLevel(era), // 1850=1, 1990=2, 2026=3
stars: calcStars(), // 1-5
score: calcScore(), // 0-100
duration_ms: (window.__GGS_LIVE__ && window.__GGS_LIVE__.activeMs) || 0,
completed: true,
results: {
era: 1850|1990|2026,
days_total: state.day,
outcome: 'arrival_with_calf'|'arrival_no_calf'|'starvation'|'injury'|'whaling',
energy_end: state.energy,
health_end: state.health,
stress_peak: state.stressPeak,
stages_completed: state.stage,
encounters_total: state.encountersDone,
choices_log: state.choicesLog, // pro Begegnung: {stage, event_id, choice_label}
facts_seen: state.factsRead, // wieviele Fakt-Boxen angezeigt
},
},
}),
});
```
**Wichtig:** `duration_ms` aus `window.__GGS_LIVE__.activeMs` lesen (NICHT eigene Wall-Clock). Sonst zählt Idle mit.
### 3.6 Glossar-Tooltip (in den Begegnungs-Texten verlinken)
Lib einbinden:
```html
<script src="/assets/js/glossar-tooltip.js" defer></script>
<script>
window.addEventListener('DOMContentLoaded', () => {
GlossarTooltip.init({apiBase:'/php/api', moduleId:'wal'});
});
</script>
```
In den Begegnungs-Texten verwendest du:
```html
<span class="ggs-g" data-glossar="bubble-net">Bubble-Net</span>
```
Atlas liefert die Begriffe nach an die Glossar-Instanz. Vorschlag-Liste in §6.
### 3.7 Quick-Intro NICHT verwenden
Wir wollen ein **vollwertiges Tutorial** mit 4-5 Schritten + Bildern (Welt-Variante erklären, Choice-Mechanik, Stats erklären, Minispiele ankündigen). Quick-Intro wäre zu dünn für diese Sim.
### 3.8 Persistenz (Pflicht)
`localStorage`-Key: `ggs-save-wal-{era}` (z.B. `ggs-save-wal-1850`).
- Speichern nach jeder Begegnung + bei Era-Switch.
- Beim Reload: lädt den aktuellen Stand. Reset-Button (🔄) löscht und startet neu (mit Bestätigung).
- Pro Era ein separater Save (Schüler:in kann zwischen Welten wechseln, ohne Stand zu verlieren — relevant für Vergleichs-Reflexion).
Siehe module-interface.md §7b für Vollbeispiel.
### 3.9 iPad-Pattern (Pflicht)
- Touch-Ziele 36-40 px (Header-Buttons sind das schon).
- Choice-Buttons mind. 48 px hoch.
- `font-size: 16px` für Texteingaben (sonst zoomt Safari).
- Canvas-Minispiele mit `touch-action: none`.
- Kein Hover-only — alles per Tap erreichbar.
---
## 4. Inhaltsstruktur (basierend auf Thomas' Vorlage)
### 4.1 Etappen (alle drei Welt-Varianten)
| Etappe | Name | Saison |
|---|---|---|
| 1 | Frederick Sound · Alaska | September |
| 2 | Offener Nordpazifik | Oktober |
| 3 | Subtropischer Übergang | November |
| 4 | Hawaiianische Gewässer | Dezember |
Pro Etappe 2-4 Begegnungen. Endet immer mit Maui + (in 1850 vielleicht abweichend wegen Walfangstation Lahaina) Geburt des Kalbs.
### 4.2 Begegnungs-Matrix nach Welt-Variante
Aus dem Originalkonzept übernehmen + ergänzen:
#### Etappe 1 (Frederick Sound)
- **1850:** Bubble-Net (mit großer Gruppe — Population groß) · Letzte Begegnung Sommer-Gruppe
- **1990:** Bubble-Net (mit *kleiner* Gruppe — Population dünn) · Aufbruch *allein* (Reflexion: „Wo sind die anderen?")
- **2026:** Bubble-Net wie heute · Letzte Begegnung wie heute (das aktuelle Skript)
#### Etappe 2 (Offener Nordpazifik)
- **1850:** **Walfänger-Brigg** (sich annähernde Walfänger statt Frachter) · **Eisberg-Treibhindernis** statt Geisternetz · Sturm · Orcas
- **1990:** **Frachtroute dünn** (1-2 Schiffe pro Tag, ausweichbar) · Geisternetze JA (Hochzeit der Treibnetz-Fischerei) · Sturm · Orcas
- **2026:** Frachtroute dicht (heute) · Geisternetz · Sturm · Orcas
#### Etappe 3 (Subtropischer Übergang)
- **1850:** **Wal-Pod-Begegnung** (große Gruppe — bewahrtes Phänomen) · Sturm-Nachläufer · Kuroshio-Ausläufer
- **1990:** Marine-Sonar (Kalter Krieg!) · Treibmüll-Anfänge (statt voller Müllstrudel) · Kuroshio
- **2026:** Marine-Sonar · Pazifischer Müllstrudel · Kuroshio (das aktuelle Skript)
#### Etappe 4 (Hawaiianische Gewässer)
- **1850:** **Walfang-Station Lahaina am Horizont** · Geburt im versteckten Kanal
- **1990:** Erste Brutgebiete vor Maui (mit Walbeobachtungsbooten als neues Phänomen) · Geburt
- **2026:** Erste Brutgebiete (mit etablierter Walbeobachtung + Schiffsverkehr) · Geburt
### 4.3 Action-Minispiel Frachtroute (2D)
Inspiration: `wal-frachtroute(4).html`**aber in 2D**.
**Mechanik:**
- Seitenansicht: Wasseroberfläche oben, Tiefe unten bis ~150 m.
- Wal-Mutter (große Silhouette) + Wal-Kalb (kleiner, folgt nach).
- Schiffe ziehen von rechts nach links durch den oberen Bereich. Tiefgang 12-15 m sichtbar.
- Player steuert vertikal (↑↓) und horizontal (←→ — leichte Strömung).
- 2 Atem-Bars (Mutter, Kalb) — Auftauchen füllt, Tauchen leert. Kalb leert schneller.
- Progress-Bar oben: Querung der Route (0→100 %).
- Sonnenstrahlen-Effekte zur Tiefe (Vol-Light-Style, einfache lineare Gradienten).
**Verlier-Bedingungen** (wie im Original): Kollision Mutter, Kollision Kalb, Mutter ertrinkt (Atem 0), Kalb ertrinkt.
**Win-Bedingung:** Querung 100 % erreichen.
Die Variante:
- **1850:** Ersetzt durch **Walfänger-Brigg-Begegnung** — kein Tauchspiel, Choice-basiert (sicher umgehen vs. nahe ran), KEIN Minispiel.
- **1990:** Mini-Action wie 2026, aber nur 1-2 Schiffe statt Dauer-Strom.
- **2026:** Voll-Action wie wal-frachtroute, aber 2D.
---
## 5. Cockpit-Sichtbarkeit & Benchmark — was Atlas brauchen wird
Du baust die Sim. Atlas verkabelt teacher.html + Benchmark.php sobald deine results-Felder definiert sind. **Liefere uns:**
### 5.1 LIVE_PRIMARY_FIELDS-Vorschlag
```js
'wal': [
{k:'health_status', label:'Status', align:'center', fmt: v => LIVE_VALUE_LABELS.health[v]},
{k:'era', label:'Welt', align:'center'},
{k:'stage', label:'Etappe'},
{k:'stageName', label:'Ort', align:'left'},
{k:'day', label:'Tag'},
{k:'progressPct', label:'Reise', fmt: v => _liveBar(v, 0, 100, 'mid')},
{k:'energy', label:'⚡ Energie', fmt: v => _liveBar(v, 0, 100, 'high')},
{k:'health', label:'❤️ Gesund.', fmt: v => _liveBar(v, 0, 100, 'high')},
{k:'stress', label:'😖 Stress', fmt: v => _liveBar(v, 0, 100, 'low')},
{k:'hasCalf', label:'🐳 Kalb', align:'center', fmt: v => v ? '✓' : '—'},
],
```
### 5.2 Benchmark-Formel-Vorschlag
```php
private static function scoreWal(array $r): ?int {
$era = (int)($r['era'] ?? 0);
$days = (int)($r['days_total'] ?? 0);
$outcome = $r['outcome'] ?? null;
$energy = (int)($r['energy_end'] ?? 0);
$health = (int)($r['health_end'] ?? 0);
$stressP = (int)($r['stress_peak'] ?? 0);
if (!$outcome) return null;
// Outcome-Basis
$base = match($outcome) {
'arrival_with_calf' => 70,
'arrival_no_calf' => 50,
'whaling' => 0,
'starvation' => 0,
'injury' => 0,
default => 0,
};
// Health- und Energy-Bonus (saubere Wahl)
$bonus = round($health * 0.12) + round($energy * 0.08);
// Stress-Malus (lange Belastung)
$malus = round($stressP * 0.05);
// Zeit-Malus (Zielreise ~90 Tage)
$timePenalty = max(0, ($days - 90)) * 0.3;
return max(0, min(100, (int)round($base + $bonus - $malus - $timePenalty)));
}
```
Begründung: Outcome dominiert (Kalb-Geburt = Hauptlernziel), Health/Energy als Wahlqualität, Stress-Malus für „durch-die-Hölle"-Routen.
---
## 6. Glossar-Begriffe — Atlas reicht das nach an die Glossar-Instanz
Bitte verwende in den Begegnungs-Texten Tooltips für diese Begriffe:
| Slug | Begriff | Wo verwendet |
|---|---|---|
| `bubble-net` | Bubble-Net-Feeding | Etappe 1 Begegnung 1 |
| `migration-orientierung` | Magnetfeld-Sinn / Orientierung | Etappe 2 Intro |
| `frachtroute` | Frachtroute (Pazifik-Hauptrouten) | Etappe 2 Frachter |
| `verkehrstrennungsgebiet` | IMO-Schifffahrtsroute | Etappe 2 Wins-Faktbox |
| `geisternetz` | Verloren gegangenes Fischereinetz | Etappe 2 Müll |
| `bycatch` | Beifang | Etappe 2 Geisternetz |
| `kuroshio-strom` | Kuroshio (warme Pazifikströmung) | Etappe 3 Kuroshio |
| `marine-sonar` | Militärisches Sonar | Etappe 3 Sonar |
| `mikroplastik` | Plastik-Anreicherung im Ozean | Etappe 3 Müll |
| `pazifischer-gyre` | Pazifischer Subtropen-Wirbel | Etappe 3 Müll |
| `walfang-moratorium` | IWC-Moratorium 1986 | Etappe 4 Faktbox + 1990-Intro |
| `iwc` | International Whaling Commission | wo Moratorium erwähnt |
| `walfaengerei-1850` | Walfangindustrie 19. Jh. | 1850-Etappen |
| `brutgebiet` | Hawaii Humpback Marine Sanctuary | Etappe 4 |
| `klimazone-subtropisch` | Subtropische Zone | Etappe 3 Intro |
| `wal-gesang` | Buckelwal-Gesang | Etappe 4 |
| `speck-blubber` | Speck als Energiereserve | Etappe 1 Intro |
| `echolot-tier` | Echo-Orientierung bei Walen | Etappe 2 Frachter Hintergrund |
| `bne` | Bildung für Nachhaltige Entwicklung | Endscreen |
| `lahaina` | Walfang-Station Maui 19. Jh. | 1850 Etappe 4 |
20 Begriffe. Atlas reicht das an die Glossar-Instanz weiter — du kannst die Slugs schon verwenden, Tooltip greift dann auf Fallback zurück bis die DB-Einträge da sind.
---
## 7. Lehrplan-Anker — Atlas reicht das nach an die Lehrplan-Instanz
Wir empfehlen pro Land 8-12 Anker. Vorerst nutzt du `module_kompetenz_map` mit folgenden bekannten Kompetenzen aus dem System:
- **AT GW Sek I:** Kompetenz 1.x (Klimazonen/Wassersysteme), 2.x (globale Stoffströme), 4.x (Belastungsgrenzen), 5.x (Mensch-Natur-Konflikte)
- **CH RZG Sek I:** RZG.3.x (Mensch-Natur-Systeme), RZG.4.x (globale Handelsketten)
- **DE Sek I:** Klimawandel, Welthandel, Globale Tier-Mensch-Konflikte
- **LI:** analog zu DE Berlin/Brandenburg-Pattern
Atlas liefert nach — du musst dazu nichts tun, außer im Modul-Header das 📚-Lehrplan-Link platzieren (das ist im Header-Standard bereits drin).
---
## 8. Dateien, die du anlegen sollst
```
App/sims/wal/
├── game.html ← Hauptdatei (du baust)
├── engine.js ← optional, Sim-Logik separat (empfohlen ab ~500 LoC)
├── assets/
│ ├── img/
│ │ ├── card-wal.png ← schon angefragt, kommt von Atlas
│ │ ├── era-1850.webp ← Onboarding/Era-Picker
│ │ ├── era-1990.webp
│ │ ├── era-2026.webp
│ │ ├── stage1-alaska.webp ← Etappen-Intro-Bilder
│ │ ├── stage2-nordpazifik.webp
│ │ ├── stage3-subtropisch.webp
│ │ └── stage4-hawaii.webp
│ └── audio/ ← optional, Walgesang (NOAA Public Domain)
│ └── humpback-song.mp3 ← falls du Audio-Element nutzt
```
**Bildstil:** Flat Scandinavian (siehe sim-spec-2026-06-12.md §6). DALL-E-Prompt-Block:
```
Flat scandinavian illustration, geometric shapes, soft warm color palette
(#1f4b37 forest, #4a7c4e moss, #e8c547 honey, #e8e4d8 sand background,
#b04a3e coral accent sparingly). No people, no text, no photo-realism.
Clean educational style for children 10-14, Sek I.
[hier deine Szene-Beschreibung]. Square 1024x1024.
```
WebP, max 200 KB pro Bild. Atlas hilft bei Bildgenerierung wenn du DALL-E-Briefings schickst.
---
## 9. Was Atlas am Ende übernimmt (du musst nicht)
1. `LIVE_PRIMARY_FIELDS['wal']` in [`App/teacher.html`](../App/teacher.html) einbauen (deine Vorlage aus §5.1)
2. `Benchmark::scoreWal()` in [`App/php/lib/Benchmark.php`](../App/php/lib/Benchmark.php) integrieren (deine Formel aus §5.2)
3. Glossar-Begriffe an Glossar-Instanz weiterreichen (deine Liste aus §6)
4. Lehrplan-Anker an Lehrplan-Instanz weiterreichen (Atlas-Vorschlag aus §7)
5. Playwright-Test in `.dontDeploy/playwright/tests/stufe1-smoke/`
6. Deploy
7. Status auf `aktiv` schalten nach Test-Welle
---
## 10. Done-Definition
Du gibst zurück:
- [ ] `App/sims/wal/game.html` (+ optional `engine.js`)
- [ ] Sim-eigene Assets in `App/sims/wal/assets/`
- [ ] Eine kurze README im Sim-Ordner: was du implementiert hast, was offen ist
- [ ] Aktualisierte LIVE_PRIMARY_FIELDS-Liste (falls deine results-Felder anders heißen)
- [ ] Aktualisierte Benchmark-Formel (falls deine results-Felder anders heißen)
- [ ] Aktualisierte Glossar-Begriff-Liste (falls du Begriffe ergänzt oder umbenennst)
Die Sim muss:
- [ ] Alle 3 Welt-Varianten spielbar liefern
- [ ] Bei `WAL_FORCED_ERA` direkt in dieser Welt starten
- [ ] Bei `WAL_FORCED_ERA=null` Era-Picker zeigen
- [ ] `GGS_LIVE_STATE` mit allen Feldern aus §3.4 liefern
- [ ] `GGS_TUTORIAL.replay()` registrieren
- [ ] Submit am Reise-Ende abgeben
- [ ] localStorage-Autosave pro Era
- [ ] iPad-funktional sein
- [ ] Sprache: NIEMALS „spielen/Spiel/Spieler". Immer „bearbeiten/Simulation/Lernende". Sek-I-Du-Form.
- [ ] Leichte-Sprache-Variante respektieren wenn `STUDENT_EASY=true`
---
## 11. Wenn du Fragen hast
Bei Plattform-Fragen: Atlas (Plattform-Instanz). Bei inhaltlich-didaktischen Fragen: Thomas. Bei Glossar oder Lehrplan: jeweilige Instanzen (siehe sim-spec-2026-06-12.md §14).
Die Plattform-Standards in sim-spec-2026-06-12.md sind Pflicht — Abweichungen bitte vorher abklären, nicht eigenmächtig „abkürzen". Wenn dir Conventions fehlen oder unklar sind, frag — wir ergänzen die Spec.
**Erfolg: ein didaktisch starkes, fachlich korrektes, iPad-funktionales, persistentes Wal-Migrations-Modul mit drei vergleichbaren Welt-Varianten, das in eine 35-Minuten-Schulstunde passt.**
Viel Erfolg!
— Atlas
+8
View File
@@ -0,0 +1,8 @@
{
"error": {
"message": "Billing hard limit has been reached.",
"type": "billing_limit_user_error",
"param": null,
"code": "billing_hard_limit_reached"
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,430 @@
<?php
// Generator für lehrplan-uebersicht.html
$j = json_decode(file_get_contents(__DIR__ . '/lehrplan-synthese-phase3.json'), true);
$s = json_decode(file_get_contents(__DIR__ . '/_phase3_stats.json'), true);
function e($x) { return htmlspecialchars($x, ENT_QUOTES, 'UTF-8'); }
$sims_meta = [
'busfahrt' => ['title' => 'Busfahrt', 'emoji' => 'BUS'],
'energiemanager' => ['title' => 'Energiemanager', 'emoji' => 'ENERG'],
'eu-werkstatt' => ['title' => 'EU-Werkstatt', 'emoji' => 'EU'],
'farmer' => ['title' => 'Farmer', 'emoji' => 'FARM'],
'fluss' => ['title' => 'Fluss', 'emoji' => 'FLUSS'],
'heli' => ['title' => 'Heli', 'emoji' => 'HELI'],
'klima' => ['title' => 'Klima', 'emoji' => 'KLIMA'],
'klima-3d' => ['title' => 'Klima 3D', 'emoji' => 'K3D'],
'logistik' => ['title' => 'Logistik', 'emoji' => 'LOG'],
'sonnensystem' => ['title' => 'Sonnensystem', 'emoji' => 'SOLAR'],
'weltkueche' => ['title' => 'Weltkueche', 'emoji' => 'KOCH'],
];
$active_sims = array_keys($sims_meta);
$countries = ['AT', 'CH', 'DE', 'LI'];
$country_labels = ['AT' => 'AT', 'CH' => 'CH (SG)', 'DE' => 'DE', 'LI' => 'LI'];
// Top-Anker pro Sim aus Synthese: PRO LAND EINER (max 3 = AT+CH+DE)
// Plus: Anker-Zaehlungen pro Sim x Land fuer die Coverage-Ampel
$top_per_sim = [];
$count_sim_country_new = []; // [sim][country] = anzahl
$count_sim_country_existing = []; // bestand aus DB (wird unten gefuellt)
foreach ($active_sims as $sim) {
$top_per_sim[$sim] = ['AT' => null, 'CH' => null, 'DE' => null];
$count_sim_country_new[$sim] = ['AT' => 0, 'CH' => 0, 'DE' => 0, 'LI' => 0];
$count_sim_country_existing[$sim] = ['AT' => 0, 'CH' => 0, 'DE' => 0, 'LI' => 0];
}
foreach ($j['anchors_neu'] as $a) {
foreach ($a['sims'] as $sim) {
if (!isset($top_per_sim[$sim])) continue;
$c = $a['country'];
if (isset($count_sim_country_new[$sim][$c])) {
$count_sim_country_new[$sim][$c]++;
}
// pro Land den ersten Anker als Top-Beispiel speichern
if (in_array($c, ['AT', 'CH', 'DE']) && $top_per_sim[$sim][$c] === null) {
$top_per_sim[$sim][$c] = [
'country' => $c,
'title' => $a['title'],
'reference' => $a['reference'],
'kompetenz' => $a['kompetenz_zuordnung'],
'neu' => true,
];
}
}
}
// Bestand aus DB
try {
require_once 'C:/xampp/htdocs/geograsim/App/php/config/db.php';
$db = getDB();
$stmt = $db->query("SELECT km.module_id, la.country, COUNT(DISTINCT la.id) AS n
FROM lehrplan_anchors la
JOIN kompetenz_modules km ON la.kompetenz_id = km.kompetenz_id
GROUP BY km.module_id, la.country");
foreach ($stmt as $r) {
if (isset($count_sim_country_existing[$r['module_id']][$r['country']])) {
$count_sim_country_existing[$r['module_id']][$r['country']] = (int)$r['n'];
}
}
} catch (Exception $e) { /* lokal not available, fallback 0 */ }
// Ampel-Funktion: Anker-Zahl -> Farbe + Status
// Annahme: pro Land+Sim ist ein guter Abdeckungsgrad >= 4 Anker, OK >= 2, duenn 1, leer 0
function coverage_status($total) {
if ($total >= 6) return ['gut', '🟢', '#4a7c4e', '#e3e9d6'];
if ($total >= 3) return ['ok', '🟡', '#a37e1a', '#fff7e0'];
if ($total >= 1) return ['duenn', '🟠', '#9c4d2e', '#fde0db'];
return ['leer', '🔴', '#9c2e2e', '#fbcfcf'];
}
// Lücken aus den 3 JSON-Files
$gaps = [
[
'thema' => 'Plantage / Monokultur (Begriff)',
'sim_abgedeckt' => 'JA: Farmer-Sim modelliert Cluster-Risiko bei Monokultur; weltkueche zeigt Plantage-Klimabilanz.',
'lehrplan' => 'AT: nicht woertlich. BU 4. Kl. AT spricht von "Charakteristika der konventionellen Landwirtschaft". DE: NRW IF6 + BY-Gym-Geo10-LB7 nennen "Plantagenwirtschaft" woertlich. CH: WAH/RZG.3.2 nennt "Plantage" als Produktionsform.',
'status' => 'teilweise abgedeckt — AT-Lehrplan-Luecke. DE+CH wortwoertlich.',
],
[
'thema' => 'Fair Trade / fairer Handel (Begriff)',
'sim_abgedeckt' => 'NEIN — kein Sim-Konzept dafuer. Sinngemaess: weltkueche Klimabilanz + Farmer Importpreise.',
'lehrplan' => 'Begriff in keinem der Lehrplaene woertlich (AT/CH/DE). Sinngemaess ueber Globalisierungsbereiche.',
'status' => 'Sim-Luecke — Vorschlag: Fair-Trade-Badges in weltkueche-Rezepten.',
],
[
'thema' => 'Klimazonen explizit (Koeppen-Geiger)',
'sim_abgedeckt' => 'JA: Farmer (Sahel-Cluster, Tropen), klima (Klimazonen-Karte), klima-3d.',
'lehrplan' => 'AT: Begriff "Klimazonen" nicht woertlich im GW Sek I. DE: NRW IF5 "Klimazonen der Erde" + BY-Rs-Geo7 "Klima-/Vegetationszonen". CH: RZG.1.2 "Landschaftszonen" + "Kalte/gemaessigte/Subtropen-/Tropenzone".',
'status' => 'AT-Luecke. DE+CH stark verankert. Sim deckt es ab.',
],
[
'thema' => 'EU-Institutionen (Kommission, Parlament, Ministerrat) namentlich',
'sim_abgedeckt' => 'JA: EU-Werkstatt thematisiert Buergerinitiative — Institutionsnamen jedoch implizit.',
'lehrplan' => 'AT: GW 4.2 nennt nur "vier Grundfreiheiten, Euro, Bildungskooperationen". GPB 4. Kl. spricht von EU als Wirtschafts-/Friedensprojekt. Keine Institutionsnamen woertlich. DE: NRW IF7 + BY-Gym-Geo10 thematisieren EU-Politik. CH: RZG.8.3 spricht nur abstrakt von "europaeischer Einigung" + Schweiz-Position.',
'status' => 'Plattform-weite Luecke — kein Lehrplan nennt Kommission/Parlament/Rat woertlich. Atlas-Empfehlung: EU-Werkstatt kann Institutionsnamen einfuehren als Plus.',
],
[
'thema' => 'Systemisches Denken explizit',
'sim_abgedeckt' => 'JA: Plattform-Kompetenz #46 systemisches-denken existiert; logistik + klima nutzen sie.',
'lehrplan' => 'AT: GW 4.3 "Wandel der Machtverhaeltnisse zwischen Staaten ... NGOs, Konzernen". DE: DGfG-F2 "Zusammenwirken von Geofaktoren ... als System darstellen"; NRW SK1 "Geofaktoren-Zusammenwirken". CH: keine eigene Kompetenz, implizit in RZG.3.x.',
'status' => 'DE explizit, AT sinngemaess, CH implizit.',
],
[
'thema' => 'Logistik / Disposition als LP-Anker',
'sim_abgedeckt' => 'JA: logistik-Sim Kern.',
'lehrplan' => 'AT: GW 2.2 + GW 3.3 (Import/Export). DE: NRW IF10 "Just-in-time-Produktion" + DGfG-F3 "Welthandel". CH: WAH.2.2 "globale Warenstroeme, Import/Export, Logistik" woertlich.',
'status' => 'Stark verankert. Plattform-Kompetenz #43 wirtschaft-logistikverstaendnis matcht direkt.',
],
[
'thema' => 'Welternaehrung & Disparitaeten',
'sim_abgedeckt' => 'JA: Weltkueche + Farmer.',
'lehrplan' => 'AT: EH 2. Kl. + UT 13 + GW 1.3. DE: NRW IF7 + BY-Gym-Geo10-LB2 + BY-Rs-Geo9. CH: WAH.4.5 + NMG.7.4 + RZG.2.2.',
'status' => 'In ALLEN drei Laendern explizit. Loest neue Plattform-Kompetenz #108 welternaehrung-konsum-disparitaeten aus.',
],
[
'thema' => 'Plantage / Monokultur als Sim-Mechanik',
'sim_abgedeckt' => 'JA: Farmer-Cluster-Risiko, Schaedlingsanfaelligkeit. Welternaehrung-Sim Rezepte.',
'lehrplan' => 'Siehe oben.',
'status' => 'Sim deckt es voll ab.',
],
];
ob_start();
?><!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Lehrplan-Coverage Phase 3 · GeoGraSim</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;background:#f5f2eb;color:#2a2a2a;line-height:1.5;padding:24px;max-width:1400px;margin:0 auto}
h1{color:#1f4e5a;font-size:1.6rem;margin-bottom:4px}
.sub{font-size:.82rem;color:#8a8a8a;margin-bottom:18px}
h2{color:#1f4e5a;font-size:1.15rem;margin:28px 0 10px;padding-bottom:6px;border-bottom:2px solid #4a7c8a;display:flex;align-items:center;gap:8px}
h3{color:#4a7c8a;font-size:.98rem;margin:18px 0 8px}
table{width:100%;border-collapse:collapse;font-size:.84rem;background:#fff;border-radius:6px;overflow:hidden;margin-bottom:10px}
th,td{text-align:left;padding:7px 10px;border-bottom:1px solid #e0ddd4;vertical-align:top}
th{background:#1f4e5a;color:#fff;font-weight:600;font-size:.74rem;text-transform:uppercase;letter-spacing:.04em}
tr:hover{background:#fafaf5}
tr:last-child td{border-bottom:none}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums;font-weight:600}
td.num .delta{color:#4a7c4e;font-size:.78em;margin-left:4px}
td.num .delta.zero{color:#bbb}
td.zero{color:#cfcfcf}
code{background:#f5f2eb;padding:2px 6px;border-radius:3px;font-family:'JetBrains Mono',Consolas,monospace;font-size:.85em}
a{color:#1f4e5a}
.badge{display:inline-block;font-size:.68rem;font-weight:700;padding:2px 7px;border-radius:9999px;margin-right:4px}
.b-new{background:#e3e9d6;color:#4a6b3f}
.b-existing{background:#d6e3e9;color:#1f4e5a}
.b-country-at{background:#fde0db;color:#9c4d2e}
.b-country-ch{background:#fff7e0;color:#8a6516}
.b-country-de{background:#d6e3e9;color:#1f4e5a}
.b-country-li{background:#e0ddd4;color:#666}
.top-nav{background:#fff;border:1px solid #e0ddd4;border-radius:8px;padding:10px 14px;margin-bottom:18px;font-size:.86rem;display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.top-nav a{text-decoration:none;padding:5px 11px;border-radius:6px;color:#1f4e5a}
.top-nav a:hover{background:#f5f2eb}
.top-nav a.active{background:#1f4e5a;color:#fff}
.info-box{background:#fff7e0;border-left:4px solid #d2a24c;padding:12px 16px;margin:14px 0;border-radius:0 6px 6px 0;font-size:.88rem}
.stat-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-bottom:14px}
.stat-card{background:#fff;border-radius:8px;padding:12px 16px;border:1px solid #e0ddd4}
.stat-card .label{font-size:.72rem;text-transform:uppercase;color:#888;font-weight:700;letter-spacing:.04em}
.stat-card .value{font-size:1.8rem;font-weight:800;color:#1f4e5a;line-height:1.1;margin-top:2px}
.stat-card .delta{font-size:.85rem;color:#4a7c4e;font-weight:600}
.sim-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px;margin-top:14px}
.sim-card{background:#fff;border:1px solid #e0ddd4;border-radius:8px;padding:14px 16px}
.sim-card h4{color:#1f4e5a;font-size:.98rem;margin-bottom:8px;display:flex;align-items:center;gap:8px}
.sim-card .counts{font-size:.78rem;color:#666;margin-bottom:10px}
.sim-card .counts b{color:#1f4e5a}
.sim-card ul{list-style:none;padding:0;font-size:.84rem}
.sim-card li{padding:6px 0;border-bottom:1px dotted #e0ddd4}
.sim-card li:last-child{border-bottom:none}
.gap-list .gap-item{background:#fff;border:1px solid #e0ddd4;border-left:4px solid #d2a24c;border-radius:0 6px 6px 0;padding:12px 16px;margin-bottom:10px}
.gap-item h4{color:#1f4e5a;font-size:.95rem;margin-bottom:6px}
.gap-item .gap-line{font-size:.84rem;margin-top:4px}
.gap-item .gap-line b{color:#1f4e5a}
.gap-item .status{display:inline-block;font-size:.72rem;font-weight:700;padding:3px 10px;border-radius:9999px;background:#fff7e0;color:#8a6516;margin-top:6px}
.gap-item .status.full{background:#e3e9d6;color:#4a6b3f}
.gap-item .status.partial{background:#fde0db;color:#9c4d2e}
.matrix-table th.col-sim,.matrix-table td.col-sim{background:#fafaf5;font-weight:600;color:#1f4e5a}
.matrix-table td.cell{text-align:center;font-variant-numeric:tabular-nums}
.matrix-table td.cell.zero{color:#cfcfcf}
.matrix-table td.cell .new{display:inline-block;color:#4a7c4e;font-weight:700;font-size:.78em;margin-left:3px}
details{background:#fff;border:1px solid #e0ddd4;border-radius:6px;margin-bottom:8px}
details summary{padding:10px 14px;cursor:pointer;font-weight:600;color:#1f4e5a;font-size:.92rem}
details[open] summary{border-bottom:1px solid #e0ddd4}
details > div{padding:0 14px 12px}
.note{font-size:.78rem;color:#888;font-style:italic;margin-top:6px}
</style>
</head>
<body>
<nav class="top-nav">
<strong>Entwurfswerkzeug</strong>
<a href="index.html">Uebersicht</a>
<a href="../entwurf.html">Entwurf</a>
<a href="../review.html">Review</a>
<a href="accounts.html">Accounts</a>
<a href="standards.html">Standards</a>
<a href="lehrplan-uebersicht.html" class="active">Lehrplan-Coverage</a>
<a href="../../App/admin-sitemap.html" target="_blank">Sitemap</a>
</nav>
<h1>Lehrplan-Coverage Phase 3</h1>
<p class="sub">Stand: 2026-06-08 · Synthese-Snapshot AT/CH/DE/LI · Quelle: <code>.dontDeploy/entwurfswerkzeug/lehrplan-synthese-phase3.json</code></p>
<div style="background:#fff;border-radius:8px;padding:14px 18px;margin:14px 0;border:1px solid #e0ddd4">
<h3 style="color:#1f4e5a;margin:0 0 8px;font-size:1rem">📖 Was du auf dieser Seite siehst — kurz erklärt</h3>
<ul style="font-size:.9rem;line-height:1.6;margin:0;padding-left:1.4em;color:#3a3a3a">
<li><b>Anker</b> = ein wörtlicher Verweis aus einem Schul-Lehrplan auf eines unserer Sim-Themen.
Beispiel: AT-Lehrplan GW Kompetenz 3.3 sagt wörtlich „Import/Export — Außenwirtschaft Österreichs". Das ist ein Anker für unsere Busfahrt/Logistik/Weltküche-Sims.
Jeder Anker hat Land · Fach · Klassenstufe · wörtliches Zitat · Quelle-URL.</li>
<li><b>Plattform-Kompetenz</b> = einer von 14 (mit der neuen 15) intern definierten Sammelbegriffen wie „Klimawandel", „Welthandel", „Kartenkompetenz". Jede Sim ist mehreren zugeordnet. Anker werden indirekt über die Plattform-Kompetenz mit der Sim verknüpft.</li>
<li><b>Coverage / Abdeckung</b> = wie viele Anker wir pro Sim und pro Land haben. Wir zeigen <b>keine Prozente</b>, weil es keine geschlossene Soll-Liste „Lehrplan = 100 Anker" gibt. Stattdessen <b>Ampel</b>:
<span style="background:#e3e9d6;color:#4a6b3f;padding:2px 8px;border-radius:9999px;font-weight:700;font-size:.8em">🟢 gut ≥ 6</span>
<span style="background:#fff7e0;color:#a37e1a;padding:2px 8px;border-radius:9999px;font-weight:700;font-size:.8em">🟡 ok 35</span>
<span style="background:#fde0db;color:#9c4d2e;padding:2px 8px;border-radius:9999px;font-weight:700;font-size:.8em">🟠 dünn 12</span>
<span style="background:#fbcfcf;color:#9c2e2e;padding:2px 8px;border-radius:9999px;font-weight:700;font-size:.8em">🔴 leer 0</span></li>
<li><b>Bestand vs. neu:</b> „Bestand" = was schon in der DB war. „Neu" = was Phase-3-Recherche zusätzlich vorschlägt (noch nicht in DB — muss erst per SQL-Migration übernommen werden).</li>
</ul>
</div>
<div class="info-box">
<b>Quellen:</b> Bestand-Zahlen aus DB (<code>lehrplan_anchors</code>, <?=count($s['komp_titles'])-1?> Plattform-Kompetenzen).
Neu-Zahlen aus Phase-2-Synthese: AT-RIS BGBl. II 1/2023 (BMBWF Sek I + VS), CH-LP21 (Sankt Gallen-Quelle <code>sg.lehrplan.ch</code>), DE-DGfG/NRW/Niedersachsen/Bayern (alles wörtlich aus PDFs extrahiert).
</div>
<h2>Gesamt-Statistik vor und nach Phase 3</h2>
<div class="stat-cards">
<div class="stat-card">
<div class="label">Anker AT (bestand &rarr; neu)</div>
<div class="value"><?=$s['bestand_per_country']['AT']?> &rarr; <?=$s['bestand_per_country']['AT']+($s['neu_per_country']['AT']??0)?></div>
<div class="delta">+<?=$s['neu_per_country']['AT']??0?> aus BGBl. II 1/2023</div>
</div>
<div class="stat-card">
<div class="label">Anker CH (bestand &rarr; neu)</div>
<div class="value"><?=$s['bestand_per_country']['CH']?> &rarr; <?=$s['bestand_per_country']['CH']+($s['neu_per_country']['CH']??0)?></div>
<div class="delta">+<?=$s['neu_per_country']['CH']??0?> aus LP21 (SG)</div>
</div>
<div class="stat-card">
<div class="label">Anker DE (bestand &rarr; neu)</div>
<div class="value"><?=$s['bestand_per_country']['DE']?> &rarr; <?=$s['bestand_per_country']['DE']+($s['neu_per_country']['DE']??0)?></div>
<div class="delta">+<?=$s['neu_per_country']['DE']??0?> DGfG/NRW/NI/BY</div>
</div>
<div class="stat-card">
<div class="label">Anker LI</div>
<div class="value"><?=$s['bestand_per_country']['LI']?> &rarr; <?=$s['bestand_per_country']['LI']?></div>
<div class="delta" style="color:#888">unveraendert</div>
</div>
<div class="stat-card">
<div class="label">Total Anker</div>
<div class="value"><?=array_sum($s['bestand_per_country'])?> &rarr; <?=array_sum($s['bestand_per_country'])+array_sum($s['neu_per_country'])?></div>
<div class="delta">+<?=array_sum($s['neu_per_country'])?> Anker</div>
</div>
<div class="stat-card">
<div class="label">Plattform-Kompetenzen</div>
<div class="value">14 &rarr; 15</div>
<div class="delta">+1 (welternaehrung-konsum-disparitaeten)</div>
</div>
</div>
<h2>Sim &times; Land Matrix</h2>
<p class="sub">Anker pro Sim und Land. Format: <code>bestand</code> mit <span style="color:#4a7c4e;font-weight:700">+neu</span>.</p>
<table class="matrix-table">
<thead>
<tr>
<th class="col-sim">Sim</th>
<?php foreach ($countries as $c): ?>
<th class="num"><?=e($country_labels[$c])?></th>
<?php endforeach; ?>
<th class="num">Sum bestand</th>
<th class="num">Sum neu</th>
</tr>
</thead>
<tbody>
<?php foreach ($active_sims as $sim):
$cells = $s['sim_land_matrix'][$sim];
$sum_b = 0; $sum_n = 0;
foreach ($countries as $c) { $sum_b += $cells[$c]['bestand']; $sum_n += $cells[$c]['neu']; }
?>
<tr>
<td class="col-sim"><?=e($sims_meta[$sim]['title'])?></td>
<?php foreach ($countries as $c): $b = $cells[$c]['bestand']; $n = $cells[$c]['neu']; ?>
<td class="cell <?=($b==0 && $n==0)?'zero':''?>"><?=$b?><?php if($n>0): ?><span class="new">+<?=$n?></span><?php endif; ?></td>
<?php endforeach; ?>
<td class="num"><?=$sum_b?></td>
<td class="num"><span class="delta">+<?=$sum_n?></span></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<h2>Plattform-Kompetenz &times; Land Matrix</h2>
<p class="sub">Anker pro Plattform-Kompetenz und Land.</p>
<table class="matrix-table">
<thead>
<tr>
<th class="col-sim">Kompetenz</th>
<?php foreach ($countries as $c): ?>
<th class="num"><?=e($country_labels[$c])?></th>
<?php endforeach; ?>
<th class="num">Sum bestand</th>
<th class="num">Sum neu</th>
</tr>
</thead>
<tbody>
<?php foreach ($s['komp_titles'] as $kt):
$kid = (int)$kt['id'];
if (!isset($s['komp_land_matrix'][$kid])) {
// neue Kompetenz 108
$s['komp_land_matrix'][$kid] = ['AT'=>['bestand'=>0,'neu'=>0],'CH'=>['bestand'=>0,'neu'=>0],'DE'=>['bestand'=>0,'neu'=>0],'LI'=>['bestand'=>0,'neu'=>0]];
// Neu-Zaehlung aus Synthese
foreach ($j['anchors_neu'] as $a) {
if ($a['kompetenz_zuordnung'] === $kt['key_slug']) {
$s['komp_land_matrix'][$kid][$a['country']]['neu']++;
}
}
}
$cells = $s['komp_land_matrix'][$kid];
$sum_b = 0; $sum_n = 0;
foreach ($countries as $c) { $sum_b += $cells[$c]['bestand']; $sum_n += $cells[$c]['neu']; }
?>
<tr>
<td class="col-sim"><?=e($kt['title'])?> <?php if($kid==108): ?><span class="badge b-new">NEU</span><?php endif; ?><br><span class="note"><code><?=e($kt['key_slug'])?></code></span></td>
<?php foreach ($countries as $c): $b = $cells[$c]['bestand']; $n = $cells[$c]['neu']; ?>
<td class="cell <?=($b==0 && $n==0)?'zero':''?>"><?=$b?><?php if($n>0): ?><span class="new">+<?=$n?></span><?php endif; ?></td>
<?php endforeach; ?>
<td class="num"><?=$sum_b?></td>
<td class="num"><span class="delta">+<?=$sum_n?></span></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<h2>Pro Sim — Abdeckung mit Ampel-System</h2>
<p class="sub">Pro Sim und Land ein Beispiel-Anker (AT/CH/DE) — plus Ampel für die Gesamtzahl der Anker pro Land. <b>🟢 grün</b> ≥ 6 Anker (gut abgedeckt) · <b>🟡 gelb</b> 3-5 (ok) · <b>🟠 orange</b> 1-2 (dünn) · <b>🔴 rot</b> 0 (gar nicht).</p>
<div class="sim-grid">
<?php foreach ($active_sims as $sim):
$cells = $s['sim_land_matrix'][$sim];
$sum_b = 0; $sum_n = 0;
foreach ($countries as $c) { $sum_b += $cells[$c]['bestand']; $sum_n += $cells[$c]['neu']; }
?>
<div class="sim-card">
<h4><?=e($sims_meta[$sim]['title'])?></h4>
<div class="counts">bestand insgesamt: <b><?=$sum_b?></b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+<?=$sum_n?></b></div>
<?php // Ampel-Zeile pro Land mit Beispiel-Anker ?>
<?php foreach (['AT', 'CH', 'DE'] as $cc):
$total_for_country = $cells[$cc]['bestand'] + $cells[$cc]['neu'];
[$status, $emoji, $color, $bg] = coverage_status($total_for_country);
$example = $top_per_sim[$sim][$cc] ?? null;
?>
<div style="background:<?=$bg?>;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:<?=$color?>">
<span style="font-size:1.1em"><?=$emoji?></span>
<span><?=$cc?> — <?=$total_for_country?> Anker (<?=$status?>)</span>
<?php if ($cells[$cc]['neu'] > 0): ?><span style="margin-left:auto;color:#4a7c4e;font-weight:700">+<?=$cells[$cc]['neu']?> neu</span><?php endif; ?>
</div>
<?php if ($example): ?>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> <?=e($example['title'])?>
<div class="note" style="margin-top:2px"><?=e($example['reference'])?></div>
</div>
<?php elseif ($total_for_country == 0): ?>
<div style="font-size:.75rem;color:#666;margin-top:4px;font-style:italic">kein Anker — Lehrplan-Lücke oder Sim noch nicht zugeordnet</div>
<?php else: ?>
<div style="font-size:.75rem;color:#666;margin-top:4px;font-style:italic">Anker bereits in der DB (siehe /lehrplan-Page)</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
<h2>Themen-Luecken und Sim-Abdeckung</h2>
<p class="sub">Lehrplan-Begriffe, die in Phase 1 oder 2 als "nicht woertlich abgedeckt" gemeldet wurden — mit Sim-Querverweis.</p>
<div class="gap-list">
<?php foreach ($gaps as $g): ?>
<div class="gap-item">
<h4><?=e($g['thema'])?></h4>
<div class="gap-line"><b>Sim-Abdeckung:</b> <?=e($g['sim_abgedeckt'])?></div>
<div class="gap-line"><b>Lehrplan-Verankerung:</b> <?=e($g['lehrplan'])?></div>
<div class="status <?=(strpos(strtolower($g['status']),'voll')!==false?'full':(strpos(strtolower($g['status']),'teil')!==false?'partial':''))?>"><?=e($g['status'])?></div>
</div>
<?php endforeach; ?>
</div>
<h2>Methodik und Quellen</h2>
<details>
<summary>Datenquellen, Validierungsstatus, Fall-Back-Hinweise</summary>
<div>
<h3>AT — BGBl. II 1/2023</h3>
<p>RIS Rechtsinformationssystem des Bundes. Direkter PDF-Download + pdftotext-Extraktion. Wortwoertliche Zitate. 31 neue Anker.</p>
<p class="note">Quick-Fixes: 9 Subject-Korrekturen (5x "Politische Bildung" -> "Geschichte und Politische Bildung — Querschnitt", 4x "Informatische Bildung" -> "Digitale Grundbildung"). Optional: 33 GW-Anker mit Subject "GW" -> "Geografie und wirtschaftliche Bildung".</p>
<h3>CH — Lehrplan 21 / Kanton St. Gallen</h3>
<p>Quelle: <code>sg.lehrplan.ch</code> (Live-Auftritt Kanton St. Gallen, bestaetigt 2026-06-08 via WebSearch). LP21-Texte national einheitlich.</p>
<p class="note">URL-Verifikation: WebFetch HTTP 403 (Bot-Block) — wie bei v-fe.lehrplan.ch und gr.lehrplan.ch. URL-Schema (Codes b|id|4 fuer Fachbereiche) identisch. Bei manueller Pruefung von Thomas: 1:1-Match zur Stamm-LP21-Struktur erwartet. 41 neue Anker.</p>
<h3>DE — DGfG / NRW / NI / BY</h3>
<p>4 Quellen:</p>
<ul style="padding-left:18px">
<li><b>DGfG Bildungsstandards 2020</b> (10. Aufl., VGDH-PDF): 11 Anker (10 verwertet, DGfG-M2 als Querschnitt weggelassen)</li>
<li><b>NRW Kernlehrplan G9 Erdkunde 2019</b>: 14 Anker</li>
<li><b>Niedersachsen Kerncurriculum Gymnasium Erdkunde</b>: 7 Anker</li>
<li><b>Bayern LehrplanPLUS (Gym 5/7/10 + RS 7/8/9)</b>: 15 Anker</li>
</ul>
<p class="note">Quick-Fix: 4 Anker mit DGfG-Tot-URL <code>geographie.de/publikationen/</code> auf <code>vgdh.de/wp-content/uploads/2023/08/Bildungsstandards_Geographie_2020_Web.pdf</code> aktualisiert.</p>
<h3>Neue Plattform-Kompetenz</h3>
<p><code>welternaehrung-konsum-disparitaeten</code> — konsolidiert die AT-Vorschlagskompetenz "ernaehrungs-und-konsumkompetenz" und die DE-Vorschlagskompetenz "welternaehrung-disparitaeten" zu einer Plattform-Kompetenz mit wortwoertlichen Belegen aus EH 2. Kl. AT, UT 13 AT, NRW IF7, BY-Gym-Geo10-LB2, BY-Rs-Geo9, WAH.2.2 CH, WAH.4.5 CH, NMG.7.4 CH.</p>
</div>
</details>
</body>
</html>
<?php
$html = ob_get_clean();
file_put_contents(__DIR__ . '/lehrplan-uebersicht.html', $html);
echo "HTML geschrieben: " . strlen($html) . " Bytes\n";
@@ -0,0 +1,200 @@
<?php
// Generator für die Phase-3-Migration. Liest lehrplan-synthese-phase3.json und schreibt SQL.
$j = json_decode(file_get_contents(__DIR__ . '/lehrplan-synthese-phase3.json'), true);
function sqlstr($s) {
if ($s === null) return 'NULL';
return "'" . str_replace(["\\", "'"], ["\\\\", "''"], $s) . "'";
}
$out = '';
$out .= "-- =================================================================\n";
$out .= "-- Lehrplan-Erweiterung Phase 3 — ENTWURF (NICHT auto-ausführen)\n";
$out .= "-- Erstellt: 2026-06-08 (Atlas)\n";
$out .= "-- Quelle: .dontDeploy/entwurfswerkzeug/lehrplan-synthese-phase3.json\n";
$out .= "--\n";
$out .= "-- Inhalt:\n";
$out .= "-- 1. Neue Plattform-Kompetenz welternaehrung-konsum-disparitaeten\n";
$out .= "-- 2. Quick-Fixes Phase 1 (4 DE-URLs + 9 AT-Subjects)\n";
$out .= "-- 3. Optional: plattformweites GW-Fach-Update (33 Anker)\n";
$out .= "-- 4. 118 neue Anker (AT 31 + CH 41 + DE 46)\n";
$out .= "-- 5. kompetenz_modules-Verknuepfungen (weltkueche + 3 sek.)\n";
$out .= "--\n";
$out .= "-- Idempotenz: alle INSERTs verwenden ON DUPLICATE KEY UPDATE oder INSERT IGNORE.\n";
$out .= "-- Mehrfach-Ausfuehrung veraendert die DB ab dem zweiten Lauf nicht.\n";
$out .= "--\n";
$out .= "-- ACHTUNG: Vor Punkt 4 muss EINMALIG ein UNIQUE-Index angelegt werden:\n";
$out .= "-- ALTER TABLE lehrplan_anchors\n";
$out .= "-- ADD UNIQUE KEY uniq_anchor_identity (country, reference(120), kompetenz_id);\n";
$out .= "-- Ohne diesen Index sind die folgenden Anker-INSERTs nicht idempotent.\n";
$out .= "-- =================================================================\n\n";
$out .= "START TRANSACTION;\n\n";
// ===== 1. Neue Plattform-Kompetenz =====
$out .= "-- -----------------------------------------------------------------\n";
$out .= "-- 1. Neue Plattform-Kompetenz: welternaehrung-konsum-disparitaeten\n";
$out .= "-- -----------------------------------------------------------------\n";
$k = $j['meta']['neu_konsolidierte_plattform_kompetenz'];
$out .= "INSERT INTO kompetenzen (key_slug, title, description, category, age_min, age_max, sort_order)\n";
$out .= "VALUES (\n";
$out .= " " . sqlstr($k['key_slug']) . ",\n";
$out .= " " . sqlstr($k['title']) . ",\n";
$out .= " " . sqlstr($k['description']) . ",\n";
$out .= " " . sqlstr($k['category']) . ",\n";
$out .= " " . (int)$k['age_min'] . ", " . (int)$k['age_max'] . ", 108\n";
$out .= ") ON DUPLICATE KEY UPDATE\n";
$out .= " title = VALUES(title),\n";
$out .= " description = VALUES(description),\n";
$out .= " category = VALUES(category),\n";
$out .= " age_min = VALUES(age_min),\n";
$out .= " age_max = VALUES(age_max);\n\n";
$out .= "-- Variable fuer spaetere Anker-INSERTs:\n";
$out .= "SET @welternaehrung_id := (SELECT id FROM kompetenzen WHERE key_slug = 'welternaehrung-konsum-disparitaeten');\n\n";
// ===== 2. Quick-Fixes =====
$out .= "-- -----------------------------------------------------------------\n";
$out .= "-- 2. Quick-Fixes aus Phase 1\n";
$out .= "-- -----------------------------------------------------------------\n";
$qf1 = $j['quick_fix_details']['url_dgfg_alt_zu_vgdh'];
$out .= "-- 2.1: 4 DE-DGfG-URLs auf VGDH-PDF korrigieren (Phase 1: url_dead)\n";
$ids = implode(',', $qf1['betroffene_ids']);
$out .= "UPDATE lehrplan_anchors\n";
$out .= " SET reference_url = " . sqlstr($qf1['neuer_url']) . "\n";
$out .= " WHERE id IN (" . $ids . ")\n";
$out .= " AND reference_url = " . sqlstr($qf1['alter_url']) . ";\n";
$out .= "-- Vorher pruefen: SELECT id, reference_url FROM lehrplan_anchors WHERE id IN (" . $ids . ");\n\n";
$qf2 = $j['quick_fix_details']['subject_politische_bildung'];
$out .= "-- 2.2: 5 AT-Anker Politische Bildung -> Querschnitt-Kennzeichnung\n";
$ids2 = implode(',', $qf2['betroffene_ids']);
$out .= "UPDATE lehrplan_anchors\n";
$out .= " SET subject = " . sqlstr($qf2['neuer_subject']) . "\n";
$out .= " WHERE id IN (" . $ids2 . ")\n";
$out .= " AND subject = 'Politische Bildung';\n\n";
$qf3 = $j['quick_fix_details']['subject_informatische_bildung'];
$out .= "-- 2.3: 4 AT-Anker Informatische Bildung -> Digitale Grundbildung\n";
$ids3 = implode(',', $qf3['betroffene_ids']);
$out .= "UPDATE lehrplan_anchors\n";
$out .= " SET subject = " . sqlstr($qf3['neuer_subject']) . "\n";
$out .= " WHERE id IN (" . $ids3 . ")\n";
$out .= " AND subject = 'Informatische Bildung';\n\n";
// ===== 3. Optionales GW-Update =====
$out .= "-- -----------------------------------------------------------------\n";
$out .= "-- 3. OPTIONAL: Plattformweites GW-Fach-Update (33 AT-Anker)\n";
$out .= "-- -----------------------------------------------------------------\n";
$out .= "-- Vorher-Vergleich (fuer Review):\n";
$out .= "-- SELECT COUNT(*) AS gw_count FROM lehrplan_anchors WHERE subject='GW' AND country='AT';\n";
$out .= "-- Erwartet: 33 (Stand 2026-06-08)\n";
$out .= "-- Idempotenz: nach erstem Lauf 0 Treffer, da WHERE subject=GW.\n";
$out .= "--\n";
$out .= "-- Auskommentiert — bei Bedarf manuell aktivieren:\n";
$out .= "-- UPDATE lehrplan_anchors\n";
$out .= "-- SET subject = 'Geografie und wirtschaftliche Bildung'\n";
$out .= "-- WHERE subject = 'GW' AND country = 'AT';\n";
$out .= "--\n";
$out .= "-- Nachher-Vergleich:\n";
$out .= "-- SELECT COUNT(*) AS gw_count_after FROM lehrplan_anchors WHERE subject='GW' AND country='AT';\n";
$out .= "-- Erwartet: 0\n";
$out .= "-- SELECT COUNT(*) AS gwb_count FROM lehrplan_anchors\n";
$out .= "-- WHERE subject='Geografie und wirtschaftliche Bildung' AND country='AT';\n";
$out .= "-- Erwartet: 33\n\n";
// ===== 4. Neue Anker =====
$out .= "-- -----------------------------------------------------------------\n";
$out .= "-- 4. Neue Lehrplan-Anker (118 Datensaetze: AT 31 + CH 41 + DE 46)\n";
$out .= "-- -----------------------------------------------------------------\n";
$out .= "-- Identitaets-Match: (country, reference, kompetenz_id).\n";
$out .= "-- Bei Re-Ausfuehrung wird der Datensatz aktualisiert (URL/Quote/Title).\n\n";
$komp_map = [
'klimawandel' => 1,
'naturgefahren-hochwasser' => 2,
'karten-navigation-alpen' => 3,
'stadtplanung-raum' => 4,
'raeumliche-orientierung-europa' => 41,
'kartenkompetenz-logistik' => 42,
'wirtschaft-logistikverstaendnis' => 43,
'planungs-entscheidungskompetenz' => 44,
'verkehrserziehung-sicherheit' => 45,
'systemisches-denken' => 46,
'energieversorgung-speicherung' => 57,
'erdmechanik-klimazonen' => 80,
'eu-politische-beteiligung' => 93,
'landwirtschaft-klimazonen' => 107,
];
$idx = 0;
$total = count($j['anchors_neu']);
foreach ($j['anchors_neu'] as $a) {
$idx++;
$slug = $a['kompetenz_zuordnung'];
if ($slug === 'welternaehrung-konsum-disparitaeten') {
$komp_expr = '@welternaehrung_id';
} else {
if (!isset($komp_map[$slug])) {
$out .= "-- WARN: unbekannter Kompetenz-Slug $slug fuer " . $a['_source'] . " — uebersprungen\n\n";
continue;
}
$komp_expr = (string)$komp_map[$slug];
}
$out .= "-- [" . $idx . "/" . $total . "] " . $a['_source'] . " -> " . $slug . "\n";
$out .= "INSERT INTO lehrplan_anchors\n";
$out .= " (kompetenz_id, country, anchor_type, region, school_type, grade, subject, title, reference, reference_url, quote, sort_order)\n";
$out .= "VALUES (\n";
$out .= " " . $komp_expr . ",\n";
$out .= " " . sqlstr($a['country']) . ",\n";
$out .= " " . sqlstr($a['anchor_type']) . ",\n";
$out .= " " . ($a['region'] === null ? 'NULL' : sqlstr($a['region'])) . ",\n";
$out .= " " . sqlstr($a['school_type']) . ",\n";
$out .= " " . sqlstr($a['grade']) . ",\n";
$out .= " " . sqlstr($a['subject']) . ",\n";
$out .= " " . sqlstr($a['title']) . ",\n";
$out .= " " . sqlstr($a['reference']) . ",\n";
$out .= " " . sqlstr($a['reference_url']) . ",\n";
$out .= " " . sqlstr($a['quote']) . ",\n";
$out .= " " . (int)$a['sort_order'] . "\n";
$out .= ") ON DUPLICATE KEY UPDATE\n";
$out .= " anchor_type = VALUES(anchor_type),\n";
$out .= " region = VALUES(region),\n";
$out .= " school_type = VALUES(school_type),\n";
$out .= " grade = VALUES(grade),\n";
$out .= " subject = VALUES(subject),\n";
$out .= " title = VALUES(title),\n";
$out .= " reference_url = VALUES(reference_url),\n";
$out .= " quote = VALUES(quote),\n";
$out .= " sort_order = VALUES(sort_order);\n\n";
}
// ===== 5. kompetenz_modules =====
$out .= "-- -----------------------------------------------------------------\n";
$out .= "-- 5. kompetenz_modules-Verknuepfungen\n";
$out .= "-- -----------------------------------------------------------------\n";
foreach ($j['kompetenz_modules_neu'] as $km) {
$slug = $km['kompetenz_id_oder_slug'];
if ($slug === 'welternaehrung-konsum-disparitaeten') {
$kid_expr = '@welternaehrung_id';
} else {
if (!isset($komp_map[$slug])) continue;
$kid_expr = (string)$komp_map[$slug];
}
$out .= "INSERT IGNORE INTO kompetenz_modules (kompetenz_id, module_id, level, strength)\n";
$out .= "VALUES (" . $kid_expr . ", " . sqlstr($km['module_id']) . ", " . (int)$km['level'] . ", " . sqlstr($km['strength']) . ");\n";
}
$out .= "\n";
$out .= "COMMIT;\n\n";
$out .= "-- =================================================================\n";
$out .= "-- Erwartete Statistik nach Migration:\n";
$out .= "-- SELECT country, COUNT(*) FROM lehrplan_anchors GROUP BY country;\n";
$out .= "-- AT 134 (103+31), CH 57 (16+41), DE 64 (18+46), LI 7. Total: 262.\n";
$out .= "-- SELECT COUNT(*) FROM kompetenzen;\n";
$out .= "-- Vorher 14 + 1 neu = 15.\n";
$out .= "-- =================================================================\n";
file_put_contents(__DIR__ . '/../../App/Don_t_Deploy/2026-06-08-lehrplan-erweiterung-phase3.sql', $out);
echo "SQL geschrieben: " . strlen($out) . " Bytes\n";
File diff suppressed because one or more lines are too long
+372
View File
@@ -0,0 +1,372 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Test-Accounts · GeoGraSim</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;background:#f5f2eb;color:#2a2a2a;line-height:1.5;padding:24px;max-width:1300px;margin:0 auto}
h1{color:#1f4e5a;font-size:1.5rem;margin-bottom:4px}
.sub{font-size:.82rem;color:#8a8a8a;margin-bottom:14px}
h2{color:#1f4e5a;font-size:1.1rem;margin:24px 0 8px;padding-bottom:4px;border-bottom:2px solid #4a7c8a;display:flex;align-items:center;gap:8px}
h3{color:#4a7c8a;font-size:.95rem;margin:14px 0 6px}
table{width:100%;border-collapse:collapse;font-size:.84rem;background:#fff;border-radius:6px;overflow:hidden;margin-bottom:8px}
th,td{text-align:left;padding:6px 10px;border-bottom:1px solid #e0ddd4;vertical-align:top}
th{background:#1f4e5a;color:#fff;font-weight:600;font-size:.74rem;text-transform:uppercase;letter-spacing:.04em}
tr:hover{background:#fafaf5}
tr:last-child td{border-bottom:none}
code{background:#f5f2eb;padding:2px 6px;border-radius:3px;font-family:'JetBrains Mono',Consolas,monospace;font-size:.85em}
a{color:#1f4e5a}
.pw{font-family:'JetBrains Mono',Consolas,monospace;font-weight:700;color:#9c4d2e}
.pw.empty{color:#bbb;font-weight:400;font-style:italic}
.badge{display:inline-block;font-size:.68rem;font-weight:700;padding:2px 7px;border-radius:9999px;margin-right:4px}
.b-local{background:#d6e3e9;color:#1f4e5a}
.b-prod{background:#fde0db;color:#9c4d2e}
.b-admin{background:#e8c547;color:#5a4416}
.b-school{background:#e3e9d6;color:#4a6b3f;font-weight:500}
.top-nav{background:#fff;border:1px solid #e0ddd4;border-radius:8px;padding:10px 14px;margin-bottom:18px;font-size:.86rem;display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.top-nav a{text-decoration:none;padding:5px 11px;border-radius:6px;color:#1f4e5a}
.top-nav a:hover{background:#f5f2eb}
.top-nav a.active{background:#1f4e5a;color:#fff}
.grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;align-items:start}
.info-box{background:#fff7e0;border-left:4px solid #d2a24c;padding:12px 16px;margin:14px 0;border-radius:0 6px 6px 0;font-size:.88rem}
.stat-row{display:flex;gap:18px;font-size:.84rem;color:#666;margin-bottom:8px;flex-wrap:wrap}
.stat-row b{color:#1f4e5a}
.login-link{font-size:.74rem;color:#4a7c8a;text-decoration:none;background:#f5f2eb;padding:2px 6px;border-radius:4px}
.login-link:hover{background:#1f4e5a;color:#fff}
details{background:#fff;border:1px solid #e0ddd4;border-radius:6px;margin-bottom:8px}
details summary{padding:10px 14px;cursor:pointer;font-weight:600;color:#1f4e5a;font-size:.92rem}
details[open] summary{border-bottom:1px solid #e0ddd4}
details > div{padding:0 14px 12px}
@media (max-width:900px){.grid{grid-template-columns:1fr}}
</style>
</head>
<body>
<nav class="top-nav">
<strong>🛠 Entwurfswerkzeug</strong>
<a href="index.html">Übersicht</a>
<a href="../entwurf.html">Entwurf</a>
<a href="../review.html">Review</a>
<a href="accounts.html" class="active">🔑 Accounts</a>
<a href="standards.html">📐 Standards</a>
<a href="legacy/" style="color:#6b6448">📂 legacy/</a>
<a href="toDelete/" style="color:#9c4d2e">📂 toDelete/</a>
<a href="../../App/admin-sitemap.html" target="_blank">📊 Sitemap</a>
</nav>
<h1>🔑 Test-Accounts</h1>
<p class="sub">Schnellzugang für Test-Logins (Lehrer:innen + Schüler:innen) · Stand: 2026-06-08</p>
<!-- ⭐ ANGEHEFTET — Schroffi-Klasse ganz oben für sofortigen Zugriff -->
<div style="background:linear-gradient(135deg,#fde0db 0%,#fff7e0 100%);border:3px solid #c85c4a;border-radius:12px;padding:18px 22px;margin:14px 0 22px;box-shadow:0 4px 14px rgba(200,92,74,.18)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
<span style="font-size:1.6rem"></span>
<span style="font-size:1.1rem;font-weight:800;color:#9c4d2e">Schroffis Online-Testklasse</span>
<span style="font-size:.74rem;background:#c85c4a;color:#fff;padding:3px 10px;border-radius:9999px;font-weight:700;letter-spacing:.05em">PROD · DIE BRAUCHE ICH OFT</span>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px">
<div>
<div style="font-size:.72rem;text-transform:uppercase;color:#8a6516;font-weight:700;letter-spacing:.05em;margin-bottom:4px">Lehrer-Login</div>
<div style="font-family:'JetBrains Mono',Consolas,monospace;font-size:.95rem;font-weight:700;color:#9c4d2e;background:#fff;padding:8px 12px;border-radius:6px;border:1px solid #c85c4a">
schroffenegger@ph-vorarlberg.ac.at
</div>
<div style="font-size:.74rem;color:#8a6516;margin-top:3px">id 107 · intern <code>schroffenegger_359</code></div>
</div>
<div>
<div style="font-size:.72rem;text-transform:uppercase;color:#8a6516;font-weight:700;letter-spacing:.05em;margin-bottom:4px">Klassen-Code (Schüler-Login)</div>
<div style="font-family:'JetBrains Mono',Consolas,monospace;font-size:1.4rem;font-weight:800;color:#9c4d2e;background:#fff;padding:6px 14px;border-radius:6px;border:1px solid #c85c4a;letter-spacing:.1em;text-align:center">
LCVZHN
</div>
<div style="font-size:.74rem;color:#8a6516;margin-top:3px">„Meine Klasse" · 4 Schüler:innen</div>
</div>
</div>
<table style="margin-top:14px;background:#fff">
<thead><tr><th>Username</th><th>Passwort</th><th>Display</th><th>Letzter Login</th></tr></thead>
<tbody>
<tr><td><code style="font-size:.95rem;font-weight:700">Lena</code></td><td class="pw" style="font-size:.95rem">fnahc</td><td>Lenchen</td><td>2026-05-29 09:42</td></tr>
<tr><td><code style="font-size:.95rem;font-weight:700">Teresa</code></td><td class="pw" style="font-size:.95rem">fnahc</td><td>Tesa</td><td><i style="color:#888">nie</i></td></tr>
<tr><td><code style="font-size:.95rem;font-weight:700">bubi</code></td><td class="pw" style="font-size:.95rem">fnahc</td><td></td><td><i style="color:#888">nie</i></td></tr>
<tr><td><code style="font-size:.95rem;font-weight:700">Franz</code></td><td class="pw" style="font-size:.95rem">fnahc</td><td></td><td><i style="color:#888">nie</i></td></tr>
</tbody>
</table>
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:10px">
<a href="https://geograsim.at/login" target="_blank" style="background:#c85c4a;color:#fff;text-decoration:none;border-radius:6px;padding:8px 14px;font-weight:700;font-size:.86rem">👩‍🏫 Lehrer-Login</a>
<a href="https://geograsim.at/login" target="_blank" style="background:#fff;color:#9c4d2e;text-decoration:none;border-radius:6px;padding:8px 14px;font-weight:700;font-size:.86rem;border:1.5px solid #c85c4a">🧑‍🎓 Schüler-Login (Code LCVZHN)</a>
<a href="https://geograsim.at/teacher" target="_blank" style="background:#fff;color:#9c4d2e;text-decoration:none;border-radius:6px;padding:8px 14px;font-weight:700;font-size:.86rem;border:1.5px solid #c85c4a">🔴 Live-Cockpit</a>
</div>
</div>
<div class="info-box">
<b>So funktioniert Schüler-Login:</b>
Schüler:innen melden sich mit <code>Klassen-Code</code> (z.B. <code>JAKOB1</code>) + Username + (Plaintext-)Passwort an.
Wenn das Passwort <span class="pw empty">NULL</span> ist, verwendet die Klasse <i>Anonymous-Pattern</i> — kein Passwort,
nur Klassen-Code + Username. (<code>students.is_anonymous=1</code>).
</div>
<!-- ================== LOKAL ================== -->
<h2><span class="badge b-local">LOKAL</span> XAMPP-Dev-DB</h2>
<div class="stat-row">
<span>👩‍🏫 <b>7</b> Lehrer:innen</span>
<span>🏫 <b>5</b> Klassen</span>
<span>🧑‍🎓 <b>16</b> Schüler:innen</span>
<span><a href="http://localhost/geograsim/App/login.html" target="_blank" class="login-link">→ Login-Seite</a></span>
</div>
<h3>Lehrer:innen — lokal</h3>
<table>
<thead><tr><th>id</th><th>Username</th><th>E-Mail</th><th>Display-Name</th><th>Schule</th><th>Login</th></tr></thead>
<tbody>
<tr><td>1</td><td><code>demo</code></td><td></td><td>Demo-Lehrkraft</td><td></td><td><a class="login-link" href="http://localhost/geograsim/App/login.html?user=demo" target="_blank"></a></td></tr>
<tr><td>2</td><td><code>test_928</code></td><td>test@schule.at</td><td>Test Lehrer</td><td>Test Schule</td><td></td></tr>
<tr><td>99</td><td><code>testlehrer</code></td><td>test@geograsim.at</td><td>Test-Lehrkraft</td><td>Testschule</td><td></td></tr>
<tr><td>100</td><td><code>neu_915</code></td><td>neu@schule.at</td><td>Neuer Lehrer</td><td>Schule X</td><td></td></tr>
<tr><td>101</td><td><code>lernapps</code></td><td>lernapps@oead.at</td><td>OeAD Lernapps</td><td>OeAD</td><td></td></tr>
<tr><td>102</td><td><code>jakob</code> <span class="badge b-school">AT</span></td><td>jakob@geograsim.at</td><td>Jakob</td><td></td><td></td></tr>
<tr><td>103</td><td><code>v2admin</code> <span class="badge b-admin">ADMIN</span></td><td>v2admin@local</td><td>V2 Admin</td><td>Lokale V2-Test-Schule</td><td></td></tr>
</tbody>
</table>
<h3>Klassen + Schüler:innen — lokal</h3>
<details open>
<summary>Klasse <code>JAKOB1</code> · „Jakob 1" · 3 Schüler:innen · Lehrer <code>jakob</code></summary>
<div>
<table>
<thead><tr><th>Username</th><th>Passwort</th><th>Display-Name</th></tr></thead>
<tbody>
<tr><td><code>Alexom</code></td><td class="pw">3322</td><td>Alexom</td></tr>
<tr><td><code>Berturam</code></td><td class="pw">7788</td><td>Berturam</td></tr>
<tr><td><code>Sussama</code></td><td class="pw">9991</td><td>Sussama</td></tr>
</tbody>
</table>
</div>
</details>
<details>
<summary>Klasse <code>TEST01</code> · „Test" · 3 Schüler:innen · Lehrer <code>testlehrer</code></summary>
<div>
<table>
<thead><tr><th>Username</th><th>Passwort</th><th>Display-Name</th></tr></thead>
<tbody>
<tr><td><code>anna</code></td><td class="pw empty">NULL (anonym)</td><td>Anna</td></tr>
<tr><td><code>ben</code></td><td class="pw empty">NULL</td><td>Ben</td></tr>
<tr><td><code>clara</code></td><td class="pw empty">NULL</td><td>Clara</td></tr>
</tbody>
</table>
</div>
</details>
<details>
<summary>Klasse <code>P9BNKR</code> · „2B" · 3 Schüler:innen · Lehrer <code>test_928</code></summary>
<div>
<table>
<thead><tr><th>Username</th><th>Passwort</th><th>Display-Name</th></tr></thead>
<tbody>
<tr><td><code>schueler01</code></td><td class="pw empty">NULL (anonym)</td><td>schueler01</td></tr>
<tr><td><code>schueler02</code></td><td class="pw empty">NULL</td><td>schueler02</td></tr>
<tr><td><code>schueler03</code></td><td class="pw empty">NULL</td><td>schueler03</td></tr>
</tbody>
</table>
</div>
</details>
<details>
<summary>Klasse <code>V2DEMO</code> · „V2-Demo 1A" · 7 Schüler:innen · Lehrer <code>v2admin</code></summary>
<div>
<table>
<thead><tr><th>Username</th><th>Passwort</th><th>Display-Name</th></tr></thead>
<tbody>
<tr><td><code>lina</code></td><td class="pw empty">NULL</td><td>Lina</td></tr>
<tr><td><code>max</code></td><td class="pw empty">NULL</td><td>Max</td></tr>
<tr><td><code>mia</code></td><td class="pw empty">NULL</td><td>Mia</td></tr>
<tr><td><code>moxo</code></td><td class="pw empty">NULL</td><td>Moxomilian Jackbiel</td></tr>
<tr><td><code>sofia</code></td><td class="pw empty">NULL</td><td>Sofia</td></tr>
<tr><td><code>tim</code></td><td class="pw empty">NULL</td><td>Tim</td></tr>
<tr><td><code>yusuf</code></td><td class="pw empty">NULL</td><td>Yusuf</td></tr>
</tbody>
</table>
</div>
</details>
<details>
<summary>Klasse <code>GEO1A</code> · „1A" · 0 Schüler:innen · Lehrer <code>demo</code></summary>
<div><p class="sub">Leere Demo-Klasse.</p></div>
</details>
<!-- ================== PROD ================== -->
<h2><span class="badge b-prod">PROD</span> geograsim.at</h2>
<div class="stat-row">
<span>👩‍🏫 <b>17</b> Lehrer:innen</span>
<span>🏫 <b>11</b> Klassen</span>
<span>🧑‍🎓 <b>~70</b> Schüler:innen</span>
<span><a href="https://geograsim.at/login" target="_blank" class="login-link">→ Prod-Login</a></span>
</div>
<!-- ⭐ Schroffi-Eigene Test-Klasse — prominent gehighlightet -->
<div style="background:#fff7e0;border:2px solid #d2a24c;border-radius:8px;padding:14px 18px;margin:14px 0">
<h3 style="color:#8a6516;margin:0 0 8px;font-size:1rem">⭐ Meine Online-Testklasse (Thomas Schroffenegger, PH Vorarlberg)</h3>
<table style="margin:0;background:transparent">
<tbody>
<tr><th style="background:transparent;color:#8a6516;width:130px">Lehrer-Login</th>
<td><code>schroffenegger@ph-vorarlberg.ac.at</code> · <span class="sub" style="color:#8a6516;font-size:.78rem">(intern: <code>schroffenegger_359</code> · id 107)</span></td></tr>
<tr><th style="background:transparent;color:#8a6516">Klassen-Code</th>
<td><code style="font-size:1.05rem;font-weight:700;color:#9c4d2e">LCVZHN</code> · „Meine Klasse"</td></tr>
<tr><th style="background:transparent;color:#8a6516">Schüler-Login</th>
<td>Username + Passwort <code class="pw">fnahc</code> für alle 4</td></tr>
</tbody>
</table>
<table style="margin-top:8px">
<thead><tr><th>Username</th><th>Passwort</th><th>Display</th><th>Letzter Login</th></tr></thead>
<tbody>
<tr><td><code>Lena</code></td><td class="pw">fnahc</td><td>Lenchen</td><td>2026-05-29 09:42</td></tr>
<tr><td><code>Teresa</code></td><td class="pw">fnahc</td><td>Tesa</td><td><i style="color:#888">nie</i></td></tr>
<tr><td><code>bubi</code></td><td class="pw">fnahc</td><td></td><td><i style="color:#888">nie</i></td></tr>
<tr><td><code>Franz</code></td><td class="pw">fnahc</td><td></td><td><i style="color:#888">nie</i></td></tr>
</tbody>
</table>
<p class="sub" style="margin-top:6px;color:#8a6516">
🔗 Schüler-Login-URL: <a href="https://geograsim.at/login" target="_blank">geograsim.at/login</a> → Klassen-Code <code>LCVZHN</code>
</p>
</div>
<h3>Lehrer:innen — Prod (Stand 2026-06-08)</h3>
<table>
<thead><tr><th>id</th><th>Username</th><th>E-Mail</th><th>Display-Name</th><th>Schule</th></tr></thead>
<tbody>
<tr><td>1</td><td><code>demo</code></td><td></td><td>Demo-Lehrkraft</td><td></td></tr>
<tr><td>2</td><td><code>demo_563</code></td><td>demo@test.at</td><td>Demo Lehrer</td><td>Demo Schule</td></tr>
<tr><td>99</td><td><code>testlehrer</code></td><td>test@geograsim.at</td><td>Test-Lehrkraft</td><td>Testschule</td></tr>
<tr><td>100</td><td><code>test2_502</code></td><td>test2@geograsim.at</td><td>Reg Test</td><td>Schule</td></tr>
<tr><td>101</td><td><code>oem_767</code></td><td>oem@gmx.at</td><td>Thomas Schroffenegger</td><td>MS Dornbirn</td></tr>
<tr><td>102</td><td><code>konzett_998</code></td><td>konzett@msls.edu.dornbirn.at</td><td>Meenu Konzett</td><td>MS Lustenauerstrasse, Dornbirn</td></tr>
<tr><td>103</td><td><code>lernapps</code></td><td>lernapps@oead.at</td><td>OeAD Lernapps</td><td>OeAD</td></tr>
<tr><td>104</td><td><code>schroffi_650</code></td><td>schroffi@gmx.at</td><td>Harald Emmerich</td><td>vsm</td></tr>
<tr><td>105</td><td><code>thomas.schroffenegger_485</code></td><td>thomas.schroffenegger@ph-vorarlberg.ac.at</td><td>Thomas Schroffenegger</td><td>PH Vorarlberg</td></tr>
<tr><td>106</td><td><code>jakob</code></td><td>jakob@geograsim.at</td><td>Jakob</td><td></td></tr>
<tr><td>107</td><td><code>schroffenegger_359</code></td><td>schroffenegger@ph-vorarlberg.ac.at</td><td>schroffenegger</td><td></td></tr>
<tr><td>108</td><td><code>qpv-e25-073_251</code></td><td>qpv-e25-073@oead.at</td><td>qpv-e25-073</td><td></td></tr>
<tr><td>109</td><td><code>olivia.kaesche_683</code></td><td>olivia.kaesche@ruetischule.ch</td><td>olivia.kaesche</td><td></td></tr>
<tr><td>110</td><td><code>helmut.pecher_215</code></td><td>helmut.pecher@kphvie.ac.at</td><td>Helmut Pecher</td><td>PVS Strebersdorf</td></tr>
</tbody>
</table>
<h3>Klassen + Schüler:innen — Prod</h3>
<details open>
<summary>Klasse <code>K9FQ7R</code> · „1A" · 25 Schüler:innen · Lehrer <code>lernapps</code> (OeAD)</summary>
<div>
<p class="sub">OeAD-Lernapps Test-Klasse mit 4-stelligen Plaintext-Passwörtern.</p>
<table>
<thead><tr><th>Username</th><th>PW</th><th>Username</th><th>PW</th><th>Username</th><th>PW</th><th>Username</th><th>PW</th><th>Username</th><th>PW</th></tr></thead>
<tbody>
<tr>
<td><code>LernApp01</code></td><td class="pw">6746</td>
<td><code>LernApp02</code></td><td class="pw">8199</td>
<td><code>LernApp03</code></td><td class="pw">3187</td>
<td><code>LernApp04</code></td><td class="pw">7391</td>
<td><code>LernApp05</code></td><td class="pw">9111</td>
</tr>
<tr>
<td><code>LernApp06</code></td><td class="pw">6052</td>
<td><code>LernApp07</code></td><td class="pw">6628</td>
<td><code>LernApp08</code></td><td class="pw">5070</td>
<td><code>LernApp09</code></td><td class="pw">9488</td>
<td><code>LernApp10</code></td><td class="pw">5079</td>
</tr>
<tr>
<td><code>LernApp11</code></td><td class="pw">9578</td>
<td><code>LernApp12</code></td><td class="pw">3053</td>
<td><code>LernApp13</code></td><td class="pw">8932</td>
<td><code>LernApp14</code></td><td class="pw">2570</td>
<td><code>LernApp15</code></td><td class="pw">4978</td>
</tr>
<tr>
<td><code>LernApp16</code></td><td class="pw">1761</td>
<td><code>LernApp17</code></td><td class="pw">6538</td>
<td><code>LernApp18</code></td><td class="pw">6298</td>
<td><code>LernApp19</code></td><td class="pw">8051</td>
<td><code>LernApp20</code></td><td class="pw">1178</td>
</tr>
<tr>
<td colspan="10" style="font-style:italic;color:#888;padding:8px">+ LernApp2125 (Passwörter via SSH abrufbar)</td>
</tr>
</tbody>
</table>
</div>
</details>
<details>
<summary>Klasse <code>VRGETF</code> · „Meine Klasse" · 25 Schüler:innen · Lehrer <code>thomas.schroffenegger_485</code> (PH Vorarlberg)</summary>
<div>
<p class="sub">25 Schüler <code>schueler01</code> bis <code>schueler25</code> — alle Passwörter <code>NULL</code> (anonym).</p>
</div>
</details>
<details>
<summary>Klasse <code>TEST01</code> · „Test" · 3 Schüler:innen · Lehrer <code>testlehrer</code></summary>
<div>
<table>
<thead><tr><th>Username</th><th>Passwort</th><th>Display</th></tr></thead>
<tbody>
<tr><td><code>anna</code></td><td class="pw empty">NULL (anonym)</td><td>Anna</td></tr>
<tr><td><code>ben</code></td><td class="pw empty">NULL</td><td>Ben</td></tr>
<tr><td><code>clara</code></td><td class="pw empty">NULL</td><td>Clara</td></tr>
</tbody>
</table>
</div>
</details>
<details>
<summary>Klasse <code>8MY62C</code> · „1a" · 3 Schüler:innen · Lehrer <code>oem_767</code> (MS Dornbirn)</summary>
<div>
<p class="sub"><code>schueler01</code> bis <code>schueler03</code> — alle NULL.</p>
</div>
</details>
<details>
<summary>Klasse <code>JAKOB1</code> · „Jakob 1" · 3 Schüler:innen · Lehrer <code>jakob</code></summary>
<div><p class="sub">Spiegel der lokalen Klasse (Alexom/Berturam/Sussama).</p></div>
</details>
<details>
<summary>Klasse <code>LCVZHN</code> · „Meine Klasse" · 4 Schüler:innen · Lehrer <code>schroffenegger_359</code> (PH Vorarlberg)</summary>
<div><p class="sub">Klein-Setup, schueler0104, alle NULL.</p></div>
</details>
<details>
<summary>Klasse <code>MLVJBU</code> · „Meine Klasse" · 5 Schüler:innen · Lehrer <code>qpv-e25-073_251</code> (OeAD)</summary>
<div><p class="sub">5 Schüler:innen, vermutlich anonym.</p></div>
</details>
<details>
<summary>Klassen ohne Schüler:innen</summary>
<div>
<table>
<thead><tr><th>Code</th><th>Klasse</th><th>Lehrer</th></tr></thead>
<tbody>
<tr><td><code>GEO1A</code></td><td>1A</td><td>demo</td></tr>
<tr><td><code>BY66A9</code></td><td>1b</td><td>konzett_998 (MS Lustenauerstrasse)</td></tr>
<tr><td><code>YTU7H9</code></td><td>Meine Klasse</td><td>olivia.kaesche_683 (Rütischule CH)</td></tr>
<tr><td><code>Z8PKJN</code></td><td>Meine Klasse</td><td>helmut.pecher_215 (PVS Strebersdorf)</td></tr>
</tbody>
</table>
</div>
</details>
<div class="info-box" style="margin-top:24px">
<b>🔄 Daten neu ziehen:</b><br>
<code>"c:/xampp/php/php.exe" -r "..."</code> für lokal (siehe Memory),
und <code>ssh -i ~/.ssh/openclaw root@85.215.155.82 'docker exec webstack-mysql sh -c ...'</code> für Prod.<br>
Datei wird beim nächsten Sweep automatisch durch Atlas aktualisiert — bei Fragen einfach
<i>Update accounts.html</i>" sagen.
</div>
<footer style="margin:24px 0;text-align:center;font-size:.75rem;color:#8a8a8a">
Liegt unter <code>.dontDeploy/entwurfswerkzeug/accounts.html</code> · wird nicht deployed.
</footer>
</body>
</html>
@@ -0,0 +1,144 @@
{"id":1137,"country":"AT","subject":"Bewegung und Sport","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Bewegung und Sport — Orientierungslauf, Wandern","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Bewegung und Sport","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Orientierungslauf und Wandern mit Karte und Kompass sind praktische Anwendungen der Kartenkompetenz und Teil des Bewegung-und-Sport-Lehrplans.","kompetenz_id":3}
{"id":1224,"country":"AT","subject":"Bewegung und Sport","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Bewegung und Sport — Tourenplanung, Wetterbeobachtung, Sicherheit am Berg","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Bewegung und Sport","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Im Bereich „Bewegung im Freien\" werden Tourenplanung, Wetterbeurteilung, Lawinen- und Wegekunde behandelt. Die Heli-Simulation macht greifbar, was passiert, wenn diese Vorbereitung versagt — und warum Bergrettung immer ein Team-Einsatz ist.","kompetenz_id":3}
{"id":1095,"country":"AT","subject":"Biologie und Umweltbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Biologie und Umweltbildung — Ökosysteme, Arten­sterben, Anthropozän","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Biologie und Umweltbildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Klima­wandel wird im Fach Biologie und Umweltbildung über Ökosysteme, Lebensräume, Artenvielfalt und den Schutz von Boden, Wasser, Luft und Wald sichtbar. Der Namenszusatz „Umweltbildung\" macht den Bezug seit 2023 explizit.","kompetenz_id":1}
{"id":1115,"country":"AT","subject":"Biologie und Umweltbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Biologie und Umweltbildung — Ökosystem Fluss und Aue","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Biologie und Umweltbildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Das Fach behandelt den Lebensraum Fluss, Auwälder, Renaturierung und die Rolle von Überschwemmungen im natürlichen Kreislauf. Ökologische Grundlagen ergänzen den GW-Blick auf Hochwasserschutz.","kompetenz_id":2}
{"id":1153,"country":"AT","subject":"Biologie und Umweltbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Biologie und Umweltbildung — Grünraum und Stadtklima","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Biologie und Umweltbildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Der Fachlehrplan Biologie und Umweltbildung behandelt den Lebensraum Stadt, Stadtklima, Begrünung und die Rolle von Grünflächen für Gesundheit und Biodiversität.","kompetenz_id":4}
{"id":1197,"country":"AT","subject":"Biologie und Umweltbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Biologie und Umweltbildung — Tag-Nacht-Rhythmus, Jahreszeiten und Lebewesen","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Biologie und Umweltbildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Jahreszeiten prägen Lebenszyklen von Pflanzen und Tieren (Vegetationszonen, Zugvögel, Winterruhe). Tag-Nacht-Rhythmus steuert circadiane Biologie. Die Simulation macht die astronomische Ursache für biologische Phänomene sichtbar.","kompetenz_id":80}
{"id":1218,"country":"AT","subject":"Biologie und Umweltbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Biologie und Umweltbildung — Pflanzen, Tiere, Ökosysteme, Bodenkunde","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Biologie und Umweltbildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Der Lebensraum Boden, Nährstoffkreisläufe, Photosynthese, tierische Produktion und Ökosystem-Dienstleistungen sind biologische Fundamente der Landwirtschaft. Die Simulation behandelt Nutzpflanzen (Weizen, Mais, Reis, Banane, Kaffee, Olive, Datteln) und Nutztiere (Rind, Schaf, Kamel, Rentier) im jeweiligen ökologischen Kontext.","kompetenz_id":107}
{"id":1096,"country":"AT","subject":"Chemie","school_type":"MS \/ AHS-U","grade":"3.4. Klasse","title":"Chemie — Treibhausgase, Verbrennung fossiler Brennstoffe","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Chemie","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die chemischen Grundlagen von CO₂, Methan und Verbrennungsprozessen werden im Chemieunterricht der 4. Klasse behandelt. Sie liefern die stoffliche Basis für das Verständnis des Treibhauseffekts.","kompetenz_id":1}
{"id":1219,"country":"AT","subject":"Chemie","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Chemie — Düngung, Bodenchemie, Photosynthese","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Chemie","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Stickstoff-Phosphor-Kalium-Düngung, Bodenchemie und der Stoff­wechsel der Photosynthese (CO₂ + H₂O → Zucker) sind chemische Grundlagen, die Erträge erklären. Die Simulation referenziert diese Konzepte über Eignungsstufen und Bodenmüdigkeit.","kompetenz_id":107}
{"id":1205,"country":"AT","subject":"Deutsch","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Deutsch — Texte verstehen, argumentieren, beschwerden formulieren","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Deutsch","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"EU-Petitionen und Bürgerinitiativen schriftlich formulieren, Gesetzes­texte erschließen, Positionen begründet vortragen — die Sprachkompetenzen aus dem Deutschunterricht sind die Grundlage für aktive politische Beteiligung.","kompetenz_id":93}
{"id":1099,"country":"AT","subject":"Geschichte und politische Bildung","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Geschichte und politische Bildung — Industrialisierung und Klimapolitik","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Geschichte und politische Bildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Der menschengemachte Klimawandel lässt sich historisch als Folge der Industrialisierung einordnen. Klimaschutz­politik (Pariser Abkommen, EU-Green-Deal) ist Gegenstand der politischen Bildung ab der 3. Klasse.","kompetenz_id":1}
{"id":1118,"country":"AT","subject":"Geschichte und politische Bildung","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Geschichte und politische Bildung — Hochwasser­ereignisse Österreichs","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Geschichte und politische Bildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die großen Hochwasser­ereignisse (1954, 2002, 2013, 2024) sind historische Wendepunkte der österreichischen Schutzpolitik. Geschichte liefert den Kontext für politische und gesellschaftliche Reaktionen.","kompetenz_id":2}
{"id":1136,"country":"AT","subject":"Geschichte und politische Bildung","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Geschichte und politische Bildung — historische Karten, Grenzen","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Geschichte und politische Bildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Historische Karten zeigen, wie sich Räume, Grenzen und Ortsbezeichnungen über die Zeit ändern. Der kritische Umgang mit Kartenquellen ist eine Kernkompetenz der historischen Bildung.","kompetenz_id":3}
{"id":1154,"country":"AT","subject":"Geschichte und politische Bildung","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Geschichte und politische Bildung — Urbanisierung und Stadtentwicklung","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Geschichte und politische Bildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die historische Entwicklung europäischer Städte — von der Industrialisierung bis zur suburbanen Zersiedelung — erklärt die heutigen Raumstrukturen und ist Teil des GSP-Lehrplans.","kompetenz_id":4}
{"id":1179,"country":"AT","subject":"Geschichte und politische Bildung","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Geschichte und politische Bildung — Europäische Integration","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Geschichte und politische Bildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die Entwicklung der Europäischen Union, Handelsgeschichte und die historische Rolle von Häfen und Handelswegen stehen im Fachlehrplan Geschichte und ergänzen die räumliche Orientierung.","kompetenz_id":41}
{"id":1180,"country":"AT","subject":"Geschichte und politische Bildung","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Geschichte und politische Bildung — Industrialisierung und Handelsnetze","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Geschichte und politische Bildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die historische Entwicklung von Handelsnetzen, Industrialisierung und Globalisierung ist Gegenstand des GSP-Unterrichts und liefert den zeitlichen Kontext für heutige Logistiksysteme.","kompetenz_id":43}
{"id":1196,"country":"AT","subject":"Geschichte und politische Bildung","school_type":"MS \/ AHS-U","grade":"3.4. Klasse","title":"Geschichte und politische Bildung — Heliozentrisches Weltbild","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Geschichte und politische Bildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Der Wechsel vom geozentrischen zum heliozentrischen Weltbild (Kopernikus, Galilei) ist ein Wendepunkt der Wissenschaftsgeschichte. Die Simulation zeigt das heliozentrische Modell anschaulich und liefert den anschaulichen Grund, warum dieser Schritt überhaupt nötig wurde.","kompetenz_id":80}
{"id":1204,"country":"AT","subject":"Geschichte und politische Bildung","school_type":"MS \/ AHS-U","grade":"3.4. Klasse","title":"Geschichte und politische Bildung — EU-Geschichte und Demokratie","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Geschichte und politische Bildung","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die Entwicklung der Europäischen Union (Montanunion, EWG, Maastricht, Lissabon), das Verhältnis von supranationaler und nationaler Demokratie sowie der Vergleich politischer Systeme sind Kernbestand des Fachlehrplans GSP.","kompetenz_id":93}
{"id":1113,"country":"AT","subject":"Gesundheitsförderung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 3 — Gesundheitsförderung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Katastrophenvorsorge und Verhaltensregeln bei Naturgefahren gehören zur Gesundheits­bildung. Die Kompetenz zum Selbstschutz und zur Gemeinschaftshilfe wird in diesem Querschnittsthema didaktisch adressiert.","kompetenz_id":2}
{"id":1222,"country":"AT","subject":"Gesundheitsförderung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 3 — Gesundheits­förderung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Erste Hilfe, Notfallkommunikation, das Verhalten am Berg (Wetterbeobachtung, Tourenplanung, Lawinengefahr) sind Bausteine der Gesundheitsbildung. Heli-Navigation macht den Ernstfall sichtbar — und damit die Bedeutung präventiver Sicherheit.","kompetenz_id":3}
{"id":1082,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"1. Klasse (Sek I)","title":"Grundlagen des Klimawandels — nachhaltige Ernährung","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 1.9 \/ 1.10","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Grundlegende Phänomene und Prozesse beschreiben, die für das Verständnis des Klimawandels und seiner gesellschaftlichen, wirtschaftlichen und ökologischen Folgen wichtig sind; den Einfluss des Klimawandels auf die Erzeugung von ausreichenden und gesunden Nahrungsmitteln an Fallbeispielen erörtern.","kompetenz_id":1}
{"id":1083,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"2. Klasse (Sek I)","title":"Nachhaltiger Umgang mit Energie und Ressourcen","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 2.1 \/ 2.3","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die Nutzung erneuerbarer und nicht erneuerbarer Energieträger vergleichen und aus den Blickwinkeln von Klimawandel und Nachhaltigkeit reflektieren; den verantwortungsvollen Umgang mit den natürlichen Ressourcen wie Wasser, Luft oder Boden erörtern.","kompetenz_id":1}
{"id":1084,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"Mensch und Natursysteme — Belastungsgrenzen der Erde","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.2 \/ 4.3 \/ 4.10","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Folgen der Überschreitung von Belastungsgrenzen der Erde, des Klimawandels oder des Artensterbens auf das Leben und Wirtschaften erörtern; nachhaltige wirtschaftliche Aktivitäten in der globalisierten Welt vor dem Hintergrund von Klimawandel, Ressourcenknappheit, Flucht und Migration diskutieren.","kompetenz_id":1}
{"id":1104,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"1. Klasse (Sek I)","title":"Wechselwirkungen von Naturereignissen und Lebensqualität","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 1.11 \/ 1.12","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Wechselwirkungen von Naturereignissen, Lebensqualität und Wirtschaften der Menschen anhand von Fallbeispielen beschreiben und erörtern; unterschiedliche Betroffenheit von Menschen und Gesellschaften durch Naturgefahren bewerten und Möglichkeiten solidarischen Handelns erörtern.","kompetenz_id":2}
{"id":1105,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"Belastungsgrenzen der Erde — Hochwasser im Anthropozän","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.1 \/ 4.2","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Ausmaß des menschlichen Einflusses auf Natursysteme an Beispielen wie Land- und Ressourcennutzung beschreiben; Folgen der Überschreitung von Belastungsgrenzen der Erde auf das Leben und Wirtschaften erörtern — Hochwasser als unmittelbare Folge veränderter Flächennutzung.","kompetenz_id":2}
{"id":1221,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"2.3. Klasse (Sek I)","title":"Bergrettung und Katastrophenschutz im alpinen Raum","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 1.11 \/ 1.12","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Wechselwirkungen von Naturereignissen, Lebensqualität und Wirtschaften der Menschen anhand von Fallbeispielen beschreiben und erörtern; unterschiedliche Betroffenheit von Menschen durch Naturgefahren bewerten und Möglichkeiten solidarischen Handelns erörtern. Lawinen, Steinschlag, Hochwasser und Sturm machen Bergrettung mit dem Hubschrauber zur Lebensader im alpinen Raum.","kompetenz_id":2}
{"id":1124,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"1. Klasse (Sek I)","title":"Orientierung auf Maßstabsebenen mit Geomedien","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 1.3 \/ 1.6","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Das persönliche Leben beginnend in der Wohn-\/Schulortgemeinde auf verschiedenen Maßstabsebenen mit Hilfe von Geomedien einordnen und darstellen; Fallbeispiele aus Zentren und Peripherien mit Geomedien lokalisieren.","kompetenz_id":3}
{"id":1125,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"3. Klasse (Sek I)","title":"Zentren und Peripherien Österreichs in Geomedien","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 3.15 \/ 3.16","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Zentren und Peripherien Österreichs abgrenzen, in Geomedien verorten und die Relativität jeder Abgrenzung erläutern; Gestaltung von zentralen und peripheren Lebensräumen mit Hilfe von originalen Begegnungen und Geomedien vergleichen.","kompetenz_id":3}
{"id":1142,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"3. Klasse (Sek I)","title":"Raumnutzung und Nutzungskonflikte in Österreich","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 3.15 \/ 3.16 \/ 3.17","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Zentren und Peripherien Österreichs abgrenzen; Gestaltung zentraler und peripherer Lebensräume vergleichen; mit (Geo-)Medien Raumnutzungen für Wohnen, Arbeit, Verkehr, Freizeitaktivitäten und Tourismus vergleichen sowie Lösungsansätze der Raumplanung bei Nutzungskonflikten erörtern.","kompetenz_id":4}
{"id":1143,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"Städte und Ballungsräume in einer globalisierten Welt","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.7 \/ 4.8","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Verteilung der Bevölkerung und unterschiedliche Bevölkerungsdynamik aus globaler Perspektive analysieren; Entwicklung, Bedeutung und Verteilung von Städten, Ballungsräumen und Peripherien mit (Geo-)Medien beschreiben und vergleichen.","kompetenz_id":4}
{"id":1160,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"Länder, Regionen und Staaten Europas einordnen (lg-orient-01 \/ 02 \/ 03)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.4","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Unterschiedliche Gliederungen, Wahrnehmungen und Darstellungen von Europa und der Europäischen Union in ihrer Vielfalt beschreiben, in (Geo-)Medien analysieren und dazu kritisch Stellung beziehen.","kompetenz_id":41}
{"id":1161,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"3.4. Klasse (Sek I)","title":"Häfen, Terminals und große Verkehrsachsen verorten (lg-orient-04)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 3.6 \/ 4.8","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Industrieregionen, Tourismusregionen, landwirtschaftlich geprägte Regionen … sowie Entwicklung, Bedeutung und Verteilung von Städten, Ballungsräumen und Peripherien mit (Geo-)Medien beschreiben und vergleichen.","kompetenz_id":41}
{"id":1209,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"1.2. Klasse (Sek I)","title":"Hauptstädte und Staaten Europas verorten","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 1.3 \/ 1.6","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Persönliches Leben auf verschiedenen Maßstabsebenen mit Hilfe von Geomedien einordnen und darstellen; weltweit ausgewählte Fallbeispiele aus Zentren und Peripherien lokalisieren. Die topografische Orientierung in Europa (Hauptstädte, Staaten, Flüsse) ist das Fundament für alle weiteren Europa-Themen.","kompetenz_id":41}
{"id":1162,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"1. Klasse (Sek I)","title":"Karten lesen, Lagebeziehungen erkennen (lg-karte-01 \/ 02)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 1.3 \/ 1.6","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Persönliches Leben auf verschiedenen Maßstabsebenen mit Hilfe von Geomedien einordnen und darstellen; Gemeinsamkeiten und Unterschiede im Zusammenhang mit Wohnen und Mobilität in weltweit ausgewählten Fallbeispielen mit Geomedien lokalisieren.","kompetenz_id":42}
{"id":1163,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"3. Klasse (Sek I)","title":"Distanzen, Verkehrsnetze und Suchstrategien auf Karten deuten (lg-karte-03 \/ 04)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 3.15 \/ 3.17","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Mit Hilfe von (Geo-)Medien die Raumnutzungen für Wohnen, Arbeit, Verkehr, Freizeitaktivitäten und Tourismus vergleichen sowie Lösungsansätze der Raumplanung bei Nutzungskonflikten erörtern.","kompetenz_id":42}
{"id":1210,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"2.3. Klasse (Sek I)","title":"Maßstab und Distanz — Entfernungen zwischen Städten schätzen","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 1.4 \/ 3.15","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Wesentliche Charakteristika der räumlichen Umwelt erheben, beschreiben und individuell bewerten; Zentren und Peripherien Österreichs (und Europas) abgrenzen, in Geomedien verorten. Distanzen in Kilometern grob abzuschätzen ist eine Grundkompetenz der Kartenarbeit — von Wien nach Paris sind ungefähr 1240 km Luftlinie.","kompetenz_id":42}
{"id":1164,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"3. Klasse (Sek I)","title":"Güterströme und außenwirtschaftliche Verflechtungen verstehen (lg-wirt-01)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 3.14","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Gründe, Bedeutung sowie ökonomische, gesellschaftliche und ökologische Folgen der außenwirtschaftlichen Verflechtungen (Import\/Export) Österreichs mit der EU und der restlichen Welt erläutern.","kompetenz_id":43}
{"id":1165,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"3. Klasse (Sek I)","title":"Kombinierte Verkehre (Bahn · Schiff · LKW) nachvollziehen (lg-wirt-02 \/ 03)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 3.6 \/ 3.13","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Industrieregionen, Tourismusregionen … charakterisieren; die wirtschaftliche Entwicklung Österreichs anhand von Kenngrößen (BIP, Beschäftigung, Außenhandel) beschreiben und persönliche, gesellschaftliche und ökologische Folgen diskutieren.","kompetenz_id":43}
{"id":1166,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"2. Klasse (Sek I)","title":"Zeit-Kosten-Zusammenhänge reflektieren (lg-wirt-04)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 2.6 \/ 2.7","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Reflektierte Entscheidungen bei der Nutzung von Bank-, Verkehrs- oder Handelsdienstleistungen treffen; Angebot und Nachfrage auf Märkten sowie deren Funktionen insbesondere für die Preisbildung charakterisieren.","kompetenz_id":43}
{"id":1167,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"2. Klasse (Sek I)","title":"Reihenfolge und Einsatz knapper Mittel planen (lg-plan-01 \/ 02)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 2.8","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Einfache Projektideen zur Erzeugung von Gütern oder zur Bereitstellung von Dienstleistungen unter Analyse von Angebot, Nachfrage, Ressourceneinsatz, Knappheit und Preisgestaltung entwickeln, umsetzen und Auswirkungen reflektieren.","kompetenz_id":44}
{"id":1168,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"2. Klasse (Sek I)","title":"Leerfahrten vermeiden, Ressourcen sparsam einsetzen (lg-plan-03 \/ 04)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 2.1 \/ 2.3","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die Nutzung erneuerbarer und nicht erneuerbarer Energieträger aus den Blickwinkeln von Klimawandel und Nachhaltigkeit reflektieren; den verantwortungsvollen Umgang mit natürlichen Ressourcen erörtern.","kompetenz_id":44}
{"id":1169,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"1. Klasse (Sek I)","title":"Einfluss von Wetter, Sperren und Störungen auf Verkehr erkennen (lg-verk-01)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 1.11","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Wechselwirkungen von Naturereignissen, Lebensqualität und Wirtschaften der Menschen anhand von Fallbeispielen beschreiben und erörtern.","kompetenz_id":45}
{"id":1170,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"3. Klasse (Sek I)","title":"Verkehr als komplexes System aus Infrastruktur und Entscheidungen verstehen (lg-verk-02 \/ 03)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 3.17","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Mit Hilfe von (Geo-)Medien die Raumnutzungen für Wohnen, Arbeit, Verkehr, Freizeitaktivitäten und Tourismus vergleichen sowie Lösungsansätze der Raumplanung bei Nutzungskonflikten erörtern.","kompetenz_id":45}
{"id":1171,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"Einzelentscheidungen wirken auf das Gesamtsystem (lg-sys-01 \/ 02)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.6","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Chancen und Herausforderungen der europäischen Integration sowie die Notwendigkeit und Schwierigkeit von gemeinsamen Problemlösungen auf europäischer Ebene in einer globalisierten Welt erörtern.","kompetenz_id":46}
{"id":1172,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"Globale Lieferketten auf Europa-Ebene herunterbrechen (lg-sys-03)","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.10","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Elemente, Anforderungen und Herausforderungen nachhaltiger wirtschaftlicher Aktivitäten und Beziehungen in der globalisierten Welt diskutieren, zB vor dem Hintergrund von Klimawandel, Ressourcenknappheit, Armut und Wohlstand, Flucht und Migration.","kompetenz_id":46}
{"id":1182,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"2. Klasse (Sek I)","title":"Erneuerbare Energieträger und Wasserkraft","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 2.1","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die Nutzung erneuerbarer und nicht erneuerbarer Energieträger vergleichen und aus den Blickwinkeln von Klimawandel und Nachhaltigkeit reflektieren. Wasserkraft ist Österreichs wichtigste erneuerbare Stromquelle — die Simulation macht das Prinzip der gespeicherten Höhenenergie erfahrbar.","kompetenz_id":57}
{"id":1183,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"3. Klasse (Sek I)","title":"Wasserkraft als Wirtschaftsstandort Österreichs","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 3.6 \/ 3.13","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Industrieregionen … charakterisieren und verorten; die wirtschaftliche Entwicklung Österreichs anhand von Kenngrößen beschreiben. Alpine Pumpspeicher (Kaprun, Malta, Kops II, Reißeck, Limberg) sind ein zentraler Standortfaktor und Exportgut der österreichischen Energiewirtschaft.","kompetenz_id":57}
{"id":1184,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"Mensch-Umwelt-Beziehungen — Eingriff in den Naturraum","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.1 \/ 4.3 \/ 4.10","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Ausmaß des menschlichen Einflusses auf Natursysteme an Beispielen wie Land- und Ressourcennutzung beschreiben; eigene Ideen für nachhaltige Handlungen entwickeln. Stauseen verändern Flüsse, Lebensräume und Landschaften — die Simulation macht den Zielkonflikt zwischen Energieausbau und Naturraum-Schutz erlebbar.","kompetenz_id":57}
{"id":1206,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"2. Klasse (Sek I)","title":"Lastausgleich im Tagesverlauf — Strombedarf verstehen","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 2.1 \/ 2.6","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Reflektierte Entscheidungen bei der Nutzung von Verkehrs- oder Handelsdienstleistungen treffen, erneuerbare Energieträger aus Blickwinkeln von Klimawandel und Nachhaltigkeit reflektieren. Strombedarf schwankt zwischen Werktag und Wochenende, Morgen und Mittag — die Simulation macht diese Tagesprofile erlebbar.","kompetenz_id":57}
{"id":1190,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"1. Klasse (Sek I)","title":"Beleuchtungszonen der Erde — Wendekreise und Polarkreise","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 1.4 \/ 1.6","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Wesentliche Charakteristika der räumlichen Umwelt erheben, beschreiben und individuell bewerten; Geomedien zur Lokalisierung nutzen. Wendekreise (23,5° N\/S), Polarkreise (66,5° N\/S) und Klimazonen sind Folge der Erdachsneigung — die Simulation macht diesen Zusammenhang sichtbar.","kompetenz_id":80}
{"id":1191,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"2. Klasse (Sek I)","title":"Klima und Wetter im globalen System","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 2.1 \/ 2.3","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Natürliche Ressourcen, Lebensraum-Klima und globale Zusammenhänge im Spiegel der Erdmechanik einordnen. Mitternachtssonne und Polarnacht zeigen, wie Achsneigung das lokale Klima prägt.","kompetenz_id":80}
{"id":1198,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"Werte und zentrale Themen der EU","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.5","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die Bedeutung gemeinsamer Werte wie Friedenserhaltung, Demokratie oder Freiheit begründen und zentrale Themen der EU wie die vier Grundfreiheiten (Waren, Personen, Kapital und Dienstleistungen), den Euro, Bildungskooperationen, Qualitätsstandards, Verbraucherschutz oder Regionalentwicklung im Hinblick auf das eigene Leben reflektieren.","kompetenz_id":93}
{"id":1199,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"Chancen und Herausforderungen der EU-Integration","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.6","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Chancen und Herausforderungen der europäischen Integration sowie die Notwendigkeit und Schwierigkeit von gemeinsamen Problemlösungen auf europäischer Ebene in einer globalisierten Welt erörtern.","kompetenz_id":93}
{"id":1200,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"EU-Gliederungen und Darstellungen analysieren","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.4","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Unterschiedliche Gliederungen, Wahrnehmungen und Darstellungen von Europa und der Europäischen Union in ihrer Vielfalt beschreiben, in (Geo-)Medien analysieren und dazu kritisch Stellung beziehen.","kompetenz_id":93}
{"id":1213,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"1. Klasse (Sek I)","title":"Nachhaltige Ernährung — wo wächst was?","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 1.8 \/ 1.9","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Ausgehend von individuellen Ernährungsgewohnheiten (land-)wirtschaftliche Produktion, deren gesellschaftliche, wirtschaftliche und naturräumliche Bedingungen sowie deren Wandel an weltweit ausgewählten Fallbeispielen verorten und vergleichen; den Einfluss des Klimawandels auf die Erzeugung von ausreichenden und gesunden Nahrungsmitteln erörtern.","kompetenz_id":107}
{"id":1214,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"3. Klasse (Sek I)","title":"Landwirtschaftlich geprägte Regionen Österreichs und der Welt","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 3.6","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Industrieregionen, Tourismusregionen, landwirtschaftlich geprägte Regionen und Regionen mit anderen wirtschaftlichen Schwerpunkten charakterisieren und verorten. Die Simulation deckt Klimazonen weltweit (Tropen, Subtropen, gemäßigte Zonen, Polargebiete) ab und macht Anbaugrenzen erlebbar.","kompetenz_id":107}
{"id":1215,"country":"AT","subject":"GW","school_type":"MS \/ AHS-U","grade":"4. Klasse (Sek I)","title":"Anbaugrenzen, Belastungsgrenzen, globale Welternährung","reference":"BMBWF GW-Lehrplan Sek I 2023, Kompetenz 4.1 \/ 4.10","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Ausmaß des menschlichen Einflusses auf Natursysteme an Beispielen wie Land- und Ressourcennutzung beschreiben; Anforderungen nachhaltiger wirtschaftlicher Aktivitäten in der globalisierten Welt diskutieren — darunter Klimawandel-Folgen für die Welternährung (Verschiebung von Anbauzonen, Verlust fruchtbarer Böden).","kompetenz_id":107}
{"id":1131,"country":"AT","subject":"Informatische Bildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 4 — Informatische Bildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"GIS und digitale Geomedien (OpenStreetMap, Atlas-Software, Geocaching) sind Anwendungsbeispiele der Informatischen Bildung. Im GW-Lehrplan über die Kompetenzen 1.3, 1.6 und 3.15 direkt verankert.","kompetenz_id":3}
{"id":1174,"country":"AT","subject":"Informatische Bildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 4 — Informatische Bildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Digitale Kartendienste, GIS, Routenplanung und Geocaching sind Anwendungsbeispiele der Informatischen Bildung — im Logistik-Modul durchgehend im Einsatz.","kompetenz_id":42}
{"id":1187,"country":"AT","subject":"Informatische Bildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 4 — Informatische Bildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Lastausgleich, Prognosen und Steuerentscheidungen sind algorithmische Aufgaben. Smart Grids und Lastmanagement sind direkte Anwendungen der Informatischen Bildung — im Pumpspeicher-Modell als Steuerlogik erfahrbar.","kompetenz_id":57}
{"id":1193,"country":"AT","subject":"Informatische Bildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 4 — Informatische Bildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"3D-Visualisierungen, Modellbildung und Simulation von Himmelsmechanik sind anschauliche Anwendungen der Informatischen Bildung. Die Simulation macht abstrakte astronomische Modelle direkt erlebbar.","kompetenz_id":80}
{"id":1212,"country":"AT","subject":"Interkulturelle Bildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 5 — Interkulturelle Bildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Hauptstädte, Sprachen und kulturelle Eigenheiten der europäischen Nachbarländer kennen ist Grundlage interkultureller Verständigung. Die Info-Cards der Busfahrt-Simulation liefern jeweils 2-3 Fakten zur Stadt — Bevölkerung, Sprache, Besonderheiten.","kompetenz_id":41}
{"id":1098,"country":"AT","subject":"Mathematik","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Mathematik — Größen, Daten, Prozent (fächerübergreifendes Rechnen)","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Mathematik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Klimadaten werden gemessen, verglichen und berechnet: ppm, °C, MW, kWh, t CO₂, Prozentanteile im Energiemix. Der Mathematik-Lehrplan liefert mit „Größen\" und „Daten\" die Werkzeuge, um diese Zahlen einzuordnen.","kompetenz_id":1}
{"id":1117,"country":"AT","subject":"Mathematik","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Mathematik — Pegelstände, Statistik, Wahrscheinlichkeit","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Mathematik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Hochwasser­ereignisse werden mit Jahrhundert-Wahrscheinlichkeiten (HQ100), Pegelkurven und statistischen Datenreihen gemessen. Der Mathematik-Lehrplan liefert die nötigen Werkzeuge zur Dateninterpretation.","kompetenz_id":2}
{"id":1134,"country":"AT","subject":"Mathematik","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Mathematik — Koordinatensystem, Maßstab, Winkel","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Mathematik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Das zweidimensionale Koordinatensystem, Maßstabs­umrechnung, Winkelberechnung und Flächen gehören zum Mathematik-Kernstoff und sind die Basis für Kartenarbeit und Navigation.","kompetenz_id":3}
{"id":1152,"country":"AT","subject":"Mathematik","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Mathematik — Flächenberechnung, Bevölkerungsdichte, Diagramme","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Mathematik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Flächen in Hektar, Bevölkerungsdichte in Einwohner pro km², Diagramme zum Flächenverbrauch — Stadtplanung arbeitet durchgehend mit Größen und Daten, die im Mathematik-Unterricht geübt werden.","kompetenz_id":4}
{"id":1211,"country":"AT","subject":"Mathematik","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Mathematik — Maßstab, Verhältnisse, Größenordnungen von Strecken","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Mathematik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Maßstabsumrechnung (1 cm auf der Karte = 100 km in der Wirklichkeit), Schätzgenauigkeit und Größenordnungen sind klassische Mathematik-Anwendungsfelder. Die Busfahrt-Simulation übt sie an konkreten europäischen Distanzen.","kompetenz_id":42}
{"id":1178,"country":"AT","subject":"Mathematik","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Mathematik — Optimierung, Zeit, Kosten, Wege","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Mathematik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Reihenfolge-Probleme, Routen, Kapazitätsverteilung, Zeit-Kosten-Berechnungen: Logistik ist ein Anwendungsfeld kombinatorischer und arithmetischer Kompetenzen aus dem Mathematik-Unterricht.","kompetenz_id":44}
{"id":1181,"country":"AT","subject":"Mathematik","school_type":"MS \/ AHS-U","grade":"3.4. Klasse","title":"Mathematik — Modellierung vernetzter Systeme","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Mathematik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Funktionen, Abhängigkeiten, Diagramme und Datenauswertung sind mathematische Werkzeuge, um Systemverhalten zu modellieren — Voraussetzung für die Analyse logistischer Gesamtsysteme.","kompetenz_id":46}
{"id":1189,"country":"AT","subject":"Mathematik","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Mathematik — funktionale Zusammenhänge, Diagramme, Bilanzen","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Mathematik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"5-Tage-Bedarfskurven lesen, Füllstände in Prozent berechnen, Zuflüsse und Abflüsse bilanzieren — die Simulation übt das Lesen funktionaler Zusammenhänge und einfache Bilanzrechnungen direkt am Anwendungsfall.","kompetenz_id":57}
{"id":1195,"country":"AT","subject":"Mathematik","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Mathematik — Skalen, Verhältnisse, Größenordnungen","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Mathematik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Verhältnisse (Sonne:Erde:Mond ≈ 109:1:0,27), Skalenwechsel (symbolisch ↔ realistische Größen ↔ realistische Distanzen), Winkel (23,5° Achsneigung, 5,1° Mondbahn-Neigung) — die Simulation übt mathematisches Skalendenken in einem konkreten Anwendungsfall.","kompetenz_id":80}
{"id":1220,"country":"AT","subject":"Mathematik","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Mathematik — Karten, Eignungsklassen, Erträge in Prozent","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Mathematik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Karten- und Maßstabsarbeit, prozentuale Erträge (S1=100%, S2=80%, S3=60%, N1=30%, N2=0%), Bilanzrechnungen und Risikoabwägung — die Simulation übt mathematische Grundkompetenzen am konkreten Anwendungsfall.","kompetenz_id":107}
{"id":1132,"country":"AT","subject":"Medienbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 6 — Medienbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Karten sind Medien: Maßstab, Generalisierung und Darstellungswahl prägen das Bild des Raums. Die reflektierte Nutzung von Karten ist ein klassisches Medienbildungs-Lernziel.","kompetenz_id":3}
{"id":1203,"country":"AT","subject":"Medienbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 6 — Medienbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Wer macht europäische Politik sichtbar? Wer beeinflusst sie? Lobbyismus, Transparenzregister und der Umgang mit Quellen sind Querschnittsthemen der Medienbildung — die EU-Werkstatt arbeitet mit echten Fällen und ermöglicht Quellenkritik.","kompetenz_id":93}
{"id":1097,"country":"AT","subject":"Physik","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Physik — Wärmestrahlung, Energieformen, Treibhauseffekt","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Physik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Der Treibhauseffekt ist physikalisch über Strahlung, Wärmetransport und Energiebilanzen erklärbar. Das Fach Physik liefert Einheiten wie Watt, Kilowatt und Kilowattstunde, die für das Verständnis von erneuerbaren Energien zentral sind.","kompetenz_id":1}
{"id":1116,"country":"AT","subject":"Physik","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Physik — Wasserdruck, Strömung und Energie","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Physik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Hochwasser wird physikalisch durch Strömungsgeschwindigkeit, Wasserdruck und Energieabgabe beschreibbar. Diese Grundlagen erklären, warum Dämme brechen oder warum Retentionsräume Spitzenabflüsse reduzieren.","kompetenz_id":2}
{"id":1135,"country":"AT","subject":"Physik","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Physik — Projektion, Lot, Erdrotation","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Physik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Kartenprojektionen, die Kugelgestalt der Erde und die Erdrotation sind physikalische Grundlagen der Gradnetz-Orientierung. Auch die Schwerkraft (Lotrichtung) ist für Höhenmessungen relevant.","kompetenz_id":3}
{"id":1225,"country":"AT","subject":"Physik","school_type":"MS \/ AHS-U","grade":"3.4. Klasse","title":"Physik — Auftrieb, Wind, Wetterphänomene","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Physik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Hubschrauber-Auftrieb, Wirkung von Aufwind und Fallwind in den Alpen, Höhenmesser, Druck und Temperatur — die physikalischen Grundlagen erklären, warum mancher Einsatz zu gefährlich ist und warum die Höhenstufen reale Konsequenzen haben.","kompetenz_id":3}
{"id":1188,"country":"AT","subject":"Physik","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Physik — Energieerhaltung, Höhenenergie, Wirkungsgrad","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Physik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Pumpspeicher illustrieren das Prinzip der Energieerhaltung wie kein zweites: potentielle Lageenergie wird in elektrische Energie umgewandelt und umgekehrt. Wirkungsgrade beim Pumpen und Turbinieren machen physikalische Verluste konkret.","kompetenz_id":57}
{"id":1207,"country":"AT","subject":"Physik","school_type":"MS \/ AHS-U","grade":"3.4. Klasse","title":"Physik — Leistung, Arbeit, Spitzenlast vs. Grundlast","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Physik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Die Unterscheidung von Leistung (Watt, Megawatt) und Arbeit (Wattstunde, Megawattstunde), Grundlast und Spitzenlast sowie das Konzept des Wirkungsgrads sind Kernstoff der Physik. Der Energiemanager lässt diese Größen in Echtzeit-Bilanzen sichtbar werden.","kompetenz_id":57}
{"id":1194,"country":"AT","subject":"Physik","school_type":"MS \/ AHS-U","grade":"2.4. Klasse","title":"Physik — Himmelsmechanik, Licht, Größenordnungen","reference":"BMBWF Lehrplan Sek I 2023, Fachlehrplan Physik","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Erdrotation und Erdumlauf, Lichtausbreitung mit endlicher Geschwindigkeit (8 Min Sonnenlicht zur Erde), Größenordnungen im Sonnensystem (Sonne ≈ 1,3 Mio Erdvolumen) sind Kernbestandteile des Physik-Lehrplans und werden in der Simulation direkt angeschaut.","kompetenz_id":80}
{"id":1093,"country":"AT","subject":"Politische Bildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 7 — Politische Bildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Politische Bildung umfasst u.a. Klimaschutzpolitik, internationale Abkommen und demokratische Entscheidungsprozesse. Im GW-Lehrplan u.a. in den Kompetenzen 3.9 (ökosoziale Marktwirtschaft) und 4.6 (gemeinsame Problemlösungen in der EU) sichtbar.","kompetenz_id":1}
{"id":1114,"country":"AT","subject":"Politische Bildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 7 — Politische Bildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Raumordnung, Hochwasserschutz und die Finanzierung von Schutzbauten sind politische Entscheidungen. Das Thema „unterschiedliche Betroffenheit\" (Kompetenz 1.12) ist eng mit demokratischer Teilhabe und Solidarität verbunden.","kompetenz_id":2}
{"id":1149,"country":"AT","subject":"Politische Bildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 7 — Politische Bildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Stadt­planung, Widmungsverfahren und demokratische Beteiligung (Bürger­initiativen, Gemeinderatsbeschlüsse) sind Kernthemen der Politischen Bildung. Direkt im GW-Lehrplan über Kompetenz 3.17 (Lösungs­ansätze bei Nutzungs­konflikten) verankert.","kompetenz_id":4}
{"id":1173,"country":"AT","subject":"Politische Bildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 7 — Politische Bildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Europa als politisches System (EU, Grundfreiheiten, Regionalentwicklung) ist Querschnittsauftrag der Politischen Bildung. Orientierungsaufbau stützt die politische Urteilsbildung.","kompetenz_id":41}
{"id":1201,"country":"AT","subject":"Politische Bildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 7 — Politische Bildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Demokratie verstehen und mitgestalten ist das Kernanliegen der politischen Bildung. Die EU-Werkstatt macht das ordentliche Gesetzgebungsverfahren, das Initiativmonopol der Kommission und die Hürden der Bürgerinitiative an konkreten Fällen erlebbar.","kompetenz_id":93}
{"id":1092,"country":"AT","subject":"Umweltbildung für nachhaltige Entwicklung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 11 — Umweltbildung für nachhaltige Entwicklung","reference":"BMBWF Lehrplan Sek I 2023, Allgemeiner Teil, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Umweltbildung für nachhaltige Entwicklung ist in allen Unterrichtsgegenständen der Sek I verbindlich verankert. Im GW-Lehrplan ist Klimawandel über die Kompetenzen 1.5, 1.10, 2.1, 2.3, 4.2 und 4.3 direkt an dieses Querschnittsthema gebunden.","kompetenz_id":1}
{"id":1112,"country":"AT","subject":"Umweltbildung für nachhaltige Entwicklung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 11 — Umweltbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Hochwasser, Bodennutzung und Ökosysteme verbinden sich in der Umweltbildung zum Verständnis nachhaltiger Raumnutzung. Im GW-Lehrplan verankert in Kompetenz 2.3 (natürliche Ressourcen) und 4.1\/4.3 (Natursysteme).","kompetenz_id":2}
{"id":1151,"country":"AT","subject":"Umweltbildung für nachhaltige Entwicklung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 11 — Umweltbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Verdichtung vs. Zersiedelung, Grünräume und Flächenverbrauch sind BNE-Fragen. Der GW-Lehrplan bindet diese über Kompetenz 4.10 (nachhaltige Aktivitäten) an das Querschnittsthema.","kompetenz_id":4}
{"id":1177,"country":"AT","subject":"Umweltbildung für nachhaltige Entwicklung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 11 — Umweltbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Das Zusammenspiel von Ressourcenknappheit, Verkehrswegen und globalen Wertschöpfungsketten — BNE-Perspektive auf Logistik.","kompetenz_id":46}
{"id":1185,"country":"AT","subject":"Umweltbildung für nachhaltige Entwicklung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 11 — Umweltbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Pumpspeicher als Schlüssel der Energiewende: Sie verschieben überschüssigen Wind- und Solarstrom in Zeiten höherer Nachfrage. BNE behandelt diese Technologien als Kernbeitrag zu klimaneutraler Stromversorgung.","kompetenz_id":57}
{"id":1192,"country":"AT","subject":"Umweltbildung für nachhaltige Entwicklung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 11 — Umweltbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Das Verstehen von Klimazonen und Erd-Sonne-System ist Voraussetzung dafür, Klimawandel und globale Naturphänomene überhaupt einordnen zu können — Querschnitts-Auftrag der Umweltbildung.","kompetenz_id":80}
{"id":1216,"country":"AT","subject":"Umweltbildung für nachhaltige Entwicklung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 11 — Umweltbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Bodennutzung, Monokultur, Bodenmüdigkeit, Wasserverbrauch und Klimawandelanpassung in der Landwirtschaft sind Kernfelder der BNE. Die Simulation arbeitet mit Köppen-Geiger-Klimazonen und FAO-Eignungsklassen (S1N2) — wissenschaftlich fundiert, didaktisch reduziert.","kompetenz_id":107}
{"id":1133,"country":"AT","subject":"Verkehrs- und Mobilitätsbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 12 — Verkehrs- und Mobilitätsbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Routenplanung, Öffentliche-Verkehrs-Netze und die Orientierung im Raum sind praktische Anwendungsfelder der Mobilitätsbildung. Karten sind dabei das zentrale Werkzeug.","kompetenz_id":3}
{"id":1223,"country":"AT","subject":"Verkehrs- und Mobilitätsbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 12 — Mobilitätsbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Luftverkehr als Sondermobilität (Rettung, Transport, Versorgung) und die Verzahnung mit bodengebundenem Verkehr (Krankenwagen, Tunnelrettung) sind Querschnitt der Mobilitätsbildung. Heli ist nicht „spielen mit Hubschrauber\", sondern arbeiten an einer realen Verkehrs- und Rettungsinfrastruktur.","kompetenz_id":3}
{"id":1150,"country":"AT","subject":"Verkehrs- und Mobilitätsbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 12 — Verkehrs- und Mobilitätsbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Wie sich Städte in Zukunft mit MIV, ÖPNV, Fuß- und Radverkehr verbinden, ist Querschnittsthema der Mobilitäts­bildung. Eng verknüpft mit den GW-Kompetenzen 3.6 (Standortfaktoren) und 3.17 (Raum­planung).","kompetenz_id":4}
{"id":1176,"country":"AT","subject":"Verkehrs- und Mobilitätsbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 12 — Verkehrs- und Mobilitätsbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Reflektierte Verkehrs- und Mobilitätsentscheidungen, Wetter- und Störungseinflüsse, Sicherheit im Verkehr — das Querschnittsthema deckt das Logistik-Modul exakt ab.","kompetenz_id":45}
{"id":1208,"country":"AT","subject":"Verkehrs- und Mobilitätsbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 12 — Energie und Mobilität","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Elektromobilität verschiebt Lastprofile in der Nacht (Laden) und am Tag (Schnelllader) — Mobilität und Stromnetz hängen direkt zusammen. Der Energiemanager macht diese Verzahnung sichtbar.","kompetenz_id":57}
{"id":1094,"country":"AT","subject":"Wirtschafts-, Finanz- und Verbraucher:innenbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 13 — Wirtschaftsbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Der Zusammenhang zwischen nachhaltigem Konsum, ökosozialer Marktwirtschaft und Klimawandel ist Querschnittsauftrag — im GW über die Kompetenzen zu Energieträgern (2.1), Preisbildung (3.8) und globaler Wirtschaft (4.10) verankert.","kompetenz_id":1}
{"id":1175,"country":"AT","subject":"Wirtschafts-, Finanz- und Verbraucher:innenbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 13 — Wirtschaftsbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Produktions- und Handelsentscheidungen, Preisbildung, Kosten-Nutzen-Abwägungen und reflektierter Konsum sind Pflichtbereich der Wirtschaftsbildung — zentral im Logistik-Modul.","kompetenz_id":43}
{"id":1186,"country":"AT","subject":"Wirtschafts-, Finanz- und Verbraucher:innenbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 13 — Wirtschaftsbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Effizienz, Wirkungsgrade, Reservehaltung und der Wert von Energie zu unterschiedlichen Tageszeiten sind Wirtschaftsthemen — die Simulation macht Kosten-Nutzen-Abwägungen am Pumpspeicher-Beispiel anschaulich.","kompetenz_id":57}
{"id":1202,"country":"AT","subject":"Wirtschafts-, Finanz- und Verbraucher:innenbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 13 — Wirtschaftsbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Verbraucherschutz, Binnenmarkt-Standards (USB-C, Roaming, DSGVO) und die ökonomische Dimension von EU-Gesetzen sind Pflichtbereich der Wirtschaftsbildung. Die EU-Werkstatt verbindet politische Beteiligung mit konkreten Verbraucher:innen-Themen.","kompetenz_id":93}
{"id":1217,"country":"AT","subject":"Wirtschafts-, Finanz- und Verbraucher:innenbildung","school_type":"MS \/ AHS-U","grade":"1.4. Klasse","title":"Übergreifendes Thema Nr. 13 — Wirtschaftsbildung","reference":"BMBWF Lehrplan Sek I 2023, Vierter Teil (Übergreifende Themen)","reference_url":"https:\/\/www.ris.bka.gv.at\/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850","quote":"Globale Lieferketten von Lebensmitteln, Importpreis-Mechanik (Reefer-Container, Welt-Häfen), CO₂-Fußabdruck der Ernährung — die Simulation macht den ökonomischen Aspekt der Landwirtschaft sichtbar, von der Anbauentscheidung bis zum Marktpreis.","kompetenz_id":107}
{"id":1100,"country":"CH","subject":"BNE — Natürliche Umwelt und Ressourcen","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Lehrplan 21 — BNE-Rahmen","reference":"Lehrplan 21, BNE (Bildung für Nachhaltige Entwicklung)","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=e%7C200%7C1","quote":"Klimawandel ist im Lehrplan 21 explizit als BNE-Thema verankert und verbindet RZG, NT, WAH und ERG fächerübergreifend.","kompetenz_id":1}
{"id":1119,"country":"CH","subject":"BNE — Natürliche Umwelt und Ressourcen","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Lehrplan 21 — BNE-Perspektive „Natürliche Umwelt und Ressourcen\"","reference":"Lehrplan 21, BNE-Rahmen (Bildung für Nachhaltige Entwicklung)","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=e%7C200%7C1","quote":"Die BNE-Rahmenperspektive bindet Hochwasser und Naturgefahren an Nachhaltigkeit, Raumnutzung und Risikokompetenz über Fachgrenzen hinweg.","kompetenz_id":2}
{"id":1155,"country":"CH","subject":"BNE — Wirtschaft und Konsum","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Lehrplan 21 — BNE-Perspektive „Wirtschaft und Konsum\"","reference":"Lehrplan 21, BNE-Rahmen","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=e%7C200%7C1","quote":"Stadtplanung ist im Lehrplan 21 als BNE-Querschnittsthema über „Wirtschaft und Konsum\" und „Natürliche Umwelt und Ressourcen\" angebunden.","kompetenz_id":4}
{"id":1157,"country":"CH","subject":"Ethik, Religionen, Gemeinschaft (ERG)","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Ethik, Religionen, Gemeinschaft — Zusammenleben in Städten","reference":"Lehrplan 21, Fachbereich ERG","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C8%7C4","quote":"Das Zusammenleben unterschiedlicher Gruppen in urbanen Räumen (Integration, Vielfalt, Solidarität) ist Gegenstand des ERG-Fachbereichs.","kompetenz_id":4}
{"id":1121,"country":"CH","subject":"Mathematik","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Mathematik — Daten, Funktionen, Wahrscheinlichkeit","reference":"Lehrplan 21, Fachbereich Mathematik","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C1%7C4","quote":"Mathematik deckt Datenauswertung (Pegel­kurven, Regenmengen, Jährlich­keiten) ab, die in der Risikobeurteilung von Hochwasser eingesetzt werden.","kompetenz_id":2}
{"id":1139,"country":"CH","subject":"Mathematik","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Mathematik — Koordinaten und Maßstab","reference":"Lehrplan 21, Fachbereich Mathematik","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C1%7C4","quote":"Koordinaten­systeme, Maßstabs­umrechnungen und Flächen­berechnungen sind Teil des Mathematik-Lehrplans und unterstützen die Arbeit mit topografischen Karten.","kompetenz_id":3}
{"id":1138,"country":"CH","subject":"Medien und Informatik (MI)","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Medien und Informatik — Digitale Karten und GIS","reference":"Lehrplan 21, Fachbereich Medien und Informatik","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C10%7C4","quote":"Der Fachbereich MI deckt digitale Kartendienste (OpenStreetMap, GIS-Tools) und den reflektierten Umgang mit geographischen Daten explizit ab.","kompetenz_id":3}
{"id":1101,"country":"CH","subject":"Natur und Technik (NT)","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Natur und Technik — Treibhauseffekt, Energie, Stoffkreisläufe","reference":"Lehrplan 21, Fachbereich Natur und Technik","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C7%7C4","quote":"Der Fachbereich NT liefert die naturwissenschaftliche Erklärung des Treibhauseffekts, der Energieformen und der Kohlenstoff­kreisläufe.","kompetenz_id":1}
{"id":1120,"country":"CH","subject":"Natur und Technik (NT)","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Natur und Technik — Wasser, Energie, Naturereignisse","reference":"Lehrplan 21, Fachbereich Natur und Technik","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C7%7C4","quote":"NT liefert die naturwissenschaftlichen Grundlagen zu Wasser, Energie und Klimasystem — und damit die Erklärungs­basis, warum Hochwasser entsteht und wie Schutzmaßnahmen physikalisch wirken.","kompetenz_id":2}
{"id":1107,"country":"CH","subject":"RZG","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"RZG.1.3 — Naturereignisse und deren Auswirkungen","reference":"Lehrplan 21, Fachbereich RZG","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C6%7C4","quote":"Die Schülerinnen und Schüler können die Entstehung von Naturereignissen (u.a. Hochwasser) erklären und daraus Schutz- und Verhaltensregeln ableiten.","kompetenz_id":2}
{"id":1127,"country":"CH","subject":"RZG","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"RZG.2.1 — Mit Karten und Atlas arbeiten","reference":"Lehrplan 21, Fachbereich RZG","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C6%7C4","quote":"Die Schülerinnen und Schüler können sich mithilfe von Karten und weiteren Medien räumlich orientieren und Informationen aus Karten zu Räumen entnehmen.","kompetenz_id":3}
{"id":1145,"country":"CH","subject":"RZG","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"RZG.4.2 — Räume gestalten und sich aneignen","reference":"Lehrplan 21, Fachbereich RZG","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C6%7C4","quote":"Die Schülerinnen und Schüler können Raumnutzungskonflikte erkennen, planerische Entscheidungen nachvollziehen und eigene Gestaltungsideen für städtische Räume entwickeln.","kompetenz_id":4}
{"id":1087,"country":"CH","subject":"RZG (Räume, Zeiten, Gesellschaften)","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"RZG.3.2 — Natürliche und vom Menschen veränderte Systeme analysieren","reference":"Lehrplan 21, Fachbereich RZG","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C6%7C4","quote":"Die Schülerinnen und Schüler können Auswirkungen des Klimawandels auf natürliche und menschliche Systeme erklären und mögliche Handlungsoptionen abwägen.","kompetenz_id":1}
{"id":1140,"country":"CH","subject":"Überfachliche Kompetenzen — Methodische","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Lehrplan 21 — Methodische Kompetenzen (Informationen nutzen)","reference":"Lehrplan 21, Überfachliche Kompetenzen","reference_url":"https:\/\/v-fe.lehrplan.ch\/","quote":"Das Arbeiten mit Karten, Atlas und digitalen Medien ist als methodische überfachliche Kompetenz im Lehrplan 21 verankert und wirkt fächerübergreifend.","kompetenz_id":3}
{"id":1102,"country":"CH","subject":"Wirtschaft, Arbeit, Haushalt (WAH)","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Wirtschaft, Arbeit, Haushalt — Energieverbrauch und Konsum","reference":"Lehrplan 21, Fachbereich WAH","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C12%7C4","quote":"WAH behandelt den persönlichen Energieverbrauch, das Konsumverhalten und die Entscheidungen privater Haushalte — eine zentrale Hebel­ebene im Klimaschutz.","kompetenz_id":1}
{"id":1156,"country":"CH","subject":"Wirtschaft, Arbeit, Haushalt (WAH)","school_type":"Sek I (Zyklus 3)","grade":"7.9. Schuljahr","title":"Wirtschaft, Arbeit, Haushalt — Flächenmarkt und Wohnen","reference":"Lehrplan 21, Fachbereich WAH","reference_url":"https:\/\/v-fe.lehrplan.ch\/index.php?code=b%7C12%7C4","quote":"Der Fachbereich WAH behandelt Wohnen, Haushalt und Konsum — und damit den wirtschaftlichen Aspekt der Stadt­planung und der Flächennutzung.","kompetenz_id":4}
{"id":1089,"country":"DE","subject":"Erdkunde","school_type":"Gymnasium \/ Realschule","grade":"Jg. 910","title":"Globale Klimaveränderungen — Ursachen und Folgen","reference":"Hessisches Kerncurriculum Sekundarstufe I — Erdkunde","reference_url":"https:\/\/kultus.hessen.de\/unterricht\/kerncurricula-und-lehrplaene\/kerncurricula\/sekundarstufe-i-kerncurricula","quote":"Die Lernenden analysieren Klimaveränderungen, ordnen anthropogene Einflussfaktoren ein und bewerten Maßnahmen zur Minderung und Anpassung auf lokaler und globaler Ebene.","kompetenz_id":1}
{"id":1090,"country":"DE","subject":"Erdkunde","school_type":"Gymnasium (Sek I)","grade":"Jg. 910","title":"Klimawandel und nachhaltige Entwicklung","reference":"Niedersächsisches Kerncurriculum Gymnasium Sek I — Erdkunde","reference_url":"https:\/\/cuvo.nibis.de\/cuvo.php?p=download&upload=61","quote":"Die Schülerinnen und Schüler erörtern Ursachen und Folgen des Klimawandels und beurteilen Handlungsoptionen im Rahmen einer Bildung für nachhaltige Entwicklung (BNE).","kompetenz_id":1}
{"id":1109,"country":"DE","subject":"Erdkunde","school_type":"Gymnasium \/ Realschule plus","grade":"Jg. 710","title":"Leben mit Naturgefahren — Hochwasser an Rhein und Mosel","reference":"Lehrplan Gesellschaftswissenschaften Sek I (2021), Rheinland-Pfalz","reference_url":"https:\/\/static.bildung-rp.de\/lehrplaene\/gesellschaftswissenschaften\/Ek_G_Sk_Sek_I__LP_2021_.pdf","quote":"Die Lernenden beschreiben Ursachen und Verlauf von Hochwasserereignissen an grossen mitteleuropäischen Flüssen und bewerten Schutzstrategien.","kompetenz_id":2}
{"id":1111,"country":"DE","subject":"Erdkunde","school_type":"Gymnasium \/ Realschule","grade":"Jg. 78","title":"Naturräumliche Prozesse — Extremwetter und Hochwasser","reference":"Hessisches Kerncurriculum Sekundarstufe I — Erdkunde","reference_url":"https:\/\/kultus.hessen.de\/unterricht\/kerncurricula-und-lehrplaene\/kerncurricula\/sekundarstufe-i-kerncurricula","quote":"Die Lernenden erklären Entstehung und Verlauf von Hochwasser als naturräumlichen Prozess und bewerten technische wie naturnahe Schutzmaßnahmen.","kompetenz_id":2}
{"id":1126,"country":"DE","subject":"Erdkunde","school_type":"Gymnasium (G9)","grade":"Jg. 57","title":"Orientierung auf der Erde — Gradnetz und topografische Karten","reference":"Kernlehrplan Erdkunde Sek I Gymnasium NRW (ab 2019\/20)","reference_url":"https:\/\/lehrplannavigator.nrw.de\/sekundarstufe-i\/kernlehrplaene-fuer-das-gymnasium-aufsteigend-ab-20192020","quote":"Die Schülerinnen und Schüler lesen und interpretieren thematische Karten, nutzen das Gradnetz zur Lagebestimmung und beschreiben physische Strukturen Mitteleuropas und der Alpen.","kompetenz_id":3}
{"id":1129,"country":"DE","subject":"Erdkunde","school_type":"Gymnasium (Sek I)","grade":"Jg. 57","title":"Orientierung auf der Erde — Gradnetz und thematische Karten","reference":"Niedersächsisches Kerncurriculum Gymnasium Sek I — Erdkunde","reference_url":"https:\/\/cuvo.nibis.de\/cuvo.php?p=download&upload=61","quote":"Die Schülerinnen und Schüler orientieren sich mithilfe des Gradnetzes, werten thematische Karten aus und beschreiben physiogeografische Grundstrukturen Europas.","kompetenz_id":3}
{"id":1147,"country":"DE","subject":"Erdkunde","school_type":"Gymnasium \/ Realschule","grade":"Jg. 79","title":"Stadt- und Siedlungsstrukturen — Wandel und Planung","reference":"Hessisches Kerncurriculum Sekundarstufe I — Erdkunde","reference_url":"https:\/\/kultus.hessen.de\/unterricht\/kerncurricula-und-lehrplaene\/kerncurricula\/sekundarstufe-i-kerncurricula","quote":"Die Lernenden beschreiben Prozesse der Stadtentwicklung in Deutschland und Europa und bewerten Konzepte nachhaltiger Raumordnung.","kompetenz_id":4}
{"id":1085,"country":"DE","subject":"Geographie","school_type":"Sek I","grade":"Jg. 910","title":"Klimawandel als globale Herausforderung","reference":"Rahmenlehrplan Teil C Geografie (Berlin\/Brandenburg)","reference_url":"https:\/\/bildungsserver.berlin-brandenburg.de\/unterricht\/rahmenlehrplaene","quote":"Die Schülerinnen und Schüler erklären Ursachen und Folgen des globalen Klimawandels und bewerten Maßnahmen der Klimaanpassung und des Klimaschutzes.","kompetenz_id":1}
{"id":1086,"country":"DE","subject":"Geographie","school_type":"Gymnasium","grade":"Jg. 9","title":"Der Mensch im Ökosystem Erde — anthropogener Klimawandel","reference":"LehrplanPLUS Bayern, Gymnasium Geographie 9","reference_url":"https:\/\/www.lehrplanplus.bayern.de\/fachlehrplan\/gymnasium\/9\/geographie","quote":"Die Schülerinnen und Schüler beurteilen Ursachen und Folgen des anthropogen verstärkten Treibhauseffekts und erläutern internationale Klimaschutzabkommen.","kompetenz_id":1}
{"id":1106,"country":"DE","subject":"Geographie","school_type":"Sek I","grade":"Jg. 56","title":"Hochwasser als naturräumlicher Prozess — Gestaltung der Erdoberfläche","reference":"Bildungsplan BW 2016, Geographie Sek I, IK 5\/6, Kap. 3.1.1.2","reference_url":"https:\/\/www.bildungsplaene-bw.de\/,Lde\/Startseite\/BP2016BW_ALLG\/BP2016BW_ALLG_SEK1_GEO_IK_5-6_01_02","quote":"Die Schülerinnen und Schüler analysieren Entstehung und Verlauf von Hochwasserereignissen und bewerten technische sowie naturnahe Schutzkonzepte.","kompetenz_id":2}
{"id":1110,"country":"DE","subject":"Geographie","school_type":"Gymnasium","grade":"Jg. 89","title":"Naturrisiken in Deutschland — Hochwasser und Muren","reference":"Lehrplan Gymnasium Geographie 2004\/2019 (Sachsen)","reference_url":"https:\/\/www.schulportal.sachsen.de\/lplandb\/lehrplan\/74","quote":"Die Schülerinnen und Schüler analysieren regionale Naturrisiken in Sachsen und Mitteleuropa und diskutieren technische und natürliche Schutzkonzepte.","kompetenz_id":2}
{"id":1130,"country":"DE","subject":"Geographie","school_type":"Gymnasium (Sek I)","grade":"Jg. 510","title":"Kartenarbeit und topografische Grundkenntnisse","reference":"Bildungsplan Gymnasium Sek I — Geographie (Hamburg)","reference_url":"https:\/\/www.hamburg.de\/resource\/blob\/123438\/90e4cf7b25114e17d1ac1f5c19c7b4f2\/geographie-gym-seki-data.pdf","quote":"Die Lernenden nutzen Karten, Gradnetz und digitale Geomedien, um sich räumlich zu orientieren und Lagebeziehungen zu beschreiben.","kompetenz_id":3}
{"id":1144,"country":"DE","subject":"Geographie","school_type":"Gymnasium","grade":"Jg. 7","title":"Stadt- und Raumstrukturen in Europa","reference":"LehrplanPLUS Bayern, Gymnasium Geographie 7","reference_url":"https:\/\/www.lehrplanplus.bayern.de\/fachlehrplan\/gymnasium\/7\/geographie","quote":"Die Schülerinnen und Schüler beschreiben Prozesse der Stadtentwicklung, erklären Zersiedelung und Verdichtung und bewerten nachhaltige Stadtkonzepte.","kompetenz_id":4}
{"id":1148,"country":"DE","subject":"Geographie","school_type":"Gymnasium (Sek I)","grade":"Jg. 710","title":"Hamburg als städtischer Raum — Stadtentwicklung und Nachhaltigkeit","reference":"Bildungsplan Gymnasium Sek I — Geographie (Hamburg)","reference_url":"https:\/\/www.hamburg.de\/resource\/blob\/123438\/90e4cf7b25114e17d1ac1f5c19c7b4f2\/geographie-gym-seki-data.pdf","quote":"Hamburg als verpflichtender Raumbezug: Schülerinnen und Schüler analysieren urbane Strukturen, treffen nachhaltige Gestaltungsentscheidungen und diskutieren Flächennutzungskonflikte.","kompetenz_id":4}
{"id":1091,"country":"DE","subject":"Geographie (national)","school_type":"Mittlerer Schulabschluss","grade":"Jg. 10","title":"Bildungsstandards Geographie — Anthropogener Klimawandel","reference":"DGfG — Bildungsstandards im Fach Geographie für den Mittleren Schulabschluss (10. Aufl. 2020)","reference_url":"https:\/\/geographie.de\/publikationen\/","quote":"Die Bildungsstandards fordern die Analyse des Mensch-Umwelt-Systems und der anthropogenen Einflüsse auf das Klima als Querschnittskompetenz.","kompetenz_id":1}
{"id":1123,"country":"DE","subject":"Geographie (national)","school_type":"Mittlerer Schulabschluss","grade":"Jg. 10","title":"Bildungsstandards Geographie — Naturgefahren und Risikoräume","reference":"DGfG — Bildungsstandards im Fach Geographie (10. Aufl. 2020)","reference_url":"https:\/\/geographie.de\/publikationen\/","quote":"Die DGfG-Standards fordern das Erkennen, Analysieren und Bewerten von Risikoräumen (Hochwasser, Hang­rutschung, Dürre) als Querschnitts­kompetenz der geographischen Bildung.","kompetenz_id":2}
{"id":1141,"country":"DE","subject":"Geographie (national)","school_type":"Mittlerer Schulabschluss","grade":"Jg. 10","title":"Bildungsstandards Geographie — Räumliche Orientierung","reference":"DGfG — Bildungsstandards im Fach Geographie (10. Aufl. 2020)","reference_url":"https:\/\/geographie.de\/publikationen\/","quote":"Räumliche Orientierung (Gradnetz, Karten, Atlanten, GIS) ist eine der sechs Kompetenzbereiche der Bildungsstandards Geographie und damit verbindliche Querschnittsvorgabe.","kompetenz_id":3}
{"id":1159,"country":"DE","subject":"Geographie (national)","school_type":"Mittlerer Schulabschluss","grade":"Jg. 10","title":"Bildungsstandards Geographie — Urbanisierung und Stadtstrukturen","reference":"DGfG — Bildungsstandards im Fach Geographie (10. Aufl. 2020)","reference_url":"https:\/\/geographie.de\/publikationen\/","quote":"Die DGfG-Standards fordern das Analysieren und Bewerten von Urbanisierungsprozessen und Stadt­strukturen im globalen Vergleich als Querschnittskompetenz.","kompetenz_id":4}
{"id":1103,"country":"LI","subject":"BNE — Klimakompetenz","school_type":"Sek I","grade":"7.9. Schulstufe","title":"LiLe — BNE-Rahmen","reference":"Liechtensteiner Lehrplan (LiLe), BNE-Perspektive Klima","reference_url":"https:\/\/fl.lehrplan.ch\/index.php?code=b%7C6%7C4","quote":"Der LiLe orientiert sich am Lehrplan 21 und bindet Klimawandel an die BNE-Perspektive — fächerübergreifend angelegt in RZG, NT, WAH und ERG.","kompetenz_id":1}
{"id":1158,"country":"LI","subject":"BNE — Raumentwicklung Rheintal","school_type":"Sek I","grade":"7.9. Schulstufe","title":"LiLe — BNE-Perspektive Raumentwicklung","reference":"Liechtensteiner Lehrplan (LiLe), BNE-Rahmen","reference_url":"https:\/\/fl.lehrplan.ch\/index.php?code=b%7C6%7C4","quote":"Die Zersiedelung im Rheintal ist ein prägendes Thema für Liechtenstein — der LiLe bindet es als BNE-Querschnitt an Mobilität, Ressourcenverbrauch und politische Gestaltung.","kompetenz_id":4}
{"id":1122,"country":"LI","subject":"BNE — Rheintal und Schutzbauten","school_type":"Sek I","grade":"7.9. Schulstufe","title":"LiLe — BNE-Perspektive Rheintal","reference":"Liechtensteiner Lehrplan (LiLe), BNE-Rahmen","reference_url":"https:\/\/fl.lehrplan.ch\/index.php?code=b%7C6%7C4","quote":"Der LiLe bindet das Thema Hochwasser am Alpenrhein fächerübergreifend an BNE — inkl. Schutzdämmen, Retentionsflächen und der internationalen Zusammenarbeit SchweizLiechtenstein–Österreich.","kompetenz_id":2}
{"id":1088,"country":"LI","subject":"RZG","school_type":"Sek I","grade":"7.9. Schulstufe","title":"Klimawandel — globale und lokale Perspektive","reference":"Liechtensteiner Lehrplan (LiLe) — RZG","reference_url":"https:\/\/fl.lehrplan.ch\/index.php?code=b%7C6%7C4","quote":"Die Lernenden verstehen den anthropogenen Klimawandel und reflektieren Handlungsoptionen auf persönlicher, nationaler und globaler Ebene.","kompetenz_id":1}
{"id":1108,"country":"LI","subject":"RZG","school_type":"Sek I","grade":"7.9. Schulstufe","title":"Hochwasser am Alpenrhein","reference":"Liechtensteiner Lehrplan (LiLe) — Lebensraum Rhein","reference_url":"https:\/\/fl.lehrplan.ch\/index.php?code=b%7C6%7C4","quote":"Die Lernenden erfassen Hochwasser als Naturgefahr im Rheintal und reflektieren die Bedeutung von Schutzdämmen und Retentionsräumen.","kompetenz_id":2}
{"id":1128,"country":"LI","subject":"RZG","school_type":"Sek I","grade":"7.9. Schulstufe","title":"Alpen und Alpenvorland — räumliche Gliederung","reference":"Liechtensteiner Lehrplan (LiLe)","reference_url":"https:\/\/www.lile.li\/","quote":"Die Lernenden beschreiben die Höhenstufen und topografischen Besonderheiten der Alpen und orientieren sich mit Karten und Koordinaten im Alpenraum.","kompetenz_id":3}
{"id":1146,"country":"LI","subject":"RZG","school_type":"Sek I","grade":"7.9. Schulstufe","title":"Siedlungsentwicklung in Liechtenstein","reference":"Liechtensteiner Lehrplan (LiLe) — RZG","reference_url":"https:\/\/fl.lehrplan.ch\/index.php?code=b%7C6%7C4","quote":"Die Lernenden setzen sich mit Zersiedelung und Verdichtung im Rheintal auseinander und diskutieren Konsequenzen für Verkehr, Versorgung und Landschaft.","kompetenz_id":4}
@@ -0,0 +1,242 @@
<?php
// Phase-1-Validierung der lehrplan_anchors gegen Live-URLs und Verordnungstexte
// Erzeugt: lehrplan-validierung-phase1.json
declare(strict_types=1);
$lines = file(__DIR__ . '/anchors-dump.jsonl', FILE_IGNORE_NEW_LINES);
$results = [];
// ---- Verifizierte Fakten aus dieser Validierungssitzung ----
// 1. https://www.ris.bka.gv.at/.../Gesetzesnummer=20007850 -> 200 OK, GeltendeFassung, "Verordnung der Bundesministerin
// fuer Unterricht, Kunst und Kultur ueber die Lehrplaene der Mittelschulen" (BGBl. II 185/2012 in der Fassung BGBl. II 1/2023).
// Offizielle Fachnamen 2023:
// - "Geografie und wirtschaftliche Bildung" (nicht "Geographie und Wirtschaftskunde", nicht "GW")
// - "Geschichte und Politische Bildung" (als EIN Fach)
// - "Digitale Grundbildung" (nicht "Informatische Bildung")
// - "Biologie und Umweltbildung"
// - "Chemie", "Physik", "Mathematik", "Deutsch", "Bewegung und Sport", "Ernaehrung und Haushalt"
// Uebergreifende Themen (NICHT eigenstaendige Faecher, sondern Querschnitt):
// - Informatische Bildung, Medienbildung, Gesundheitsfoerderung, Interkulturelle Bildung,
// Umweltbildung fuer nachhaltige Entwicklung, Verkehrs- und Mobilitaetsbildung,
// Wirtschafts-, Finanz- und Verbraucher/innenbildung, Politische Bildung (als reiner Querschnitt)
// 2. https://geographie.de/publikationen/ -> 404 Not Found (bestaetigt 2026-06-08).
// Alternativ-URL: https://vgdh.de/wp-content/uploads/2023/08/Bildungsstandards_Geographie_2020_Web.pdf (DGfG 2020, 10. Auflage)
// 3. https://kultus.hessen.de/.../sekundarstufe-i-kerncurricula -> 200 OK, listet Erdkunde Sek I PDFs (HS/RS/Gym).
// 4. https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/{7,9}/geographie -> 200 OK, "LehrplanPLUS" Bayern, aktuell (Schuljahr 2024/25 Flexibilisierung).
// 5. https://www.bildungsplaene-bw.de/.../BP2016BW_ALLG_SEK1_GEO_IK_5-6_01_02 -> 200 OK, BP 2016 BW.
// 6. https://lehrplannavigator.nrw.de/sekundarstufe-i/kernlehrplaene-fuer-das-gymnasium-aufsteigend-ab-20192020 -> 200 OK,
// Kernlehrplan Erdkunde Gymnasium SI (2019) verlinkt.
// 7. https://static.bildung-rp.de/.../Ek_G_Sk_Sek_I__LP_2021_.pdf -> 200 OK (PDF Binary), Dateiname suggeriert RP 2021,
// Inhalt nicht extrahierbar via WebFetch -> needs_human_review fuer Quote-Verifikation.
// 8. https://bildungsserver.berlin-brandenburg.de/unterricht/rahmenlehrplaene -> 200 OK,
// Berlin/Brandenburg Portalseite, verlinkt zu Rahmenlehrplaenen (kein Direkt-PDF zu Geografie Teil C).
// 9. https://www.hamburg.de/.../geographie-gym-seki-data.pdf -> 200 OK (PDF Binary), HH Geographie Gym Sek I,
// Volltext nicht extrahierbar -> needs_human_review fuer Quote-Verifikation.
// 10. https://www.schulportal.sachsen.de/lplandb/lehrplan/74 -> 200 OK, "Lehrplan Gymnasium Geographie",
// gueltig 2004/2009/2011/2019. Aelter als BW/Bayern/HE, aber LAUT QUELLE NOCH GUELTIG.
// -> Markierung "version_outdated" NICHT angebracht (er gilt offiziell). Hinweis in details.
// 11. https://cuvo.nibis.de/cuvo.php?p=download&upload=61 -> HTTP 403 fuer WebFetch (Bot-Block).
// URL-Pfad ueber sicheres Portal (cuvo.nibis.de = Niedersaechsisches Kerncurriculum-Portal).
// -> needs_human_review fuer URL-Liveness (nicht definitiv tot, nur fuer Bots blockiert).
// 12. https://v-fe.lehrplan.ch/index.php?code=... und https://fl.lehrplan.ch/index.php?code=... -> HTTP 403 fuer WebFetch.
// v-fe.lehrplan.ch = "Vorlage Lehrplan 21" (Generische Vorlage, Kantone forken davon).
// fl.lehrplan.ch = LiLe (Liechtensteiner Lehrplan, basiert auf Lehrplan 21, seit 2019 in Kraft).
// Beide Domains sind gemaess Web-Recherche aktiv -> needs_human_review fuer URL-Liveness.
// 13. https://www.lile.li/ -> 200 OK, Schulamt Liechtenstein, Portal-Seite (kein Direkt-Lehrplan-Pfad).
// 14. AT-Quotes sind in der Datenbank ueberwiegend als REDAKTIONELLE PARAPHRASEN formuliert
// ("Klimawandel wird im Fach Biologie und Umweltbildung ueber Oekosysteme ... behandelt"),
// nicht als woertliche Zitate aus dem Verordnungstext. Ohne Volltext-Diff
// kann ich Quote-Treue nicht abschliessend pruefen -> "needs_human_review" mit Hinweis,
// ausser bei direkt erkennbar woertlichen Passagen aus RIS-Verordnung.
// Direkte Zitate (erkennbar an Lehrplan-Formulierung): IDs 1082, 1083, 1084, 1104, 1105,
// 1124, 1125, 1142, 1143, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170,
// 1171, 1172, 1182, 1183, 1184, 1190, 1191, 1198, 1199, 1200, 1206, 1209, 1210, 1213, 1214,
// 1215, 1221.
// Subject-Whitelisten pro Land
$at_subjects_official_facher = [
'Geografie und wirtschaftliche Bildung','Geschichte und Politische Bildung','Geschichte und politische Bildung',
'Digitale Grundbildung','Biologie und Umweltbildung','Chemie','Physik','Mathematik','Deutsch',
'Bewegung und Sport','Ernaehrung und Haushalt','Ernährung und Haushalt','Kunst und Gestaltung','Musik','Technik und Design',
];
$at_subjects_querschnitt = [
'Umweltbildung für nachhaltige Entwicklung','Verkehrs- und Mobilitätsbildung',
'Wirtschafts-, Finanz- und Verbraucher:innenbildung','Gesundheitsförderung','Medienbildung',
'Interkulturelle Bildung','Informatische Bildung','Politische Bildung',
];
$ch_subjects_lehrplan21 = [
'RZG','RZG (Räume, Zeiten, Gesellschaften)','Natur und Technik (NT)','Wirtschaft, Arbeit, Haushalt (WAH)',
'Mathematik','Medien und Informatik (MI)','Ethik, Religionen, Gemeinschaft (ERG)',
'BNE — Natürliche Umwelt und Ressourcen','BNE — Wirtschaft und Konsum',
'Überfachliche Kompetenzen — Methodische',
];
$li_subjects = ['RZG','BNE — Klimakompetenz','BNE — Raumentwicklung Rheintal','BNE — Rheintal und Schutzbauten'];
// URL-Status-Map (verifiziert in dieser Session)
$url_status = [
'https://www.ris.bka.gv.at/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850' => 'live',
'https://geographie.de/publikationen/' => 'dead404',
'https://kultus.hessen.de/unterricht/kerncurricula-und-lehrplaene/kerncurricula/sekundarstufe-i-kerncurricula' => 'live',
'https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/9/geographie' => 'live',
'https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/7/geographie' => 'live',
'https://www.bildungsplaene-bw.de/,Lde/Startseite/BP2016BW_ALLG/BP2016BW_ALLG_SEK1_GEO_IK_5-6_01_02' => 'live',
'https://lehrplannavigator.nrw.de/sekundarstufe-i/kernlehrplaene-fuer-das-gymnasium-aufsteigend-ab-20192020' => 'live',
'https://static.bildung-rp.de/lehrplaene/gesellschaftswissenschaften/Ek_G_Sk_Sek_I__LP_2021_.pdf' => 'live_pdf_unread',
'https://bildungsserver.berlin-brandenburg.de/unterricht/rahmenlehrplaene' => 'live_portal',
'https://www.hamburg.de/resource/blob/123438/90e4cf7b25114e17d1ac1f5c19c7b4f2/geographie-gym-seki-data.pdf' => 'live_pdf_unread',
'https://www.schulportal.sachsen.de/lplandb/lehrplan/74' => 'live',
'https://cuvo.nibis.de/cuvo.php?p=download&upload=61' => 'botblock_403',
'https://www.lile.li/' => 'live_portal',
];
// Direkt-Zitate aus AT-RIS (erkennbar woertliche Lehrplan-Formulierungen)
$at_direct_quotes = [1082,1083,1084,1104,1105,1124,1125,1142,1143,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1182,1183,1184,1190,1191,1198,1199,1200,1206,1209,1210,1213,1214,1215,1221];
foreach ($lines as $l) {
$r = json_decode($l, true);
$id = (int)$r['id'];
$country = $r['country'];
$subject = $r['subject'];
$url = $r['reference_url'];
$status = null;
$details = [];
$fix = null;
// Parse host for lehrplan.ch family
$host = parse_url($url, PHP_URL_HOST);
$is_lehrplan_ch = ($host === 'v-fe.lehrplan.ch' || $host === 'fl.lehrplan.ch');
// -- 1. URL-Status pruefen
$url_state = $url_status[$url] ?? ($is_lehrplan_ch ? 'botblock_403' : 'unknown');
if ($url_state === 'dead404') {
$status = 'url_dead';
$details[] = 'reference_url liefert HTTP 404 (verifiziert 2026-06-08 via WebFetch).';
if (str_contains($url, 'geographie.de/publikationen/')) {
$fix = 'reference_url ersetzen durch https://vgdh.de/wp-content/uploads/2023/08/Bildungsstandards_Geographie_2020_Web.pdf (DGfG 2020, 10. Auflage, durch WebSearch bestaetigt)';
}
}
// -- 2. AT-Subject pruefen
if ($status === null && $country === 'AT') {
if ($subject === 'GW') {
// Bekannter Lehrer-Code, ABER seit Lehrplan 2023 heisst das Fach offiziell "Geografie und wirtschaftliche Bildung".
$status = 'needs_human_review';
$details[] = 'Subject "GW" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1/2023 lautet "Geografie und wirtschaftliche Bildung". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).';
$fix = 'Optional: Subject auf "Geografie und wirtschaftliche Bildung" angleichen (offizielle Bezeichnung 2023).';
} elseif ($subject === 'Politische Bildung') {
// Politische Bildung ist KEIN eigenstaendiges Fach mehr, sondern Teil von "Geschichte und Politische Bildung" und Querschnittsthema.
$status = 'subject_wrong';
$details[] = 'Im AT-Lehrplan 2023 (BGBl. II 1/2023) gibt es kein eigenstaendiges Pflichtfach "Politische Bildung". Politische Bildung ist (a) Teil des Fachs "Geschichte und Politische Bildung" und (b) Querschnittsthema. Reference-URL ist korrekt (RIS), aber Subject suggeriert ein nicht existierendes Fach.';
$fix = 'Subject auf "Geschichte und Politische Bildung" aendern ODER Quelle als Querschnittsthema kennzeichnen (z. B. "Querschnitt: Politische Bildung").';
} elseif ($subject === 'Informatische Bildung') {
// Im 2023er Lehrplan: Querschnittsthema; das eigenstaendige Pflichtfach heisst "Digitale Grundbildung".
$status = 'subject_wrong';
$details[] = 'Im AT-Lehrplan 2023 ist "Informatische Bildung" Querschnittsthema; das eigenstaendige Pflichtfach heisst "Digitale Grundbildung". Wenn der Anker ein Fach meinen soll, ist die Bezeichnung falsch.';
$fix = 'Subject auf "Digitale Grundbildung" aendern (wenn Fach gemeint) ODER explizit als "Querschnitt: Informatische Bildung" auszeichnen.';
} elseif (in_array($subject, $at_subjects_official_facher, true) || in_array($subject, $at_subjects_querschnitt, true)) {
// Subject ist entweder offizielles Fach oder dokumentiertes Querschnittsthema in der Verordnung.
if (in_array($id, $at_direct_quotes, true)) {
$status = 'ok';
$details[] = 'Subject in BGBl. II 1/2023 dokumentiert (Fach oder Querschnittsthema). Quote folgt Lehrplan-Formulierung (Direkt-/Naheparaphrase). reference_url live.';
} else {
$status = 'needs_human_review';
$details[] = 'Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden.';
}
} else {
$status = 'needs_human_review';
$details[] = 'Subject "' . $subject . '" nicht in der bestaetigten AT-Fach-/Querschnitts-Liste. Manuell pruefen.';
}
}
// -- 3. CH-Subject + URL
if ($status === null && $country === 'CH') {
if (!in_array($subject, $ch_subjects_lehrplan21, true)) {
$status = 'needs_human_review';
$details[] = 'Subject "' . $subject . '" nicht in der Standard-Lehrplan-21-Fachbereichsliste. Manuell pruefen.';
} elseif (str_contains($subject, 'BNE')) {
// BNE ist im Lehrplan 21 Leitidee, nicht eigenstaendiger Code e|200|1. URL-Code ggf. nicht passend.
$status = 'needs_human_review';
$details[] = 'BNE ist im Lehrplan 21 als Leitidee/Querschnitt verankert, nicht als eigenstaendiger Fachbereich. Der URL-Code "e|200|1" verweist auf eine Lehrplan-Vorlagen-Sicht (v-fe = Vorlage Fremdevaluation/Entwurf). Aktuellster Ankerpunkt zu BNE ware lehrplan.ch/index.php?nav=overview oder kantonale Spezifizierungen. URL-Liveness wegen Bot-Block 403 nicht automatisch verifizierbar.';
$fix = 'Pruefen, ob ein expliziter LP21-BNE-Querverweis (z. B. https://www.lehrplan21.ch/grundlagen-bne) sinnvoller ist als der v-fe-Code.';
} else {
$status = 'needs_human_review';
$details[] = 'Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden.';
}
}
// -- 4. LI-Subject + URL
if ($status === null && $country === 'LI') {
if (!in_array($subject, $li_subjects, true)) {
$status = 'needs_human_review';
$details[] = 'Subject "' . $subject . '" liechtensteinspezifisch, nicht automatisch bewertbar.';
} elseif (str_contains($subject, 'BNE')) {
$status = 'needs_human_review';
$details[] = 'BNE im LiLe als Querschnittsperspektive vorhanden, aber URL-Code b|6|4 zeigt RZG (Fachbereich), nicht den BNE-Querverweis. Inhaltliche Bindung Geografie<->BNE ist plausibel, aber Quote/URL koppeln BNE-Marker an RZG-Seite. Manuell pruefen. URL-Liveness wegen Bot-Block nicht automatisch verifizierbar (fl.lehrplan.ch laut Web-Recherche aktiv).';
} else {
// LI:RZG
$status = 'needs_human_review';
$details[] = 'LiLe basiert auf Lehrplan 21 und ist seit 2019 gueltig. Subject RZG passt. URL-Liveness wegen Bot-Block (403) nicht automatisch verifiziert; Domain laut Web-Recherche aktiv. Quote ist redaktionelle Bruecke, nicht woertlich aus LiLe.';
}
}
// -- 5. DE-Subject + URL
if ($status === null && $country === 'DE') {
if ($subject === 'Erdkunde' || $subject === 'Geographie') {
// Beide Bezeichnungen je Bundesland korrekt.
if ($url_state === 'live') {
$status = 'ok';
$details[] = 'reference_url ist live (Portal-/Plan-Seite). Subject "' . $subject . '" entspricht offizieller Bezeichnung des verlinkten Bundesland-Lehrplans (HE/NRW: Erdkunde; BW/BY/HH/SN: Geographie; RP: Erdkunde im Verbund Gesellschaftswissenschaften).';
if (str_contains($url, 'schulportal.sachsen.de')) {
$details[] = 'Hinweis: SN-Lehrplan ist 2004 erstmals erlassen, mit Ueberarbeitungen 2009/2011/2019. Offiziell weiter gueltig laut Schulportal Sachsen - kein "version_outdated", aber inhaltlich der aelteste aktive Plan im Set.';
}
if ($url === 'https://kultus.hessen.de/unterricht/kerncurricula-und-lehrplaene/kerncurricula/sekundarstufe-i-kerncurricula') {
$details[] = 'Hinweis: Reference-URL ist Uebersichtsseite; das eigentliche KC-PDF (HS/RS/Gym) wird von dort verlinkt, aber nicht direkt angesteuert. Praeziserer Direkt-Link zu HE-Erdkunde-KC waere besser.';
}
} elseif ($url_state === 'live_portal') {
$status = 'needs_human_review';
$details[] = 'reference_url ist Portal-/Uebersichtsseite (bildungsserver.berlin-brandenburg.de/unterricht/rahmenlehrplaene), nicht der RLP-Teil C Geografie direkt. Der RLP 1-10 Berlin/Brandenburg ist gueltig (2017+), aber URL ist zu unspezifisch.';
$fix = 'Auf Direkt-PDF zu "Teil C Geografie" des Rahmenlehrplans 1-10 Berlin-Brandenburg verlinken (z. B. bildungsserver.berlin-brandenburg.de/.../rlp-1-10-online).';
} elseif ($url_state === 'live_pdf_unread') {
$status = 'needs_human_review';
$details[] = 'reference_url liefert PDF (200 OK), Volltext aus Bot-Sicht nicht extrahierbar. Dateiname und Titel suggerieren passendes Bundesland-/Fach-Dokument, aber Quote muss manuell gegen den PDF-Inhalt verglichen werden.';
} elseif ($url_state === 'botblock_403') {
$status = 'needs_human_review';
$details[] = 'reference_url WebFetch HTTP 403 (Bot-Block). Portal cuvo.nibis.de ist Niedersaechsisches Curriculum-Portal, gemaess Web-Recherche aktiv. URL-Liveness manuell verifizieren; Quote manuell pruefen.';
} else {
$status = 'needs_human_review';
$details[] = 'URL-Status unklar, manuell pruefen.';
}
} elseif ($subject === 'Geographie (national)') {
// bereits oben als url_dead behandelt
$status = $status ?? 'needs_human_review';
$details[] = 'DGfG-Bildungsstandards sind nationaler KMK-naher Bezugsrahmen, aber kein verbindlicher Lehrplan. Subject-Markierung "Geographie (national)" ist konzeptionell ok, sofern erkennbar.';
}
}
if ($status === null) {
$status = 'needs_human_review';
$details[] = 'Klassifikator hat keinen Treffer.';
}
$entry = [
'id' => $id,
'status' => $status,
'details' => implode(' ', $details),
];
if ($fix !== null) $entry['fix_suggestion'] = $fix;
$results[] = $entry;
}
// Sortiere nach id
usort($results, fn($a,$b)=>$a['id']<=>$b['id']);
file_put_contents(__DIR__ . '/lehrplan-validierung-phase1.json', json_encode($results, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT));
// Summary
$by_status = [];
foreach ($results as $r) $by_status[$r['status']] = ($by_status[$r['status']]??0)+1;
echo "TOTAL: ".count($results)."\n";
foreach ($by_status as $s=>$c) echo " $s: $c\n";
+139
View File
@@ -0,0 +1,139 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Entwurfswerkzeug · GeoGraSim</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;background:#f5f2eb;color:#2a2a2a;line-height:1.5;padding:24px;max-width:1100px;margin:0 auto}
h1{color:#1f4e5a;font-size:1.8rem;margin-bottom:4px}
.sub{font-size:0.85rem;color:#8a8a8a;margin-bottom:18px}
h2{color:#1f4e5a;font-size:1.15rem;margin:24px 0 8px;padding-bottom:4px;border-bottom:2px solid #4a7c8a}
h3{color:#4a7c8a;font-size:0.98rem;margin:18px 0 8px}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px;margin:10px 0}
.card{background:#fff;border:1px solid #e0ddd4;border-radius:8px;padding:14px 16px;text-decoration:none;color:inherit;display:block;transition:transform .12s,border-color .12s}
.card:hover{transform:translateY(-2px);border-color:#4a7c8a}
.card h4{color:#1f4e5a;font-size:.98rem;margin-bottom:4px}
.card p{font-size:.82rem;color:#666;line-height:1.4}
.card .meta{font-size:.7rem;color:#8a8a8a;margin-top:6px;font-family:'JetBrains Mono',Consolas,monospace}
table{width:100%;border-collapse:collapse;font-size:.86rem;margin:6px 0;background:#fff;border-radius:8px;overflow:hidden}
th,td{text-align:left;padding:8px 12px;border-bottom:1px solid #e0ddd4;vertical-align:top}
th{background:#1f4e5a;color:#fff;font-weight:600;font-size:.78rem;text-transform:uppercase}
tr:last-child td{border-bottom:none}
code{background:#f5f2eb;padding:2px 6px;border-radius:3px;font-family:'JetBrains Mono',Consolas,monospace;font-size:.85em}
a{color:#1f4e5a}
.badge{display:inline-block;font-size:.7rem;font-weight:700;padding:2px 7px;border-radius:9999px;margin-right:4px}
.b-delete{background:#fde0db;color:#9c4d2e}
.b-legacy{background:#e7e3d6;color:#6b6448}
.b-archive{background:#e3e9d6;color:#4a6b3f}
.b-tool{background:#d6e3e9;color:#1f4e5a}
.b-info{background:#e8c547;color:#5a4416}
.note-box{background:#fff7e0;border-left:4px solid #d2a24c;padding:12px 16px;margin:14px 0;border-radius:0 8px 8px 0;font-size:.92rem}
.top-nav{background:#fff;border:1px solid #e0ddd4;border-radius:8px;padding:10px 14px;margin-bottom:18px;font-size:.88rem;display:flex;flex-wrap:wrap;gap:14px;align-items:center}
.top-nav a{text-decoration:none;padding:6px 12px;border-radius:6px}
.top-nav a:hover{background:#f5f2eb}
.top-nav a.active{background:#1f4e5a;color:#fff}
.meta-line{font-size:.78rem;color:#8a8a8a;margin-top:2px}
</style>
</head>
<body>
<nav class="top-nav">
<strong>🛠 Entwurfswerkzeug</strong>
<a href="index.html" class="active">Übersicht</a>
<a href="../entwurf.html">Entwurf</a>
<a href="../review.html">Review</a>
<a href="accounts.html">🔑 Accounts</a>
<a href="standards.html">📐 Standards</a>
<a href="legacy/" style="color:#6b6448">📂 legacy/</a>
<a href="toDelete/" style="color:#9c4d2e">📂 toDelete/</a>
<a href="../../App/admin-sitemap.html" target="_blank">📊 Sitemap</a>
</nav>
<h1>🛠 Entwurfswerkzeug</h1>
<p class="sub">Lokaler Arbeitsbereich für Entwicklungs-Doku, Legacy-Sichtung und Lösch-Kandidaten. Liegt unter <code>.dontDeploy/entwurfswerkzeug/</code> — wird nie deployed.</p>
<div class="note-box">
<b>Prinzip:</b> Nichts wird gelöscht. Alles, was aus dem Prod-Verzeichnis rausfällt, landet hier — zur späteren Entscheidung.
Wenn du ein File brauchst: einfach zurückverschieben mit <code>git mv .dontDeploy/entwurfswerkzeug/toDelete/X.html App/X.html</code>.
</div>
<h2>📁 Hauptbereiche</h2>
<div class="grid">
<a class="card" href="legacy/">
<h4><span class="badge b-legacy">legacy</span> Sichtungs-Halde</h4>
<p>Test-Tools, Demo-Mockups, alte Versionen — zum Ansehen noch da, aber aus der Prod entfernt.</p>
<div class="meta">.dontDeploy/entwurfswerkzeug/legacy/</div>
</a>
<a class="card" href="toDelete/">
<h4><span class="badge b-delete">toDelete</span> Lösch-Kandidaten</h4>
<p>Stand: Subagent-Sweep 2026-06-08. 11 Standalone-HTMLs vom Wrapper-System abgelöst. Warten auf Lösch-Freigabe.</p>
<div class="meta">.dontDeploy/entwurfswerkzeug/toDelete/</div>
</a>
<a class="card" href="legacy/sims-archive/">
<h4><span class="badge b-archive">sims-archive</span> Alte Sim-Entwürfe</h4>
<p>Vorgänger-Stände einzelner Sims (z.B. Klima-Plane-Test). Pro Sim ein Ordner.</p>
<div class="meta">legacy/sims-archive/</div>
</a>
<a class="card" href="../entwurf.html">
<h4><span class="badge b-tool">entwurf.html</span> Architektur-Doku</h4>
<p>Lebendes Doku-File. Codes A/B/C/D/K/L/N/Z, Konsistenz-Matrix, Sitemap, Change-Log.</p>
<div class="meta">.dontDeploy/entwurf.html</div>
</a>
<a class="card" href="../review.html">
<h4><span class="badge b-tool">review.html</span> Review-Notizen</h4>
<p>Iterations-Notizen + Stand der offenen Themen.</p>
<div class="meta">.dontDeploy/review.html</div>
</a>
<a class="card" href="../../App/admin-sitemap.html" target="_blank">
<h4><span class="badge b-info">admin-sitemap</span> Live-Sitemap</h4>
<p>Vollständige Übersicht aller 81 Prod-Seiten + Tote Links + Orphan-Liste. Wird im App-Admin-Bereich ausgeliefert.</p>
<div class="meta">App/admin-sitemap.html</div>
</a>
<a class="card" href="accounts.html">
<h4><span class="badge b-info">accounts</span> 🔑 Test-Accounts</h4>
<p>Schneller Login-Überblick: 7 Lehrer + 16 Schüler lokal, 17 Lehrer + ~70 Schüler auf Prod. Klassen-Codes, Passwörter, Display-Namen.</p>
<div class="meta">.dontDeploy/entwurfswerkzeug/accounts.html</div>
</a>
</div>
<h2>📋 toDelete/ — Aktuelle Lösch-Kandidaten</h2>
<table>
<thead><tr><th>Datei</th><th>Was es war</th><th>Status / Empfehlung</th></tr></thead>
<tbody>
<tr><td><code>dashboard.html</code></td><td>Demo-Mockup mit „alle Daten erfunden"-Banner</td><td>Ersetzt durch <code>teacher.html</code>. Endgültig löschbar.</td></tr>
<tr><td><code>game.html</code></td><td>Klima-2D Standalone (vor Wrapper-System)</td><td>Wrapper <code>pages/klima.php</code> ist Production. Endgültig löschbar.</td></tr>
<tr><td><code>game-3d.html</code></td><td>Klima-3D Standalone</td><td>Wrapper <code>pages/klima-3d.php</code> ist Production. Endgültig löschbar.</td></tr>
<tr><td><code>fluss.html</code></td><td>Flussmanagement Standalone</td><td>Wrapper <code>pages/fluss.php</code> ist Production. Endgültig löschbar.</td></tr>
<tr><td><code>heli.html</code></td><td>Heli-Navigation Standalone</td><td>Wrapper <code>pages/heli.php</code> ist Production. Endgültig löschbar.</td></tr>
<tr><td><code>stadt.html</code></td><td>Stadt-Simulation Standalone (nie fertig)</td><td>Stadt ist noch in Entwurf — beim Reaktivieren ggf. zurückholen.</td></tr>
<tr><td><code>erdbeben.html</code></td><td>Konzept-Stub Erdbeben-Sim</td><td>Konzept nie umgesetzt. Löschbar.</td></tr>
<tr><td><code>energiemix.html</code></td><td>Konzept-Stub Energiemix</td><td>Ersetzt durch <code>energiemanager</code>. Löschbar.</td></tr>
<tr><td><code>lieferketten.html</code></td><td>Konzept-Stub Lieferketten</td><td>Konzept nie umgesetzt. Löschbar.</td></tr>
<tr><td><code>regenwald.html</code></td><td>Konzept-Stub Regenwald</td><td>Konzept nie umgesetzt. Löschbar.</td></tr>
</tbody>
</table>
<h2>📋 legacy/ — Sichtungs-Material (nicht löschen)</h2>
<table>
<thead><tr><th>Datei</th><th>Zweck</th><th>Empfehlung</th></tr></thead>
<tbody>
<tr><td><code>tile-test.html</code></td><td>Test-Tool für Tile-Layouts</td><td>Behalten — gelegentlich nützlich beim Layout-Debugging.</td></tr>
<tr><td><code>sims-archive/klima-plane-test.html</code></td><td>Klima-3D Plane-Test (alter Stand)</td><td>Behalten als Referenz, falls 3D-Logik überarbeitet wird.</td></tr>
</tbody>
</table>
<h2>🔗 Quick-Links</h2>
<div class="grid">
<a class="card" href="http://localhost/geograsim/App/" target="_blank"><h4>🌐 Lokaler Sim-Server</h4><p>Lokale Prod-Sicht der App.</p></a>
<a class="card" href="https://geograsim.at/" target="_blank"><h4>🌐 Production</h4><p>geograsim.at — Test+Live.</p></a>
<a class="card" href="http://localhost/geograsim/App/admin-sitemap.html" target="_blank"><h4>📊 Sitemap</h4><p>Vollständige Page-Übersicht inkl. Tote-Links.</p></a>
<a class="card" href="http://localhost/geograsim/App/admin-styleguide.html" target="_blank"><h4>🎨 Styleguide</h4><p>Design-Tokens, Komponenten.</p></a>
</div>
<footer style="margin:32px 0 24px;text-align:center;font-size:.78rem;color:#8a8a8a">
Erstellt: 2026-06-08 · Stand-Sweep: 11 Pages in <code>toDelete/</code>, 2 in <code>legacy/</code>.
</footer>
</body>
</html>
@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>legacy/ · Entwurfswerkzeug</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;background:#f5f2eb;color:#2a2a2a;line-height:1.5;padding:24px;max-width:1100px;margin:0 auto}
h1{color:#1f4e5a;font-size:1.5rem;margin-bottom:4px}
.sub{font-size:.82rem;color:#8a8a8a;margin-bottom:14px}
h2{color:#1f4e5a;font-size:1.05rem;margin:20px 0 8px;padding-bottom:4px;border-bottom:2px solid #4a7c8a}
table{width:100%;border-collapse:collapse;font-size:.86rem;background:#fff;border-radius:6px;overflow:hidden}
th,td{text-align:left;padding:8px 12px;border-bottom:1px solid #e0ddd4;vertical-align:top}
th{background:#6b6448;color:#fff;font-weight:600;font-size:.74rem;text-transform:uppercase;letter-spacing:.04em}
tr:last-child td{border-bottom:none}
code{background:#f5f2eb;padding:2px 6px;border-radius:3px;font-family:'JetBrains Mono',Consolas,monospace;font-size:.85em}
a{color:#1f4e5a}
.note-box{background:#fff7e0;border-left:4px solid #d2a24c;padding:10px 14px;margin:12px 0;border-radius:0 6px 6px 0;font-size:.86rem}
/* einheitliches Entwurfswerkzeug-Menü */
.top-nav{background:#fff;border:1px solid #e0ddd4;border-radius:8px;padding:10px 14px;margin-bottom:18px;font-size:.86rem;display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.top-nav strong{color:#1f4e5a}
.top-nav a{text-decoration:none;padding:5px 11px;border-radius:6px;color:#1f4e5a}
.top-nav a:hover{background:#f5f2eb}
.top-nav a.active{background:#1f4e5a;color:#fff}
</style>
</head>
<body>
<nav class="top-nav">
<strong>🛠 Entwurfswerkzeug</strong>
<a href="../index.html">Übersicht</a>
<a href="../../entwurf.html">Entwurf</a>
<a href="../../review.html">Review</a>
<a href="../accounts.html">🔑 Accounts</a>
<a href="index.html" class="active" style="background:#6b6448;color:#fff">📂 legacy/</a>
<a href="../toDelete/" style="color:#9c4d2e">📂 toDelete/</a>
<a href="../../../App/admin-sitemap.html" target="_blank">📊 Sitemap</a>
</nav>
<h1>📂 legacy/</h1>
<p class="sub">Sichtungs-Material — alte Test-Tools, Stände, gelegentlich nützlich. Wird <strong>behalten</strong>, nicht gelöscht.</p>
<div class="note-box">
Im Unterschied zu <code>toDelete/</code>: was hier liegt, hat noch eine zukünftige Funktion (Layout-Debugging, Referenz-Stand).
</div>
<h2>📄 Dateien</h2>
<table>
<thead><tr><th>Datei</th><th>Zweck</th><th>Stand</th></tr></thead>
<tbody>
<tr>
<td><a href="tile-test.html"><code>tile-test.html</code></a></td>
<td>Test-Tool für Tile-Layouts (Sim-Card-Grid auf der Startseite)</td>
<td>aus Prod entfernt 2026-06-08</td>
</tr>
</tbody>
</table>
<h2>📁 Unterordner</h2>
<table>
<thead><tr><th>Pfad</th><th>Inhalt</th></tr></thead>
<tbody>
<tr>
<td><a href="sims-archive/"><code>sims-archive/</code></a></td>
<td>Vorgänger-Stände einzelner Sims. Bisher: <code>klima-plane-test.html</code> (alter 3D-Plane-Test).</td>
</tr>
</tbody>
</table>
<footer style="margin:24px 0;text-align:center;font-size:.75rem;color:#8a8a8a">
Liegt unter <code>.dontDeploy/entwurfswerkzeug/legacy/</code> · wird nicht deployed.
</footer>
</body>
</html>
@@ -0,0 +1,232 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Flugzeug-Orientierungs-Test</title>
<style>
body { margin: 0; font-family: sans-serif; background: #e4ecf0; }
#scene { width: 100vw; height: calc(100dvh - 200px); }
#controls {
position: fixed; bottom: 0; left: 0; right: 0; height: 200px;
background: #1a2a32; color: #fff; padding: 14px 22px;
display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 10px 24px;
box-shadow: 0 -4px 12px rgba(0,0,0,0.3);
}
.slider-row { display: flex; flex-direction: column; gap: 2px; }
.slider-row label { font-size: 12px; font-weight: 700; color: #b8d0e0; }
.slider-row input[type=range] { width: 100%; }
.slider-row .val { font-family: monospace; font-size: 13px; color: #7cfae0; }
.note { grid-column: 1 / -1; font-size: 12px; color: #a0b8c8; line-height: 1.5; }
.big { grid-column: 1 / -1; text-align: center; font-size: 15px; font-weight: 700; }
button { padding: 6px 14px; background: #3a7a8a; color: #fff; border: none;
border-radius: 5px; font-weight: 700; cursor: pointer; }
button:hover { background: #4a8a9a; }
</style>
</head>
<body>
<div id="scene"></div>
<div id="controls">
<div class="big">Flugzeug-Orientierungs-Test · Bewegungsrichtung = roter Pfeil (→ weltliches +X)</div>
<div class="slider-row">
<label>rotation.x (Pitch) · <span class="val" id="val-x">0.000</span></label>
<input id="rx" type="range" min="-3.14" max="3.14" step="0.01" value="0">
</div>
<div class="slider-row">
<label>rotation.y (Yaw) · <span class="val" id="val-y">0.000</span></label>
<input id="ry" type="range" min="-3.14" max="3.14" step="0.01" value="0">
</div>
<div class="slider-row">
<label>rotation.z (Roll) · <span class="val" id="val-z">0.000</span></label>
<input id="rz" type="range" min="-3.14" max="3.14" step="0.01" value="0">
</div>
<div class="slider-row">
<button id="btn-animate">Flug simulieren</button>
<button id="btn-reset">Reset</button>
<button id="btn-copy">Werte kopieren</button>
</div>
<div class="note">
Der rote Pfeil zeigt die Bewegungsrichtung bei „Flug simulieren".<br>
Finde die rotation.y-Werte, bei denen die Nase des Flugzeugs GENAU auf dem Pfeil steht (vorwärts fliegt),
und meld mir die Werte (oder drück „Werte kopieren"). π ≈ 3.14, π/2 ≈ 1.57.
</div>
</div>
<script src="https://unpkg.com/three@0.155.0/build/three.min.js"></script>
<script>
'use strict';
// --- Scene ---
const host = document.getElementById('scene');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xbcd5e6);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(2, devicePixelRatio));
renderer.setSize(host.clientWidth, host.clientHeight);
host.appendChild(renderer.domElement);
const camera = new THREE.PerspectiveCamera(45, host.clientWidth / host.clientHeight, 0.1, 100);
camera.position.set(4, 4, 4);
camera.lookAt(0, 0, 0);
addEventListener('resize', () => {
renderer.setSize(host.clientWidth, host.clientHeight);
camera.aspect = host.clientWidth / host.clientHeight;
camera.updateProjectionMatrix();
});
// Orbit (drag)
let dragging = false, lastX = 0, lastY = 0, theta = Math.PI / 4, phi = Math.PI / 4, r = 7;
function apply() {
camera.position.set(
Math.sin(theta) * Math.sin(phi) * r,
Math.cos(phi) * r,
Math.cos(theta) * Math.sin(phi) * r
);
camera.lookAt(0, 0.3, 0);
}
apply();
renderer.domElement.addEventListener('pointerdown', (e) => { dragging = true; lastX = e.clientX; lastY = e.clientY; });
addEventListener('pointermove', (e) => {
if (!dragging) return;
theta -= (e.clientX - lastX) * 0.005;
phi = Math.max(0.1, Math.min(Math.PI * 0.48, phi - (e.clientY - lastY) * 0.005));
lastX = e.clientX; lastY = e.clientY;
apply();
});
addEventListener('pointerup', () => { dragging = false; });
renderer.domElement.addEventListener('wheel', (e) => {
e.preventDefault();
r = Math.max(2, Math.min(20, r + e.deltaY * 0.01));
apply();
}, { passive: false });
// Licht
scene.add(new THREE.AmbientLight(0xfff0d8, 0.8));
const sun = new THREE.DirectionalLight(0xffe8c0, 1.0);
sun.position.set(5, 8, 3); scene.add(sun);
// Boden-Grid
const grid = new THREE.GridHelper(20, 20, 0x808080, 0xb0b0b0);
grid.position.y = -0.5;
scene.add(grid);
// Achsen-Helper: X rot, Y grün, Z blau
const axes = new THREE.AxesHelper(1.5);
axes.position.y = -0.49;
scene.add(axes);
// Achsen-Beschriftungen (kleine Sprites)
function labelSprite(text, color) {
const c = document.createElement('canvas');
c.width = c.height = 64;
const ctx = c.getContext('2d');
ctx.font = 'bold 44px sans-serif';
ctx.fillStyle = color;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(text, 32, 32);
const tex = new THREE.CanvasTexture(c);
const mat = new THREE.SpriteMaterial({ map: tex });
return new THREE.Sprite(mat);
}
const lx = labelSprite('+X (fwd)', '#c03030'); lx.position.set(1.8, -0.2, 0); lx.scale.set(0.8, 0.8, 1); scene.add(lx);
const ly = labelSprite('+Y (up)', '#309030'); ly.position.set(0, 1.8, 0); ly.scale.set(0.8, 0.8, 1); scene.add(ly);
const lz = labelSprite('+Z', '#3030c0'); lz.position.set(0, -0.2, 1.8); lz.scale.set(0.8, 0.8, 1); scene.add(lz);
// Großer roter Pfeil: zeigt die Bewegungsrichtung (+X)
const arrow = new THREE.ArrowHelper(
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(0, -0.35, 0),
2.5,
0xd02020,
0.5, 0.3
);
scene.add(arrow);
// --- Flugzeug (genau wie in game-3d.html makePlane) ---
function makePlane() {
const g = new THREE.Group();
const bodyMat = new THREE.MeshStandardMaterial({ color: 0xe8e8ea, roughness: 0.7 });
const trimMat = new THREE.MeshStandardMaterial({ color: 0x2a4a6a, roughness: 0.8 });
const darkMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 1 });
const fuselage = new THREE.Mesh(
new THREE.CylinderGeometry(0.14, 0.10, 1.2, 10), bodyMat);
fuselage.rotation.z = Math.PI / 2;
g.add(fuselage);
const cockpit = new THREE.Mesh(new THREE.BoxGeometry(0.35, 0.13, 0.22), trimMat);
cockpit.position.set(0.12, 0.13, 0); g.add(cockpit);
const wing = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.04, 1.6), bodyMat);
wing.position.set(0.0, 0.20, 0); g.add(wing);
const tail = new THREE.Mesh(new THREE.BoxGeometry(0.14, 0.25, 0.04), bodyMat);
tail.position.set(-0.55, 0.25, 0); g.add(tail);
const htail = new THREE.Mesh(new THREE.BoxGeometry(0.20, 0.03, 0.5), bodyMat);
htail.position.set(-0.55, 0.18, 0); g.add(htail);
const hub = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 0.08, 8), darkMat);
hub.rotation.z = Math.PI / 2;
hub.position.set(0.62, 0, 0); g.add(hub);
const prop = new THREE.Group();
prop.position.set(0.66, 0, 0);
const blade = new THREE.Mesh(new THREE.BoxGeometry(0.01, 0.55, 0.04), darkMat);
prop.add(blade);
g.add(prop);
g.userData.propRef = prop;
for (const sx of [-0.1, 0.1]) {
const strut = new THREE.Mesh(new THREE.CylinderGeometry(0.01, 0.01, 0.15, 6), darkMat);
strut.position.set(sx, -0.14, 0); g.add(strut);
const wheel = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 0.04, 10), darkMat);
wheel.rotation.x = Math.PI / 2;
wheel.position.set(sx, -0.22, 0); g.add(wheel);
}
return g;
}
const plane = makePlane();
plane.position.set(0, 0.1, 0);
scene.add(plane);
// --- UI-Handling ---
const rx = document.getElementById('rx');
const ry = document.getElementById('ry');
const rz = document.getElementById('rz');
const vx = document.getElementById('val-x');
const vy = document.getElementById('val-y');
const vz = document.getElementById('val-z');
function updatePlane() {
plane.rotation.set(+rx.value, +ry.value, +rz.value);
vx.textContent = (+rx.value).toFixed(3);
vy.textContent = (+ry.value).toFixed(3);
vz.textContent = (+rz.value).toFixed(3);
}
[rx, ry, rz].forEach(s => s.addEventListener('input', updatePlane));
updatePlane();
document.getElementById('btn-reset').addEventListener('click', () => {
rx.value = 0; ry.value = 0; rz.value = 0;
updatePlane();
plane.position.set(0, 0.1, 0);
});
document.getElementById('btn-copy').addEventListener('click', () => {
const text = `plane.rotation.set(${(+rx.value).toFixed(3)}, ${(+ry.value).toFixed(3)}, ${(+rz.value).toFixed(3)});`;
navigator.clipboard?.writeText(text).then(() => alert('Kopiert:\n' + text)).catch(() => alert(text));
});
let animating = false, animStart = 0;
document.getElementById('btn-animate').addEventListener('click', () => {
animating = !animating;
animStart = performance.now();
plane.position.set(-3, 0.1, 0);
});
// --- Render-Loop ---
function loop() {
requestAnimationFrame(loop);
plane.userData.propRef.rotation.x += 0.3;
if (animating) {
const dt = (performance.now() - animStart) / 1000;
// Bewegung entlang +X (wie der rote Pfeil zeigt)
plane.position.x = -3 + (dt * 1.2) % 6;
}
renderer.render(scene, camera);
}
loop();
</script>
</body>
</html>
@@ -0,0 +1,489 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kachel-Test — Gebäude</title>
<style>
body { margin:0; display:flex; align-items:flex-start; justify-content:center; min-height:100vh; background:#e0e0d8; gap:10px; flex-wrap:wrap; font-family:Inter,sans-serif; padding:15px; }
.tile { display:flex; flex-direction:column; align-items:center; gap:2px; }
.tile span { font-size:9px; color:#666; }
</style>
</head>
<body>
<div class="tile"><canvas id="t01" width="200" height="200"></canvas><span>Kirche</span></div>
<div class="tile"><canvas id="t02" width="200" height="200"></canvas><span>Moschee</span></div>
<div class="tile"><canvas id="t03" width="200" height="200"></canvas><span>Wohngebiet</span></div>
<div class="tile"><canvas id="t04" width="200" height="200"></canvas><span>Fabrik</span></div>
<div class="tile"><canvas id="t05" width="200" height="200"></canvas><span>Spital</span></div>
<div class="tile"><canvas id="t06" width="200" height="200"></canvas><span>Feuerwehr</span></div>
<div class="tile"><canvas id="t07" width="200" height="200"></canvas><span>Polizei</span></div>
<div class="tile"><canvas id="t08" width="200" height="200"></canvas><span>Schule</span></div>
<div class="tile"><canvas id="t09" width="200" height="200"></canvas><span>Supermarkt</span></div>
<div class="tile"><canvas id="t10" width="200" height="200"></canvas><span>Park</span></div>
<script>
var SZ = 200
var RW = SZ * 0.4, M = SZ / 2, HR = RW / 2
var LW = 2
var GREEN = '#b8ccaa', ROAD = '#2e2e2e', WHITE = '#fff'
var DASH = [SZ*0.07, SZ*0.05]
var DOFF = -SZ*0.035
// === Strassen-Grundfunktionen ===
function base(id) {
var c = document.getElementById(id).getContext('2d')
c.fillStyle = GREEN; c.fillRect(0,0,SZ,SZ)
return c
}
function drawRoadNS(c, x) {
// Vertikale Strasse bei x-Mitte = x
c.fillStyle = ROAD; c.fillRect(x-HR, 0, RW, SZ)
c.strokeStyle = WHITE; c.lineWidth = LW
c.beginPath(); c.moveTo(x-HR,0); c.lineTo(x-HR,SZ); c.stroke()
c.beginPath(); c.moveTo(x+HR,0); c.lineTo(x+HR,SZ); c.stroke()
c.setLineDash(DASH); c.lineDashOffset = DOFF
c.beginPath(); c.moveTo(x,0); c.lineTo(x,SZ); c.stroke()
c.setLineDash([]); c.lineDashOffset = 0
}
function drawRoadEW(c, y) {
c.fillStyle = ROAD; c.fillRect(0, y-HR, SZ, RW)
c.strokeStyle = WHITE; c.lineWidth = LW
c.beginPath(); c.moveTo(0,y-HR); c.lineTo(SZ,y-HR); c.stroke()
c.beginPath(); c.moveTo(0,y+HR); c.lineTo(SZ,y+HR); c.stroke()
c.setLineDash(DASH); c.lineDashOffset = DOFF
c.beginPath(); c.moveTo(0,y); c.lineTo(SZ,y); c.stroke()
c.setLineDash([]); c.lineDashOffset = 0
}
function drawStubN(c) {
// Sackgasse von oben — kurzer Stummel
var stubY = 4
c.fillStyle = ROAD; c.fillRect(M-HR, 0, RW, stubY)
c.beginPath(); c.arc(M, stubY, HR, 0, Math.PI); c.fill()
c.strokeStyle = WHITE; c.lineWidth = LW
c.beginPath(); c.moveTo(M-HR,0); c.lineTo(M-HR,stubY); c.stroke()
c.beginPath(); c.moveTo(M+HR,0); c.lineTo(M+HR,stubY); c.stroke()
c.beginPath(); c.arc(M, stubY, HR, 0, Math.PI); c.stroke()
}
function drawStubS(c) {
var stubY = SZ - 4
c.fillStyle = ROAD; c.fillRect(M-HR, stubY, RW, 4)
c.beginPath(); c.arc(M, stubY, HR, Math.PI, 0); c.fill()
c.strokeStyle = WHITE; c.lineWidth = LW
c.beginPath(); c.moveTo(M-HR,SZ); c.lineTo(M-HR,stubY); c.stroke()
c.beginPath(); c.moveTo(M+HR,SZ); c.lineTo(M+HR,stubY); c.stroke()
c.beginPath(); c.arc(M, stubY, HR, Math.PI, 0); c.stroke()
}
function drawStubE(c) {
var stubX = SZ - 4
c.fillStyle = ROAD; c.fillRect(stubX, M-HR, 4, RW)
c.beginPath(); c.arc(stubX, M, HR, Math.PI*1.5, Math.PI*0.5); c.fill()
c.strokeStyle = WHITE; c.lineWidth = LW
c.beginPath(); c.moveTo(SZ,M-HR); c.lineTo(stubX,M-HR); c.stroke()
c.beginPath(); c.moveTo(SZ,M+HR); c.lineTo(stubX,M+HR); c.stroke()
c.beginPath(); c.arc(stubX, M, HR, Math.PI*1.5, Math.PI*0.5); c.stroke()
}
function drawStubW(c) {
var stubX = 4
c.fillStyle = ROAD; c.fillRect(0, M-HR, 4, RW)
c.beginPath(); c.arc(stubX, M, HR, Math.PI*0.5, Math.PI*1.5); c.fill()
c.strokeStyle = WHITE; c.lineWidth = LW
c.beginPath(); c.moveTo(0,M-HR); c.lineTo(stubX,M-HR); c.stroke()
c.beginPath(); c.moveTo(0,M+HR); c.lineTo(stubX,M+HR); c.stroke()
c.beginPath(); c.arc(stubX, M, HR, Math.PI*0.5, Math.PI*1.5); c.stroke()
}
// === Gebaeude-Hilfszeichnung ===
function drawBuilding(c, x, y, w, h, color, label) {
// Gebaeude als Rechteck mit Farbe + Label
c.fillStyle = color
c.fillRect(x, y, w, h)
c.strokeStyle = 'rgba(0,0,0,0.15)'; c.lineWidth = 1
c.strokeRect(x, y, w, h)
// Dach-Effekt (dunklere Oberkante)
c.fillStyle = 'rgba(0,0,0,0.08)'
c.fillRect(x, y, w, 4)
// Label
if (label) {
c.fillStyle = 'rgba(255,255,255,0.9)'
c.font = 'bold 10px Inter, sans-serif'
c.textAlign = 'center'; c.textBaseline = 'middle'
c.fillText(label, x + w/2, y + h/2)
}
}
function drawHouse(c, x, y, w, h) {
// Kleines Haus mit Dach
c.fillStyle = '#d4a574'
c.fillRect(x, y + h*0.35, w, h*0.65)
// Dach
c.fillStyle = '#8B4513'
c.beginPath()
c.moveTo(x - 2, y + h*0.35)
c.lineTo(x + w/2, y)
c.lineTo(x + w + 2, y + h*0.35)
c.closePath()
c.fill()
// Fenster
c.fillStyle = '#a8d0e0'
c.fillRect(x + w*0.2, y + h*0.5, w*0.25, h*0.2)
// Tuer
c.fillStyle = '#6a4a3a'
c.fillRect(x + w*0.55, y + h*0.55, w*0.2, h*0.4)
}
function drawTree(c, x, y, sz) {
c.fillStyle = '#5a4020'
c.fillRect(x + sz*0.4, y + sz*0.5, sz*0.2, sz*0.5)
c.fillStyle = '#3a8a2a'
c.beginPath(); c.arc(x + sz*0.5, y + sz*0.4, sz*0.4, 0, Math.PI*2); c.fill()
}
// ============================================================
// 1. KIRCHE — Sackgasse S, grosses Gebaeude
// ============================================================
;(function(){
var c = base('t01')
drawStubS(c)
// Grosses Kirchengebaeude
drawBuilding(c, 30, 15, 140, 110, '#c8b090', '')
// Turm
c.fillStyle = '#a08060'
c.fillRect(80, 5, 40, 60)
// Kreuz oben
c.strokeStyle = '#5a4a3a'; c.lineWidth = 3
c.beginPath(); c.moveTo(100, 2); c.lineTo(100, 18); c.stroke()
c.beginPath(); c.moveTo(92, 10); c.lineTo(108, 10); c.stroke()
// Tuer
c.fillStyle = '#5a3a2a'
c.beginPath(); c.arc(100, 125, 15, Math.PI, 0); c.fill()
c.fillRect(85, 115, 30, 15)
// Label
c.fillStyle = '#5a4a3a'; c.font = 'bold 11px Inter,sans-serif'; c.textAlign = 'center'
c.fillText('Kirche', 100, 85)
})()
// ============================================================
// 2. MOSCHEE — Sackgasse N + S (zwei Zufahrten)
// ============================================================
;(function(){
var c = base('t02')
drawStubN(c)
drawStubS(c)
// Gebaeude
drawBuilding(c, 25, 35, 150, 90, '#d8c8a8', '')
// Kuppel
c.fillStyle = '#c0a878'
c.beginPath(); c.arc(100, 50, 35, Math.PI, 0); c.fill()
// Halbmond
c.fillStyle = '#e8d8a0'; c.font = '18px serif'; c.textAlign = 'center'
c.fillText('☪', 100, 42)
// Minarett
c.fillStyle = '#b8a878'
c.fillRect(150, 25, 12, 70)
c.fillRect(147, 22, 18, 6)
// Label
c.fillStyle = '#6a5a3a'; c.font = 'bold 10px Inter,sans-serif'
c.fillText('Moschee', 100, 100)
})()
// ============================================================
// 3. WOHNGEBIET — Gerade N-S, Haeuser links und rechts
// ============================================================
;(function(){
var c = base('t03')
drawRoadNS(c, M)
// Haeuser links
drawHouse(c, 8, 20, 30, 30)
drawHouse(c, 12, 70, 28, 28)
drawHouse(c, 6, 120, 32, 30)
// Haeuser rechts
drawHouse(c, 160, 15, 30, 28)
drawHouse(c, 162, 65, 28, 30)
drawHouse(c, 158, 115, 32, 28)
drawHouse(c, 164, 160, 26, 26)
})()
// ============================================================
// 4. FABRIK — Kurve (N→E), Fabrik im gruenen Bereich (unten-links)
// ============================================================
;(function(){
var c = base('t04')
// Kurve N→E
var px = M+HR, py = M-HR
c.fillStyle = ROAD
c.fillRect(M-HR, 0, RW, py)
c.fillRect(px, M-HR, SZ-px, RW)
c.beginPath(); c.moveTo(px,py); c.arc(px,py,RW,Math.PI,Math.PI*0.5,true); c.closePath(); c.fill()
// Raender
c.strokeStyle=WHITE;c.lineWidth=LW
c.beginPath();c.arc(px,py,RW,Math.PI,Math.PI*0.5,true);c.stroke()
c.beginPath();c.moveTo(M+HR,0);c.lineTo(M+HR,py);c.stroke()
c.beginPath();c.moveTo(px,M-HR);c.lineTo(SZ,M-HR);c.stroke()
c.beginPath();c.moveTo(M-HR,0);c.lineTo(M-HR,py);c.stroke()
c.beginPath();c.moveTo(px,M+HR);c.lineTo(SZ,M+HR);c.stroke()
// Mittellinie
c.setLineDash(DASH);c.lineDashOffset=DOFF
c.beginPath();c.moveTo(M,0);c.lineTo(M,py);c.arc(px,py,HR,Math.PI,Math.PI*0.5,true);c.lineTo(SZ,M);c.stroke()
c.setLineDash([]);c.lineDashOffset=0
// Fabrik unten-links
drawBuilding(c, 10, 130, 70, 55, '#8a8a8a', '')
// Schornstein
c.fillStyle = '#6a6a6a'
c.fillRect(55, 110, 12, 20)
c.fillRect(30, 115, 10, 15)
// Rauch
c.fillStyle = 'rgba(100,100,100,0.25)'
c.beginPath(); c.arc(61, 105, 6, 0, Math.PI*2); c.fill()
c.beginPath(); c.arc(58, 96, 5, 0, Math.PI*2); c.fill()
// Label
c.fillStyle = '#fff'; c.font = 'bold 10px Inter,sans-serif'; c.textAlign = 'center'
c.fillText('Fabrik', 45, 160)
})()
// ============================================================
// 5. SPITAL — Sackgasse E + W (zwei Zufahrten seitlich)
// ============================================================
;(function(){
var c = base('t05')
drawStubE(c)
drawStubW(c)
// Grosses Gebaeude
drawBuilding(c, 20, 15, 160, 80, '#e8e0d0', '')
// Rotes Kreuz
c.fillStyle = '#cc3333'
c.fillRect(90, 30, 20, 50)
c.fillRect(75, 42, 50, 20)
// Label
c.fillStyle = '#6a3a3a'; c.font = 'bold 11px Inter,sans-serif'; c.textAlign = 'center'
c.fillText('Spital', 100, 120)
// Rettungswagen-Parkplatz
c.fillStyle = '#7a7a7a'; c.fillRect(30, 100, 50, 25)
c.fillStyle = '#ddd'; c.font = '16px serif'
c.fillText('🚑', 55, 117)
})()
// ============================================================
// 6. FEUERWEHR — Sackgasse S
// ============================================================
;(function(){
var c = base('t06')
drawStubS(c)
// Gebaeude
drawBuilding(c, 25, 10, 150, 100, '#cc4444', '')
// Tore (2 Garagen)
c.fillStyle = '#8a2a2a'
c.fillRect(40, 70, 45, 40)
c.fillRect(105, 70, 45, 40)
// Tor-Linien
c.strokeStyle = '#6a1a1a'; c.lineWidth = 1
for (var i = 0; i < 5; i++) { c.beginPath(); c.moveTo(40, 75+i*7); c.lineTo(85, 75+i*7); c.stroke() }
for (var i = 0; i < 5; i++) { c.beginPath(); c.moveTo(105, 75+i*7); c.lineTo(150, 75+i*7); c.stroke() }
// Label
c.fillStyle = '#fff'; c.font = 'bold 11px Inter,sans-serif'; c.textAlign = 'center'
c.fillText('Feuerwehr', 100, 45)
// Schlauchturm
c.fillStyle = '#aa3333'
c.fillRect(160, 15, 15, 55)
})()
// ============================================================
// 7. POLIZEI — Sackgasse N
// ============================================================
;(function(){
var c = base('t07')
drawStubN(c)
// Gebaeude
drawBuilding(c, 20, 50, 160, 85, '#4a6a9a', '')
// Stern/Abzeichen
c.fillStyle = '#c0d0e8'; c.font = '24px serif'; c.textAlign = 'center'
c.fillText('⭐', 100, 85)
// Label
c.fillStyle = '#fff'; c.font = 'bold 11px Inter,sans-serif'
c.fillText('Polizei', 100, 115)
// Parkplatz
c.fillStyle = '#7a7a7a'; c.fillRect(30, 145, 60, 20)
c.fillStyle = '#ddd'; c.font = '14px serif'
c.fillText('🚔', 60, 160)
})()
// ============================================================
// 8. SCHULE — Gerade E-W, Gebaeude oben
// ============================================================
;(function(){
var c = base('t08')
drawRoadEW(c, M)
// Schulgebaeude oben
drawBuilding(c, 15, 10, 170, 60, '#e0c888', '')
// Fensterreihe
c.fillStyle = '#a8d0e0'
for (var i = 0; i < 5; i++) c.fillRect(25 + i*34, 25, 22, 18)
// Label
c.fillStyle = '#5a4a2a'; c.font = 'bold 11px Inter,sans-serif'; c.textAlign = 'center'
c.fillText('Schule', 100, 58)
// Schulhof unten (Wiese ist schon gruen, ein paar Details)
drawTree(c, 30, 145, 20)
drawTree(c, 140, 150, 18)
c.fillStyle = '#aaa'; c.fillRect(70, 155, 50, 3) // Bank
})()
// ============================================================
// 9. SUPERMARKT — Gerade N-S, Gebaeude rechts mit Parkplatz
// ============================================================
;(function(){
var c = base('t09')
drawRoadNS(c, M)
// Supermarkt rechts
drawBuilding(c, 140, 20, 55, 80, '#e0d0a0', '')
// Label
c.fillStyle = '#5a4a2a'; c.font = 'bold 9px Inter,sans-serif'; c.textAlign = 'center'
c.fillText('Super-', 167, 50)
c.fillText('markt', 167, 62)
// Parkplatz
c.fillStyle = '#8a8a8a'
c.fillRect(140, 110, 55, 40)
// Parkplatz-Linien
c.strokeStyle = '#aaa'; c.lineWidth = 1
for (var i = 0; i < 4; i++) { c.beginPath(); c.moveTo(152+i*12, 110); c.lineTo(152+i*12, 150); c.stroke() }
// Einkaufswagen
c.fillStyle = '#ddd'; c.font = '12px serif'
c.fillText('🛒', 167, 135)
})()
// ============================================================
// 10. PARK — Sackgasse N, viel Gruen
// ============================================================
;(function(){
var c = base('t10')
drawStubN(c)
// Baeume
drawTree(c, 15, 40, 28)
drawTree(c, 60, 55, 24)
drawTree(c, 130, 35, 30)
drawTree(c, 155, 80, 22)
drawTree(c, 25, 120, 26)
drawTree(c, 100, 110, 20)
drawTree(c, 150, 140, 24)
// Teich
c.fillStyle = 'rgba(60,140,200,0.4)'
c.beginPath(); c.ellipse(80, 140, 30, 18, 0.1, 0, Math.PI*2); c.fill()
c.strokeStyle = 'rgba(60,140,200,0.3)'; c.lineWidth = 1; c.stroke()
// Bank
c.fillStyle = '#8a7050'
c.fillRect(95, 82, 20, 6)
c.fillRect(95, 82, 3, 10)
c.fillRect(112, 82, 3, 10)
// Weg (kleiner Pfad)
c.strokeStyle = 'rgba(160,140,100,0.4)'; c.lineWidth = 3; c.setLineDash([4,3])
c.beginPath(); c.moveTo(M, 10); c.quadraticCurveTo(60, 80, 80, 140); c.stroke()
c.setLineDash([])
// Label
c.fillStyle = 'rgba(0,0,0,0.35)'; c.font = 'bold 11px Inter,sans-serif'; c.textAlign = 'center'
c.fillText('Park', 100, 175)
})()
// ============================================================
// EMOJI-VERSIONEN (Gegenversuch)
// ============================================================
var emojiTiles = [
{id:'e01', label:'Kirche', stub:'S', emoji:'⛪', size:95},
{id:'e02', label:'Moschee', stub:'NS', emoji:'🕌', size:85},
{id:'e03', label:'Wohngebiet', road:'NS', emojis:[
{e:'🏠',x:-0.32,y:-0.3,s:32},{e:'🏡',x:-0.3,y:0,s:30},{e:'🏠',x:-0.32,y:0.28,s:28},
{e:'🏘️',x:0.32,y:-0.25,s:30},{e:'🏠',x:0.3,y:0.05,s:32},{e:'🏡',x:0.32,y:0.3,s:28}]},
{id:'e04', label:'Fabrik', stub:'S', emoji:'🏭', size:85},
{id:'e05', label:'Spital', stub:'EW', emoji:'🏥', size:65, yoff:-0.12, extra:[{e:'🚑',x:0,y:0.3,s:30}]},
{id:'e06', label:'Feuerwehr', stub:'S', bg:'rgba(200,50,40,0.12)', emojis:[
{e:'🏠',x:0,y:-0.15,s:60},{e:'🚒',x:-0.2,y:0.22,s:40},{e:'🚒',x:0.2,y:0.22,s:40}]},
{id:'e07', label:'Polizei', stub:'N', emoji:'🚔', size:45, extra:[{e:'🏢',x:0,y:0.15,s:55}]},
{id:'e08', label:'Schule', road:'EW', emoji:'🏫', size:60, yoff:-0.28},
{id:'e09', label:'Supermarkt', road:'NS', emoji:'🏪', size:55, xoff:0.3, extra:[
{e:'🚗',x:0.22,y:0.25,s:26},{e:'🚙',x:0.35,y:0.15,s:24},{e:'🚗',x:0.22,y:0.38,s:22}]},
{id:'e10', label:'Park', stub:'N', emojis:[
{e:'🌳',x:-0.3,y:-0.1,s:32},{e:'🌳',x:-0.1,y:-0.1,s:32},{e:'🌳',x:0.1,y:-0.1,s:32},{e:'🌳',x:0.3,y:-0.1,s:32},
{e:'🌳',x:-0.2,y:0.1,s:28},{e:'🌳',x:0,y:0.1,s:28},{e:'🌳',x:0.2,y:0.1,s:28},
{e:'🌷',x:-0.35,y:0.3,s:16},{e:'🌷',x:-0.2,y:0.32,s:14},{e:'🌷',x:-0.05,y:0.3,s:16},
{e:'🌷',x:0.1,y:0.32,s:14},{e:'🌷',x:0.25,y:0.3,s:16},{e:'🌷',x:0.38,y:0.32,s:14}]},
{id:'e11', label:'Rathaus', stub:'S', emoji:'🏛️', size:85},
{id:'e13', label:'Tankstelle', road:'NS', emojis:[
{e:'⛽',x:0.28,y:-0.2,s:32},{e:'⛽',x:0.28,y:0,s:32},{e:'⛽',x:0.28,y:0.2,s:32},
{e:'🚗',x:0.38,y:-0.15,s:26},{e:'🚙',x:0.38,y:0.15,s:26}]},
{id:'e14', label:'Spielplatz', stub:'N', emojis:[
{e:'🛝',x:-0.2,y:0,s:42},{e:'🎠',x:0.2,y:0,s:36},{e:'⚽',x:0,y:0.25,s:22},
{e:'🌳',x:-0.35,y:0.3,s:26},{e:'🌳',x:0.35,y:0.3,s:26}]},
{id:'e15', label:'Bauernhof', stub:'EW', emojis:[
{e:'🏠',x:0,y:-0.05,s:55},
{e:'🌾',x:-0.35,y:-0.3,s:24},{e:'🌾',x:-0.2,y:-0.3,s:24},{e:'🌾',x:-0.05,y:-0.3,s:24},
{e:'🌾',x:0.1,y:-0.3,s:24},{e:'🌾',x:0.25,y:-0.3,s:24},{e:'🌾',x:0.4,y:-0.3,s:24},
{e:'🐄',x:-0.25,y:0.28,s:30},{e:'🐔',x:0.15,y:0.3,s:22},{e:'🐔',x:0.28,y:0.28,s:22}]},
{id:'e16', label:'Hochhäuser', road:'NS', emojis:[
{e:'🏢',x:0.22,y:-0.2,s:38},{e:'🏢',x:0.32,y:-0.2,s:38},{e:'🏢',x:0.42,y:-0.2,s:38},
{e:'🏢',x:0.22,y:0.15,s:38},{e:'🏢',x:0.32,y:0.15,s:38},{e:'🏢',x:0.42,y:0.15,s:38}]},
]
// Erzeuge HTML fuer Emoji-Tiles
var hr = document.createElement('div')
hr.style.cssText = 'width:100%;text-align:center;color:#888;font-size:11px;margin:8px 0;border-top:1px solid #ccc;padding-top:6px'
hr.textContent = '▼ Emoji-Versionen ▼'
document.body.appendChild(hr)
emojiTiles.forEach(function(t) {
var div = document.createElement('div')
div.className = 'tile'
var cv = document.createElement('canvas')
cv.width = 200; cv.height = 200; cv.id = t.id
var span = document.createElement('span')
span.textContent = t.label
div.appendChild(cv); div.appendChild(span)
document.body.appendChild(div)
var c = cv.getContext('2d')
c.fillStyle = GREEN; c.fillRect(0, 0, SZ, SZ)
// Hintergrundfarbe (z.B. rot fuer Feuerwehr)
if (t.bg) { c.fillStyle = t.bg; c.fillRect(0, 0, SZ, SZ) }
// Strasse
if (t.road === 'NS') drawRoadNS(c, M)
if (t.road === 'EW') drawRoadEW(c, M)
if (t.stub) {
if (t.stub.indexOf('N') >= 0) drawStubN(c)
if (t.stub.indexOf('S') >= 0) drawStubS(c)
if (t.stub.indexOf('E') >= 0) drawStubE(c)
if (t.stub.indexOf('W') >= 0) drawStubW(c)
}
// Emojis
c.textAlign = 'center'; c.textBaseline = 'middle'
if (t.emoji) {
c.font = (t.size || 50) + 'px serif'
c.fillText(t.emoji, M + (t.xoff || 0) * SZ, M + (t.yoff || 0) * SZ)
}
if (t.emojis) {
t.emojis.forEach(function(em) {
c.font = (em.s || 24) + 'px serif'
c.fillText(em.e, M + em.x * SZ, M + em.y * SZ)
})
}
if (t.extra) {
t.extra.forEach(function(em) {
c.font = (em.s || 24) + 'px serif'
c.fillText(em.e, M + (em.x||0) * SZ, M + (em.y||0) * SZ)
})
}
// Keine Beschriftung auf der Kachel
})
</script>
</body>
</html>
@@ -0,0 +1,496 @@
{
"country": "AT",
"recherche_datum": "2026-06-08",
"researcher_notes": [
"Quellen direkt aus RIS (Rechtsinformationssystem des Bundes) per WebFetch + pdftotext extrahiert.",
"Wörtliche Zitate aus PDF (BGBl. II Nr. 1/2023). Umlaute aus Layout-Extraktion teils als 'a/o/u' korrigiert (ä, ö, ü, ß, ) — der semantische Inhalt ist identisch zur Quelle.",
"Alle Zitate ≤ 500 Zeichen.",
"Lehrplan-Verordnung 'Lehrpläne der Mittelschulen' (Gesetzesnummer 20007850) gilt für MS und ist inhaltlich für AHS-Unterstufe (Gesetzesnummer 10008568) parallel formuliert; daher MS+AHS-U.",
"Plattform-Kompetenz-Slug 'neu:<name>' kennzeichnet Vorschläge, die noch nicht in der DB existieren.",
"Sim-Liste (zur Auswahl): klima, klima-3d, fluss, heli, energiemanager, eu-werkstatt, farmer, busfahrt, sonnensystem, logistik, weltkueche."
],
"quellen": [
{
"name": "BMBWF Lehrpläne der Mittelschulen — Anlage 1 (Verordnung BGBl. II Nr. 1/2023)",
"url": "https://www.ris.bka.gv.at/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=20007850",
"pdf_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"version_datum": "2023-01-02",
"ausgegeben": "BGBl. II — Ausgegeben am 2. Jänner 2023 — Nr. 1",
"geltend_seit": "Aufsteigend in Kraft ab 2023-09-01 (1. Klasse), 2024-09-01 (2. Kl.), 2025-09-01 (3. Kl.), 2026-09-01 (4. Kl.)"
},
{
"name": "BMBWF Lehrpläne der allgemeinbildenden höheren Schulen — Anlage A (Verordnung BGBl. II Nr. 1/2023)",
"url": "https://www.ris.bka.gv.at/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=10008568",
"pdf_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0012_E1BFECE6_7E8B_4ACF_AEFD_3EC871222138.pdfsig",
"version_datum": "2023-01-02",
"anmerkung": "Fachlehrpläne GW, BU, EH, GPB der AHS-Unterstufe sind inhaltlich identisch zur Mittelschulfassung; Zitate hier aus MS-Fassung übernommen — gelten parallel für AHS-U (Unterstufe). PDF wurde aufgrund Größe nicht separat extrahiert."
},
{
"name": "BMBWF Lehrplan der Volksschule — Anlage 1 (Verordnung BGBl. II Nr. 1/2023)",
"url": "https://www.ris.bka.gv.at/GeltendeFassung.wxe?Abfrage=Bundesnormen&Gesetzesnummer=10009275",
"pdf_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0001_CE7F0AA2_A925_4A4D_8C3C_355D12BD22D1.pdfsig",
"version_datum": "2023-01-02",
"ausgegeben": "BGBl. II — Ausgegeben am 2. Jänner 2023 — Nr. 1"
}
],
"kompetenzen": [
{
"code": "GW Kompetenzbereich 1.3 — Leben und Wirtschaften im Hinblick auf nachhaltige Ernährung",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "1. Klasse Sek I",
"title": "Landwirtschaft, Ernährungssysteme und Klimawandel — Welternährung",
"quote": "Die Schülerinnen und Schüler können ausgehend von individuellen Ernährungsgewohnheiten (land)wirtschaftliche Produktion, deren gesellschaftliche, wirtschaftliche und naturräumliche Bedingungen sowie deren Wandel an weltweit ausgewählten Fallbeispielen verorten und vergleichen; … den Einfluss des Klimawandels auf die Erzeugung von ausreichenden und gesunden Nahrungsmitteln an Fallbeispielen erörtern.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "103 von 151 (Anlage 1, BGBl. II 1/2023)",
"passende_sims": ["weltkueche", "farmer", "klima", "klima-3d", "busfahrt"],
"passende_plattform_kompetenzen": ["landwirtschaft-klimazonen", "klimawandel"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "Welternährung, Landwirtschaft global, Klimawandel-Auswirkung auf Lebensmittelerzeugung"
},
{
"code": "GW Kompetenzbereich 1.4 — Leben und Wirtschaften unter Beachtung der natürlichen Prozesse",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "1. Klasse Sek I",
"title": "Naturgefahren, Wechselwirkung Natur/Wirtschaft, solidarisches Handeln",
"quote": "Die Schülerinnen und Schüler können Wechselwirkungen von Naturereignissen, Lebensqualität und Wirtschaften der Menschen anhand von Fallbeispielen beschreiben und erörtern; unterschiedliche Betroffenheit von Menschen und Gesellschaften durch Naturgefahren in angemessener Sprache bewerten und Möglichkeiten solidarischen Handelns erörtern.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "103 von 151",
"passende_sims": ["fluss", "heli", "klima", "klima-3d"],
"passende_plattform_kompetenzen": ["naturgefahren-hochwasser", "klimawandel"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "GW Kompetenzbereich 2.1 — Nachhaltiger Umgang mit Energie und Ressourcen",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "2. Klasse Sek I",
"title": "Erneuerbare vs. nicht erneuerbare Energieträger, Klimawandel, Ressourcenverteilung",
"quote": "Die Schülerinnen und Schüler können die Nutzung erneuerbarer und nicht erneuerbarer Energieträger vergleichen und aus den Blickwinkeln von Klimawandel und Nachhaltigkeit reflektieren; die ungleiche räumliche und gesellschaftliche Verteilung von Ressourcen mit Hilfe von (Geo-)Medien beschreiben und deren Nutzung, Wiederverwendung und Entsorgung analysieren; den verantwortungsvollen Umgang mit den natürlichen Ressourcen wie Wasser, Luft oder Boden erörtern …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "103104 von 151",
"passende_sims": ["energiemanager", "klima", "klima-3d", "farmer"],
"passende_plattform_kompetenzen": ["energieversorgung-speicherung", "klimawandel"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "GW Kompetenzbereich 2.2 — Vernetztes Wirtschaften zwischen Produktion und Konsum",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "2. Klasse Sek I",
"title": "Märkte, Angebot/Nachfrage, Preisbildung, arbeitsteilige Produktion",
"quote": "Die Schülerinnen und Schüler können … arbeitsteiliges und spezialisiertes Produzieren von Gütern und Anbieten von Dienstleistungen sowie das freie Treffen von Produktionsentscheidungen durch Haushalte, Unternehmen und Staat beschreiben; … das Zusammentreffen von Angebot und Nachfrage nach verschiedenen Gütern (einschließlich Leistungen) auf Märkten (Güter-, Arbeits-, und Kapitalmärkte) sowie deren wesentliche Funktionen insbesondere für die Preisbildung charakterisieren …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "104 von 151",
"passende_sims": ["logistik", "busfahrt", "farmer", "weltkueche"],
"passende_plattform_kompetenzen": ["wirtschaft-logistikverstaendnis", "planungs-entscheidungskompetenz"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "GW Kompetenzbereich 3.3 — Entwicklungen am Wirtschaftsstandort Österreich (Außenwirtschaftliche Verflechtungen)",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "3. Klasse Sek I",
"title": "Import/Export — Außenwirtschaft Österreichs mit EU und Welt",
"quote": "Die Schülerinnen und Schüler können … Gründe, Bedeutung sowie ökonomische, gesellschaftliche und ökologische Folgen der außenwirtschaftlichen Verflechtungen (Import/Export) Österreichs mit der EU und der restlichen Welt erläutern.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "105 von 151",
"passende_sims": ["logistik", "busfahrt", "weltkueche", "eu-werkstatt"],
"passende_plattform_kompetenzen": ["wirtschaft-logistikverstaendnis", "eu-politische-beteiligung"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "Welthandel — Import/Export Österreichs explizit benannt"
},
{
"code": "GW Kompetenzbereich 3.4 — Zentren und Peripherien in Österreich",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "3. Klasse Sek I",
"title": "Raumnutzung, Raumplanung, Nutzungskonflikte",
"quote": "Die Schülerinnen und Schüler können Zentren und Peripherien Österreichs abgrenzen, in Geomedien verorten und die Relativität jeder Abgrenzung erläutern; … mit Hilfe von (Geo-)Medien die Raumnutzungen für Wohnen, Arbeit, Verkehr, Freizeitaktivitäten und Tourismus vergleichen sowie Lösungsansätze der Raumplanung bei Nutzungskonflikten erörtern.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "105 von 151",
"passende_sims": ["fluss", "logistik", "busfahrt"],
"passende_plattform_kompetenzen": ["stadtplanung-raum", "raeumliche-orientierung-europa"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "GW Kompetenzbereich 4.1 — Mensch und Natursysteme",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "4. Klasse Sek I",
"title": "Anthropozän, Belastungsgrenzen, Klimawandel, Artensterben",
"quote": "Die Schülerinnen und Schüler können das Ausmaß des menschlichen Einflusses auf Natursysteme anhand von Beispielen wie Einbringung von Schadstoffen, Land- und Ressourcennutzung … beschreiben; Folgen der Überschreitung von Belastungsgrenzen der Erde, des Klimawandels oder des Artensterbens auf das Leben und Wirtschaften erörtern; über das sich wandelnde Verhältnis zwischen Mensch und Natur reflektieren …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "105106 von 151",
"passende_sims": ["klima", "klima-3d", "farmer", "weltkueche"],
"passende_plattform_kompetenzen": ["klimawandel", "landwirtschaft-klimazonen"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "GW Kompetenzbereich 4.2 — Europa und europäische Integration",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "4. Klasse Sek I",
"title": "EU — vier Grundfreiheiten, Euro, gemeinsame Werte",
"quote": "Die Schülerinnen und Schüler können unterschiedliche Gliederungen, Wahrnehmungen und Darstellungen von Europa und der Europäischen Union in ihrer Vielfalt beschreiben, in (Geo-)Medien analysieren … die Bedeutung gemeinsamer Werte wie Friedenserhaltung, Demokratie oder Freiheit begründen und zentrale Themen der EU wie die vier Grundfreiheiten (Waren, Personen, Kapital und Dienstleistungen), den Euro, Bildungskooperationen, Qualitätsstandards, Verbraucherschutz oder Regionalentwicklung … reflektieren …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "106 von 151",
"passende_sims": ["eu-werkstatt", "logistik", "busfahrt"],
"passende_plattform_kompetenzen": ["eu-politische-beteiligung", "raeumliche-orientierung-europa"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "EU-Politik / EU-Institutionen / Vier Grundfreiheiten / Euro / Verbraucherschutz"
},
{
"code": "GW Kompetenzbereich 4.3 — Entwicklungen in einer globalisierten Welt",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "4. Klasse Sek I",
"title": "Globalisierung, Lieferketten, Klimawandel, Migration",
"quote": "Die Schülerinnen und Sch[ülerinnen] können … Möglichkeiten und Grenzen der Zusammenarbeit sowie den Wandel der Machtverhältnisse zwischen Staaten, Bündnissen, NGOs, Konzernen, internationalen Organisationen und Bewegungen untersuchen und darstellen; Elemente, Anforderungen und Herausforderungen nachhaltiger wirtschaftlicher Aktivitäten und Beziehungen in der globalisierten Welt diskutieren zB vor dem Hintergrund von Klimawandel, Ressourcenknappheit, Armut und Wohlstand, Flucht und Migration …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "106 von 151",
"passende_sims": ["weltkueche", "logistik", "busfahrt", "farmer", "klima", "klima-3d"],
"passende_plattform_kompetenzen": ["wirtschaft-logistikverstaendnis", "klimawandel", "systemisches-denken"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "Globalisierung, Welthandel, internationale Konzerne, NGOs"
},
{
"code": "GW Anwendungsbereiche 4. Klasse — Mensch-Umwelt-Verhältnis im Anthropozän",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "4. Klasse Sek I",
"title": "Anwendungsbereiche Welthandel/Globalisierung/EU",
"quote": "Anwendungsbereiche: Mensch-Umweltverhältnis im Anthropozän; Belastungsgrenzen der Erde; Werte und zentrale Themen der EU; Europäische Integration einschließlich gesellschaftliche und wirtschaftliche Folgen; Globale Bevölkerungsdynamik, Urbanisierung; Wirtschaftlicher und gesellschaftlicher Wandel durch Globalisierung und Deglobalisierung; Gemeinsame Herausforderungen in Gesellschaft, Wirtschaft, Politik und Umwelt; Eigene Chancen und Perspektiven in einer globalisierten Welt.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "106 von 151",
"passende_sims": ["eu-werkstatt", "weltkueche", "logistik", "busfahrt", "klima", "klima-3d"],
"passende_plattform_kompetenzen": ["eu-politische-beteiligung", "klimawandel", "wirtschaft-logistikverstaendnis"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "GW Didaktische Grundsätze — Maßstabsebenen und Geomedien",
"subject": "Geografie und wirtschaftliche Bildung",
"school_type": "MS / AHS-U",
"grade": "1.4. Klasse Sek I",
"title": "Räumliche Maßstabsebenen — persönlich/lokal/regional/national/global",
"quote": "Alle Zielsetzungen und Inhalte sollen daher im Wirkungsgefüge „Gesellschaft Wirtschaft Politik Umwelt“ bearbeitet werden. Dabei sollen bildungsrelevante Fallbeispiele mit Problembezug auf unterschiedlichen Maßstabsebenen (persönlich, lokal, regional, national und global) erarbeitet werden. … Medien, insbesondere Geomedien (das sind digitale und analoge Medien mit ortsbezogenen Daten), werden zum Erschließen, Produzieren und Kommunizieren von geografischen und wirtschaftlichen Inhalten eingesetzt.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "100101 von 151",
"passende_sims": ["heli", "logistik", "busfahrt", "sonnensystem"],
"passende_plattform_kompetenzen": ["karten-navigation-alpen", "kartenkompetenz-logistik", "raeumliche-orientierung-europa"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "EH Kompetenzbereich 1 — Ernährung, Gesundheit und Soziales (2. Klasse)",
"subject": "Ernährung und Haushalt",
"school_type": "MS / AHS-U",
"grade": "2. Klasse Sek I (Pflichtfach MS; AHS-U als verbindliche Übung/Wahlpflicht je Standort)",
"title": "Bedarfsgerechte Ernährung, Lebensmittelqualität, ressourcenschonende Zubereitung",
"quote": "Die Schülerinnen und Schüler können Einflüsse auf Ernährungsgewohnheiten (ua. kulturelle, religiöse, soziale und geografische) reflektieren; die Grundlagen bedarfsgerechter Ernährung benennen und das eigene Ess- und Trinkverhalten bewerten; Lebensmittel und deren Qualität nach differenzierten Aspekten beurteilen; grundlegende Techniken der Nahrungszubereitung gesundheitsorientiert und ressourcenschonend anwenden; Ernährungsentscheidungen qualitäts- und genussorientiert treffen und begründen.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "132 von 151",
"passende_sims": ["weltkueche", "farmer"],
"passende_plattform_kompetenzen": ["landwirtschaft-klimazonen", "neu:ernaehrungs-und-konsumkompetenz"],
"neu_oder_vorhanden": "neu",
"luecke_geschlossen": "Welternährung, Lebensmittelqualität, kulturelle/geografische Ernährungseinflüsse"
},
{
"code": "EH Anwendungsbereiche Kompetenzbereich 2 — Regionale/saisonale Lebensmittel, globale Produktion",
"subject": "Ernährung und Haushalt",
"school_type": "MS / AHS-U",
"grade": "2. Klasse Sek I",
"title": "Regional, saisonal, globale Produktionsketten, Nachhaltigkeit",
"quote": "Anwendungsbereiche: Regionale und saisonale Lebensmittelauswahl; Globale und lokale Zusammenhänge der Produktion von Gütern; Analyse von Produkten in Hinblick auf Nachhaltigkeit und Gesundheit Produktlinienanalyse; Arbeits- und Alltagsorganisation im privaten Haushalt; Haushaltsbudget, Kaufentscheidungen, Nutzung und Reklamation.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "132133 von 151",
"passende_sims": ["weltkueche", "farmer", "logistik"],
"passende_plattform_kompetenzen": ["landwirtschaft-klimazonen", "wirtschaft-logistikverstaendnis", "neu:ernaehrungs-und-konsumkompetenz"],
"neu_oder_vorhanden": "neu",
"luecke_geschlossen": "Saisonal/regional, globale Lebensmittel-Produktionsketten — direkt im Lehrplan benannt"
},
{
"code": "EH Zentrale Konzepte — Ernährung des Menschen / Nachhaltigkeit / Haushalt",
"subject": "Ernährung und Haushalt",
"school_type": "MS / AHS-U",
"grade": "2. Klasse Sek I",
"title": "Ernährung individuell/sozial/global; Nachhaltigkeit Ökologie/Ökonomie/Soziales",
"quote": "Ernährung des Menschen: … Der Anspruch an die Ernährung des Menschen liegt darin, dass sie gleichermaßen bedarfsgerecht, gesundheitsfördernd, genussvoll sowie nachhaltig sein soll. Der Fokus richtet sich auf den individuellen, sozialen und globalen Kontext. … Nachhaltigkeit: Das Wirkungsgefüge von Ökologie, Ökonomie und Sozialem sowie deren Wechselbeziehungen spiegelt sich im Konzept der Nachhaltigkeit.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "131132 von 151",
"passende_sims": ["weltkueche", "farmer", "klima", "klima-3d"],
"passende_plattform_kompetenzen": ["landwirtschaft-klimazonen", "klimawandel", "neu:ernaehrungs-und-konsumkompetenz"],
"neu_oder_vorhanden": "neu"
},
{
"code": "BU Anwendungsbereiche 1. Klasse — Bestäubung und Befruchtung",
"subject": "Biologie und Umweltbildung",
"school_type": "MS / AHS-U",
"grade": "1. Klasse Sek I",
"title": "Bestäubung als Funktion der Blüte",
"quote": "Anwendungsbereiche 1. Klasse: Wechselbeziehungen zwischen Lebewesen in ihrem Lebensraum; Vielfalt und Angepasstheit sowie Grundzüge der Systematik von Blütenpflanzen; Bestäubung und Befruchtung als Funktionen der Blüte; Gemeinsamkeiten von Wirbeltieren, die auf deren Verwandtschaft hinweisen; …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "91 von 151",
"passende_sims": ["farmer", "weltkueche"],
"passende_plattform_kompetenzen": ["landwirtschaft-klimazonen"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "Bestäubung als expliziter Lehrplan-Inhalt (BU 1. Kl.)"
},
{
"code": "BU Anwendungsbereiche 4. Klasse — Konventionelle vs. biologische Landwirtschaft; Klimawandel",
"subject": "Biologie und Umweltbildung",
"school_type": "MS / AHS-U",
"grade": "4. Klasse Sek I",
"title": "Landwirtschaft, Biodiversität, anthropogene Einflüsse, CO2 und Klimawandel",
"quote": "Anwendungsbereiche 4. Klasse: Ökologische Zusammenhänge, Biodiversität und anthropogene Einflüsse im Bereich der Landwirtschaft, Charakteristika der konventionellen und der biologischen Landwirtschaft; Ökologische Zusammenhänge, Biodiversität und anthropogene Einflüsse in Siedlungsräumen, Pflanzen und Tiere als Kulturfolger, Vermeidung von Müll und Lebensmittelverschwendung; Globaler Kohlenstoffkreislauf, CO2-Eintrag in die Atmosphäre, Klimawandel; …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "9192 von 151",
"passende_sims": ["farmer", "klima", "klima-3d", "weltkueche"],
"passende_plattform_kompetenzen": ["landwirtschaft-klimazonen", "klimawandel"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "Konventionelle vs. biologische Landwirtschaft, Monokultur-Bezug, Lebensmittelverschwendung, CO2-Kreislauf"
},
{
"code": "BU Anwendungsbereiche 3. Klasse — Gesteinskreislauf und Plattentektonik",
"subject": "Biologie und Umweltbildung",
"school_type": "MS / AHS-U",
"grade": "3. Klasse Sek I",
"title": "Plattentektonik und Lebensräume",
"quote": "Anwendungsbereiche 3. Klasse: … Gesteinskreislauf und Plattentektonik, Zusammenhänge zwischen Geologie und Lebensräumen; Funktion von Mikroorganismen im Boden, Bedeutung des Bodens für die Pflanzen, anthropogene Einflüsse auf den Boden; Entstehung von Fossilien und Geschichte des Lebens auf der Erde; …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "91 von 151",
"passende_sims": ["sonnensystem", "fluss", "farmer"],
"passende_plattform_kompetenzen": ["erdmechanik-klimazonen", "naturgefahren-hochwasser"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "GPB Anwendungsbereich 4. Klasse — Europäisierung / EU als Wirtschafts- und Friedensprojekt",
"subject": "Geschichte und Politische Bildung",
"school_type": "MS / AHS-U",
"grade": "4. Klasse Sek I",
"title": "EU-Beitritt Österreichs, Europa als Friedens-/Wirtschaftsprojekt",
"quote": "Europäisierung (unterschiedliche historische und gegenwärtige Konzeptionen von Europa; die EU als Wirtschafts- und Friedensprojekt; Ende des geteilten Europas 1989 und die Auswirkungen; Beitritt Österreichs zur EU und die nachfolgenden Veränderungen in Außen-, Sicherheits- und Neutralitätspolitik, in Gesellschaft und Alltag; Einfluss der EU auf die Lebenswelt der Schülerinnen und Schüler).",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "99 von 151",
"passende_sims": ["eu-werkstatt", "busfahrt"],
"passende_plattform_kompetenzen": ["eu-politische-beteiligung"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "GPB Anwendungsbereich 4. Klasse — Politische Mitbestimmung in Gegenwart und Zukunft",
"subject": "Geschichte und Politische Bildung",
"school_type": "MS / AHS-U",
"grade": "4. Klasse Sek I",
"title": "Politische Institutionen, Bürgerbeteiligung, Föderalismus, NGOs",
"quote": "Politische Mitbestimmung in Gegenwart und Zukunft (demokratische Werte und Grundrechte; Menschenrechte; Minderheitenrechte; Räume, Möglichkeiten und Strategien: politische Institutionen, außerparlamentarische Formen der Mitbestimmung … Aktionen von Bürgerinnen und Bürgern im öffentlichen Raum; Machtungleichheiten in politischen Prozessen, Opposition und Regierung, NGOs); Aufgaben und Kompetenzverteilung von Gemeinden, Ländern und Bund bundesstaatliches Prinzip/Föderalismus …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "99 von 151",
"passende_sims": ["eu-werkstatt"],
"passende_plattform_kompetenzen": ["eu-politische-beteiligung"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "GPB Anwendungsbereich 4. Klasse — Aspekte von Globalisierung",
"subject": "Geschichte und Politische Bildung",
"school_type": "MS / AHS-U",
"grade": "4. Klasse Sek I",
"title": "Globalisierung, weltweite Vernetzung, ökologische Herausforderungen",
"quote": "Aspekte von Globalisierung im 20. und 21. Jahrhundert (Friedenssicherung nach 1945 und welt- und wirtschaftspolitische Machtblöcke in globaler Perspektive, „Kalter Krieg“; politische, ökonomische und gesellschaftliche Phänomene der Globalisierung ua. Urbanisierung, ökologische Herausforderungen; weltweit sich vernetzende Phänomene ua. Konsum, Popkultur, Internet/digitale Medien, Seuchen); Befreiungsbewegungen im Globalen Süden …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "9899 von 151",
"passende_sims": ["weltkueche", "logistik", "busfahrt"],
"passende_plattform_kompetenzen": ["wirtschaft-logistikverstaendnis", "systemisches-denken"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "Globalisierung — historisch/politisch verankert"
},
{
"code": "Übergreifendes Thema 11 — Umweltbildung für nachhaltige Entwicklung (Kompetenzziele Sek I)",
"subject": "Umweltbildung für nachhaltige Entwicklung",
"school_type": "MS / AHS-U",
"grade": "Ende Sek I (übergreifend)",
"title": "BNE — Klimawandel, Ressourcen, Biodiversität, nachhaltiger Konsum",
"quote": "Die Schülerinnen und Schüler können das Zusammenwirken von ökologischen, ökonomischen und sozialen Faktoren bei Umweltproblemen verstehen und mögliche Lösungsvorschläge ableiten; die Bedeutung des Einsatzes ressourcen- und umweltschonender sowie sozial verantwortlicher Verfahren, Produkte und Dienstleistungen in Beruf und Wirtschaft erkennen … die eigenen Lebensgewohnheiten auf Prinzipien der Nachhaltigkeit überprüfen … Visionen für eine umweltverträgliche und nachhaltige Zukunft entwickeln …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "1516 von 151",
"passende_sims": ["klima", "klima-3d", "energiemanager", "farmer", "weltkueche", "fluss"],
"passende_plattform_kompetenzen": ["klimawandel", "landwirtschaft-klimazonen", "energieversorgung-speicherung"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "Übergreifendes Thema 11 — UBNE Kernthemen (Bedeutung)",
"subject": "Umweltbildung für nachhaltige Entwicklung",
"school_type": "MS / AHS-U",
"grade": "1.4. Klasse Sek I",
"title": "Kernthemen BNE — Klimawandel, Lebensraumschutz, Lebensmittelabfälle, Recycling",
"quote": "Als Kernthemen werden ua. Artenvielfalt und -schutz; Klimawandel und -schutz; Lebensräume und deren Vernetzung und Schutz von Boden, Wasser, Luft, Wäldern und Meeren; nachhaltige und sozial gerechte Ressourcen- und Energienutzung; Vermeidung von Lebensmittelabfällen, Abfall und Emissionen; Recycling; Ernährung und verantwortungsvoller Konsum gesehen.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "15 von 151",
"passende_sims": ["farmer", "weltkueche", "klima", "klima-3d", "energiemanager", "fluss"],
"passende_plattform_kompetenzen": ["klimawandel", "landwirtschaft-klimazonen", "energieversorgung-speicherung"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "Lebensmittelverschwendung, verantwortungsvoller Konsum"
},
{
"code": "Übergreifendes Thema 12 — Verkehrs- und Mobilitätsbildung (Kompetenzziele Sek I)",
"subject": "Verkehrs- und Mobilitätsbildung",
"school_type": "MS / AHS-U",
"grade": "Ende Sek I (übergreifend)",
"title": "Klimafreundliche Mobilität, Verkehrssystem, Sicherheit",
"quote": "Die Schülerinnen und Schüler können Verkehrsvorschriften erklären und einhalten; Kriterien für ein unfallfreies Miteinander im Verkehr beschreiben und bei der Teilnahme am Verkehr vorausschauend und verkehrsgerecht … handeln; … Auswirkungen der Mobilität auf die Umwelt beschreiben, das eigene Mobilitätsverhalten reflektieren und eine umweltbewusste Werthaltung einnehmen; Kriterien für ein zukunftsfähiges, nachhaltiges Verkehrssystem benennen und für das eigene Umfeld reflektieren.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "1516 von 151",
"passende_sims": ["busfahrt", "logistik", "heli"],
"passende_plattform_kompetenzen": ["verkehrserziehung-sicherheit", "systemisches-denken"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "Übergreifendes Thema 13 — Wirtschafts-, Finanz- und Verbraucher:innenbildung (Kompetenzziele Sek I)",
"subject": "Wirtschafts-, Finanz- und Verbraucher:innenbildung",
"school_type": "MS / AHS-U",
"grade": "Ende Sek I (übergreifend)",
"title": "Verantwortlicher Geld-/Konsumumgang, Wirtschaftskreislauf, Verbraucherrechte",
"quote": "Die Schülerinnen und Schüler können … ökonomische Phänomene und Zusammenhänge (Einkommens- bzw. Vermögensunterschiede, Wohlstand, Verteilungsgerechtigkeit, alternative Konsumformen, Sozialversicherung) beschreiben und analysieren; ihre Rechte und Pflichten als Verbraucherinnen und Verbraucher in konkreten Lebenssituationen beschreiben und nutzen … die Aufgabe von Wirtschaft und Staat sowie den Sinn … in Zusammenhang mit dem Wirtschaftskreislauf beschreiben …",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0005_602132D5_6AB7_4D68_B4E4_6CF508085BA2.pdfsig",
"reference_seite": "16 von 151",
"passende_sims": ["weltkueche", "logistik", "busfahrt", "farmer"],
"passende_plattform_kompetenzen": ["wirtschaft-logistikverstaendnis", "neu:ernaehrungs-und-konsumkompetenz"],
"neu_oder_vorhanden": "vorhanden"
},
{
"code": "VS Sachunterricht — Geografischer Kompetenzbereich 4. Schulstufe",
"subject": "Sachunterricht",
"school_type": "VS",
"grade": "4. Schulstufe (910 J.)",
"title": "Raumnutzung, Veränderungen in Räumen, Mensch-Raum-Abhängigkeit",
"quote": "Die Schülerinnen und Schüler können die unterschiedliche Nutzung von Räumen durch Menschen erschließen, vergleichen und einschätzen; Veränderungen in Räumen erkennen, über deren Folgen diskutieren und Ideen für die künftige Entwicklung und Gestaltung präsentieren. Zusammenhänge und Abhängigkeiten zwischen Lebensweisen und Lebensräumen von Menschen wahrnehmen, einschätzen und persönliche Schlussfolgerungen ableiten.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0001_CE7F0AA2_A925_4A4D_8C3C_355D12BD22D1.pdfsig",
"reference_seite": "83 von 130 (VS Anlage 1)",
"passende_sims": ["fluss", "klima", "klima-3d", "farmer", "busfahrt"],
"passende_plattform_kompetenzen": ["stadtplanung-raum", "naturgefahren-hochwasser", "landwirtschaft-klimazonen"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "Volksschule 4. Klasse — Raumnutzung erstmals abgedeckt"
},
{
"code": "VS Sachunterricht — Geografischer Kompetenzbereich 3. Schulstufe",
"subject": "Sachunterricht",
"school_type": "VS",
"grade": "3. Schulstufe (89 J.)",
"title": "Mobilität von Menschen, Gütern, Nachrichten; globale Folgen",
"quote": "Die Schülerinnen und Schüler können Elemente und Merkmale von Räumen in Darstellungsmitteln auffinden, raumbezogene Orientierungsraster aufbauen und interpretieren sowie analoge und digitale Orientierungsmittel anwenden. Formen der Mobilität von Menschen, Gütern und Nachrichten erkunden sowie Nutzen und globale Folgen für die Menschen und die Umwelt abschätzen.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0001_CE7F0AA2_A925_4A4D_8C3C_355D12BD22D1.pdfsig",
"reference_seite": "82 von 130",
"passende_sims": ["busfahrt", "logistik", "heli", "weltkueche"],
"passende_plattform_kompetenzen": ["raeumliche-orientierung-europa", "kartenkompetenz-logistik", "wirtschaft-logistikverstaendnis"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "VS 3. Schulstufe — Mobilität von Gütern (globale Folgen) — passt zu Welthandel/Lieferketten"
},
{
"code": "VS Sachunterricht — Wirtschaftlicher Kompetenzbereich 4. Schulstufe",
"subject": "Sachunterricht",
"school_type": "VS",
"grade": "4. Schulstufe (910 J.)",
"title": "Wirtschaftskreislauf ProduktionHandelKonsum, Konsumentscheidungen, Nachhaltigkeit",
"quote": "Die Schülerinnen und Schüler können wirtschaftliche Regeln und Zusammenhänge sowie den wirtschaftlichen Kreislauf von Produktion, Handel und Konsum erkennen und dokumentieren; Gründe und Kriterien für Konsumentscheidungen bewerten; wirtschaftliche, gesundheitliche und soziale Folgen von Konsum diskutieren. Die Bedeutung eines bewussten Handelns für Umweltschutz und Nachhaltigkeit erklären und das eigene Konsumverhalten reflektieren.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0001_CE7F0AA2_A925_4A4D_8C3C_355D12BD22D1.pdfsig",
"reference_seite": "83 von 130",
"passende_sims": ["weltkueche", "logistik", "farmer", "busfahrt"],
"passende_plattform_kompetenzen": ["wirtschaft-logistikverstaendnis", "neu:ernaehrungs-und-konsumkompetenz"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "VS 4. Schulstufe — Welthandel/Konsum-Kreislauf für 910-J. abgedeckt"
},
{
"code": "VS Sachunterricht — Wirtschaftlicher Kompetenzbereich 3. Schulstufe",
"subject": "Sachunterricht",
"school_type": "VS",
"grade": "3. Schulstufe (89 J.)",
"title": "Produktion, Weg der Güter, Angebot/Nachfrage, Knappheit",
"quote": "Die Schülerinnen und Schüler können die Produktion und den Weg von Gütern erkunden und beschreiben, Rahmenbedingungen von Produktion und Konsum wahrnehmen sowie über die Verwendung von Gütern nachdenken. Das Prinzip des Kaufvorgangs in realen wie virtuellen Marktplätzen beschreiben und über die Bedeutung der verfügbaren Mittel (Angebot und Nachfrage, Knappheit) für Kaufentscheidungen reflektieren.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0001_CE7F0AA2_A925_4A4D_8C3C_355D12BD22D1.pdfsig",
"reference_seite": "82 von 130",
"passende_sims": ["weltkueche", "logistik", "farmer", "busfahrt"],
"passende_plattform_kompetenzen": ["wirtschaft-logistikverstaendnis", "planungs-entscheidungskompetenz"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "VS 3. Schulstufe — Welthandel/Güterketten/Märkte für 89-J."
},
{
"code": "VS Sachunterricht — Naturwissenschaftlicher Kompetenzbereich 4. Schulstufe",
"subject": "Sachunterricht",
"school_type": "VS",
"grade": "4. Schulstufe (910 J.)",
"title": "Energie und Energieumwandlungen im Alltag",
"quote": "Die Schülerinnen und Schüler können Entwicklungsstadien und Lebenszyklen bei Menschen, Tieren und Pflanzen beobachten, bestimmen und Artenvielfalt kategorisieren; die Entwicklung menschlichen Lebens von der Zeugung bis zur Geburt beschreiben. Die Bedeutung von Energie und Energieumwandlungen im Alltag erkennen, beschreiben und reflektiert handeln.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0001_CE7F0AA2_A925_4A4D_8C3C_355D12BD22D1.pdfsig",
"reference_seite": "83 von 130",
"passende_sims": ["energiemanager", "klima", "klima-3d"],
"passende_plattform_kompetenzen": ["energieversorgung-speicherung", "klimawandel"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "VS 4. Schulstufe — Energieumwandlung"
},
{
"code": "VS Sachunterricht — Anwendungsbereiche 4. Schulstufe",
"subject": "Sachunterricht",
"school_type": "VS",
"grade": "4. Schulstufe (910 J.)",
"title": "Anwendungsbereiche 4. Schulstufe (Liste)",
"quote": "Anwendungsbereiche: Fortpflanzung und Körperwahrnehmung; Politik und Global Citizenship; Vergangenheit Gegenwart Zukunft; Nachhaltigkeit und Verantwortung; Nah und fern; Energie und Materie; Bionik und Robotik; Information und Social Media; Versorgung und Entsorgung; Entwicklung und Veränderung.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0001_CE7F0AA2_A925_4A4D_8C3C_355D12BD22D1.pdfsig",
"reference_seite": "83 von 130",
"passende_sims": ["eu-werkstatt", "weltkueche", "klima", "klima-3d", "energiemanager"],
"passende_plattform_kompetenzen": ["eu-politische-beteiligung", "klimawandel", "energieversorgung-speicherung"],
"neu_oder_vorhanden": "vorhanden",
"luecke_geschlossen": "VS 4. Schulstufe — Global Citizenship, Nachhaltigkeit als verbindliche Anwendungsbereiche"
},
{
"code": "VS Sachunterricht — Anwendungsbereiche 3. Schulstufe",
"subject": "Sachunterricht",
"school_type": "VS",
"grade": "3. Schulstufe (89 J.)",
"title": "Anwendungsbereiche 3. Schulstufe (Liste)",
"quote": "Anwendungsbereiche: Entwicklungsstadien und Lebenszyklen; Kinderrechte und Diversität; Geografische Gegebenheiten und Orientierung; Geschichte und Ereignisse; Öffentliche Einrichtungen und Institutionen; Erfindungen und Entdeckungen; Produktion und Handel; Klima und Wetter; Stoffe und Veränderungen; Kräfte und Wirkungen.",
"reference_url": "https://www.ris.bka.gv.at/Dokumente/BgblAuth/BGBLA_2023_II_1/Anlagen_0001_CE7F0AA2_A925_4A4D_8C3C_355D12BD22D1.pdfsig",
"reference_seite": "82 von 130",
"passende_sims": ["weltkueche", "klima", "klima-3d", "busfahrt", "logistik"],
"passende_plattform_kompetenzen": ["wirtschaft-logistikverstaendnis", "klimawandel"],
"neu_oder_vorhanden": "vorhanden"
}
],
"neu_vorgeschlagene_plattform_kompetenzen": [
{
"key_slug": "ernaehrungs-und-konsumkompetenz",
"title": "Ernährungs- und Konsumkompetenz",
"category": "gesellschaft",
"age_min": 10,
"age_max": 16,
"description": "Lebensmittel-Auswahl unter Aspekten von Gesundheit, Saisonalität, Regionalität, Nachhaltigkeit und globaler Produktion. Verantwortlicher Konsum, Produktlinienanalyse, Vermeidung von Lebensmittelverschwendung. Begründet im EH-Lehrplan (2. Kl. MS) sowie im übergreifenden Thema 13.",
"primaer_fuer_sims": ["weltkueche"],
"sekundaer_fuer_sims": ["farmer", "logistik"],
"begruendung_aus_lehrplan": "EH 2. Klasse Kompetenzbereich 'Ernährung, Gesundheit und Soziales' + 'Lebensgestaltung, Wirtschaft und Produktion'; Übergreifendes Thema 13 Wirtschafts-/Finanz-/Verbraucher:innenbildung."
}
],
"luecken_themen": [
{
"thema": "Plantage / Monokultur (als Begriff)",
"befund": "Die Lehrpläne sprechen von 'konventioneller vs. biologischer Landwirtschaft' (BU 4. Kl.) und 'anthropogenen Einflüssen' — aber die Begriffe 'Plantage' und 'Monokultur' kommen wörtlich NICHT vor. Über BU 4. Kl. Anwendungsbereich (Charakteristika der konventionellen Landwirtschaft) sinngemäß abgedeckt. Konsequenz: kein wörtliches Zitat möglich — als 'sinngemäß abgedeckt durch BU 4. Kl.' dokumentieren, nicht als eigenständiger Anker."
},
{
"thema": "Fair Trade / fairer Handel (Begriff)",
"befund": "Begriff kommt im GW- und EH-Lehrplan wörtlich NICHT vor. Sinngemäß abgedeckt durch GW 4.3 'Möglichkeiten und Grenzen … nachhaltige[r] wirtschaftliche[r] Aktivitäten und Beziehungen in der globalisierten Welt' und Übergreifendes Thema 11 ('sozial gerechte Ressourcen- und Energienutzung'). Kein wörtliches Zitat — daher kein eigenständiger Anker hinzugefügt."
},
{
"thema": "Klimazonen explizit (Köppen-Geiger)",
"befund": "Begriff 'Klimazonen' kommt im GW-Sek-I-Lehrplan wörtlich NICHT vor. Indirekt abgedeckt durch GW 1.3 (naturräumliche Bedingungen weltweiter landwirtschaftlicher Fallbeispiele) + GW 4.1 (Belastungsgrenzen) + VS Sachunterricht 'Klima und Wetter' (3. Schulstufe). Die Plattform-Kompetenz 'landwirtschaft-klimazonen' (Farmer) bezieht ihre Lehrplan-Verankerung daher indirekt; einen wörtlichen Anker gibt es nur über VS 3. Schulstufe (Anwendungsbereich 'Klima und Wetter'). Bestätigt: KEIN expliziter Sek-I-Lehrplan-Anker zu Köppen-Geiger."
},
{
"thema": "EU-Institutionen (Kommission, Parlament, Ministerrat, Europäischer Rat) namentlich",
"befund": "Die genannten Organe werden im GW-Lehrplan NICHT namentlich aufgezählt; GW 4.2 spricht nur abstrakt von 'EU-Institutionen' (sinngemäß), wörtlich werden 'vier Grundfreiheiten, Euro, Bildungskooperationen, Qualitätsstandards, Verbraucherschutz, Regionalentwicklung' genannt. Die EU-Werkstatt-Inhalte (Kommission/Parlament/Ministerrat/Rat) sind über GW 4.2 + GPB 4. Klasse ('EU als Wirtschafts- und Friedensprojekt') verankert, ohne wörtliche Institutionennamen."
},
{
"thema": "Volksschule 1.2. Schulstufe",
"befund": "Bewusst weggelassen — Zielalter GeoGraSim ist 1014 J., daher nur VS 3.+4. Schulstufe (810 J.) relevant. VS-Anker oben decken die Untergrenze ab."
},
{
"thema": "Mathematik / Physik / Chemie / Bewegung und Sport für Sek I",
"befund": "Bestehende DB hat 10 Mathe + 7 Physik + 2 Chemie + 2 BSP AT-Anker. Diese Recherche fokussierte vereinbarungsgemäß die GW/EH/BU/GPB/Übergreifenden-Themen-Lücken (Welthandel, Welternährung, EU, Klimazonen, VS). Mathematik/Physik wurden nicht erneut durchgekämmt — bei Bedarf separate Runde."
}
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,607 @@
{
"meta": {
"title": "GeoGraSim DE-Lehrplan-Recherche Sek I (Klasse 5-10)",
"erstellt": "2026-06-08",
"instanz": "Atlas",
"scope": "Bayern (Gymnasium + Realschule LehrplanPLUS), NRW (Kernlehrplan Gymnasium G9 ab 2019), Niedersachsen (Kerncurriculum Gymnasium 5-10), KMK/DGfG Bildungsstandards Mittlerer Schulabschluss 2020 (10. Auflage)",
"quellen_geladen": [
"https://vgdh.de/wp-content/uploads/2023/08/Bildungsstandards_Geographie_2020_Web.pdf (PDF -> pdftotext, 4894 Zeilen)",
"https://lehrplannavigator.nrw.de/system/files/media/document/file/g9_ek_klp_3408_2019_06_23.pdf (PDF -> pdftotext, 1270 Zeilen)",
"https://cuvo.nibis.de/cuvo.php?p=download&upload=61 (Webfetch 403, ueber caeci.de/files/seiten/erdkunde/kc%20Sek%201%20Ek.pdf -> pdftotext, 1054 Zeilen)",
"https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/5/geographie (HTML)",
"https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/7/geographie (HTML)",
"https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/10/geographie (HTML)",
"https://www.lehrplanplus.bayern.de/fachlehrplan/lernbereich/66557 (HTML, Klima und Klimawandel)",
"https://www.lehrplanplus.bayern.de/fachlehrplan/lernbereich/66374 (HTML, Planetarische Grundlagen)",
"https://www.lehrplanplus.bayern.de/fachlehrplan/realschule/8/geographie (HTML)",
"https://www.lehrplanplus.bayern.de/fachlehrplan/realschule/9/geographie (HTML)"
],
"quellen_nicht_geladen": [
"https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/8/geographie (WebFetch lieferte nur Homepage-Navigation, KEIN Inhalt extrahierbar)",
"https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/9/geographie (idem)",
"https://www.lehrplanplus.bayern.de/jahrgangsstufenprofil/gymnasium/8/geographie (idem)"
],
"kompetenz_id_legende": {
"1": "klimawandel",
"2": "naturgefahren-hochwasser",
"3": "karten-navigation-alpen",
"4": "stadtplanung-raum",
"5": "raeumliche-orientierung-europa",
"6": "kartenkompetenz-logistik",
"7": "wirtschaft-logistikverstaendnis",
"8": "planungs-entscheidungskompetenz",
"9": "verkehrserziehung-sicherheit",
"10": "systemisches-denken",
"11": "energieversorgung-speicherung",
"12": "erdmechanik-klimazonen",
"13": "eu-politische-beteiligung",
"14": "landwirtschaft-klimazonen"
},
"sim_slugs_aktiv": [
"klima", "klima-3d", "fluss", "heli", "energiemanager", "eu-werkstatt",
"farmer", "busfahrt", "sonnensystem", "logistik", "weltkueche"
],
"regeln": [
"Wörtliche Zitate, max 500 Zeichen.",
"Pro Eintrag genau eine Quelle (PDF oder Lehrplan-Seite).",
"Bei Unsicherheit weglassen.",
"Sim-Empfehlung ist Vorschlag, keine endgueltige Zuordnung — Thomas entscheidet.",
"Kompetenz-Empfehlung verweist auf bestehende 14 GeoGraSim-Plattform-Kompetenzen ODER markiert 'neu_nötig'."
]
},
"kmk_dgfg_bildungsstandards_2020": {
"quelle": "Deutsche Gesellschaft für Geographie e.V. (Hrsg.): Bildungsstandards im Fach Geographie für den Mittleren Schulabschluss mit Aufgabenbeispielen. 10., aktualisierte und überarbeitete Auflage Juli 2020. Bonn 2020.",
"url": "https://vgdh.de/wp-content/uploads/2023/08/Bildungsstandards_Geographie_2020_Web.pdf",
"hinweis": "Diese Standards wurden NICHT von der KMK, sondern von der DGfG entwickelt und gelten als de-facto-nationaler Standard, auf den die meisten Bundeslaender ihre Lehrplaene beziehen.",
"anker": [
{
"code": "DGfG-F1",
"kompetenzbereich": "Fachwissen",
"titel": "F1 Fähigkeit, die Erde als Planeten zu beschreiben",
"quote": "S11 grundlegende planetare Merkmale (z.B. Größe, Gestalt, Aufbau, Neigung der Erdachse, Gravitation) beschreiben, S2 die Stellung und die Bewegungen der Erde im Sonnensystem und deren Auswirkungen erläutern (Tag und Nacht, Jahreszeiten).",
"seite": "S. 12-13",
"sim_vorschlag": ["sonnensystem"],
"kompetenz_vorschlag": 12,
"begruendung": "Direkter Match zu erdmechanik-klimazonen — Erdrevolution, Achsneigung, Tag/Nacht/Jahreszeiten."
},
{
"code": "DGfG-F2",
"kompetenzbereich": "Fachwissen",
"titel": "F2 Fähigkeit, Räume unterschiedlicher Art und Größe als naturgeographische Systeme zu erfassen",
"quote": "S4 gegenwärtige naturgeographische Phänomene und Strukturen in Räumen (z.B. Vulkane, Erdbeben, Gewässernetz, Karstformen) beschreiben und erklären; S7 den Ablauf von naturgeographischen Prozessen in Räumen (z.B. Verwitterung, Wettergeschehen, Gebirgsbildung) darstellen; S8 das Zusammenwirken von Geofaktoren und einfache Kreisläufe (z.B. Höhenstufen der Vegetation, Meeresströmungen und Klima, Ökosystem tropischer Regenwald, Wasserkreislauf) als System darstellen.",
"seite": "S. 13-14",
"sim_vorschlag": ["fluss", "klima", "klima-3d", "sonnensystem"],
"kompetenz_vorschlag": 2,
"begruendung": "Naturgeographische Prozesse — Wasserkreislauf (fluss), Klimasystem (klima), Höhenstufen (heli)."
},
{
"code": "DGfG-F3",
"kompetenzbereich": "Fachwissen",
"titel": "F3 Fähigkeit, Räume unterschiedlicher Art und Größe als humangeographische Systeme zu erfassen",
"quote": "S12 den Ablauf von humangeographischen Prozessen in Räumen (z.B. Strukturwandel, Verstädterung, wirtschaftliche Globalisierung) beschreiben und erklären; S13 das Zusammenwirken von Faktoren in humangeographischen Systemen (z.B. Bevölkerungspolitik, Welthandel, Megastädte) erläutern.",
"seite": "S. 14",
"sim_vorschlag": ["logistik", "busfahrt", "eu-werkstatt", "weltkueche"],
"kompetenz_vorschlag": 7,
"begruendung": "Welthandel und globale Lieferketten als humangeographisches System — Kernthema Logistik/Busfahrt/Weltküche."
},
{
"code": "DGfG-F4",
"kompetenzbereich": "Fachwissen",
"titel": "F4 Fähigkeit, Mensch-Umwelt-Beziehungen in Räumen unterschiedlicher Art und Größe zu analysieren",
"quote": "S18 Auswirkungen der Nutzung und Gestaltung von Räumen (z.B. Rodung, Gewässerbelastung, Bodenerosion, Naturrisiken, Klimawandel, Wassermangel, Bodenversalzung) erläutern; S20 mögliche ökologisch, sozial und/oder ökonomisch sinnvolle Maßnahmen zur Entwicklung und zum Schutz von Räumen (z.B. Tourismusförderung, Aufforstung, Biotopvernetzung, Geotopschutz) erläutern.",
"seite": "S. 14-15",
"sim_vorschlag": ["klima", "klima-3d", "farmer", "fluss"],
"kompetenz_vorschlag": 1,
"begruendung": "Klimawandel als Mensch-Umwelt-Wechselwirkung explizit erwaehnt — klima/klima-3d primaer."
},
{
"code": "DGfG-O1",
"kompetenzbereich": "Räumliche Orientierung",
"titel": "O1 Kenntnis grundlegender topographischer Wissensbestände",
"quote": "S1 verfügen auf den unterschiedlichen Maßstabsebenen über ein basales Orientierungswissen (z.B. Name und Lage der Kontinente und Ozeane, der großen Gebirgszüge der Erde, der einzelnen Bundesländer, von großen europäischen Städten und Flüssen); S2 kennen grundlegende räumliche Orientierungsraster und Ordnungssysteme (z.B. das Gradnetz, die Klima- und Landschaftszonen der Erde).",
"seite": "S. 16-17",
"sim_vorschlag": ["busfahrt", "logistik", "fluggesellschaft"],
"kompetenz_vorschlag": 5,
"begruendung": "Topographisches Europa-Wissen — Kernziel der Busfahrt-/Logistik-Sim."
},
{
"code": "DGfG-O3",
"kompetenzbereich": "Räumliche Orientierung",
"titel": "O3 Fähigkeit zu einem angemessenen Umgang mit Karten (Kartenkompetenz)",
"quote": "S6 topographische, physische, thematische und andere alltagsübliche Karten im Web oder anderen Quellen finden, lesen und unter einer zielführenden Fragestellung auswerten; S8 topographische Übersichtsskizzen und einfache Karten analog und digital anfertigen.",
"seite": "S. 17-18",
"sim_vorschlag": ["heli", "logistik", "busfahrt"],
"kompetenz_vorschlag": 6,
"begruendung": "Direkter Match zu kartenkompetenz-logistik."
},
{
"code": "DGfG-O4",
"kompetenzbereich": "Räumliche Orientierung",
"titel": "O4 Fähigkeit zur Orientierung in Realräumen",
"quote": "S11 mit Hilfe einer Karte und anderer Orientierungshilfen (z.B. Landmarken, Straßennamen, Himmelsrichtungen, mobilen Geräten zur Standortermittlung) ihren Standort im Realraum bestimmen; S13 sich mit Hilfe von Karten und anderen Orientierungshilfen (z.B. Landmarken, Piktogrammen, Kompass, Diensten zur Routenplanung, Augmented Reality) im Realraum bewegen; S14 schematische Darstellungen von Verkehrsnetzen anwenden.",
"seite": "S. 18",
"sim_vorschlag": ["heli", "busfahrt"],
"kompetenz_vorschlag": 3,
"begruendung": "Routenplanung mit Kompass und Karte — Kern der Heli-Sim. Verkehrsnetze fuer Busfahrt."
},
{
"code": "DGfG-M2",
"kompetenzbereich": "Erkenntnisgewinnung/Methoden",
"titel": "M2 Fähigkeit, Informationen zur Behandlung von geographischen Fragestellungen zu gewinnen",
"quote": "S4 problem-, sach- und zielgemäß Informationen aus geographisch relevanten Informationsformen/-medien auswählen; S5 problem-, sach- und zielgemäß Informationen im Gelände (z.B. Beobachten, Kartieren, Messen, Zählen, Probennahme, Befragen) oder durch einfache Versuche und klassische Experimente gewinnen.",
"seite": "S. 19-20",
"sim_vorschlag": ["klima", "klima-3d", "farmer"],
"kompetenz_vorschlag": null,
"begruendung": "Querschnittskompetenz — keiner einzelnen GGS-Kompetenz zuzuordnen, gilt in allen Sims."
},
{
"code": "DGfG-B1",
"kompetenzbereich": "Beurteilung/Bewertung",
"titel": "B1 Fähigkeit, ausgewählte Situationen/Sachverhalte im Raum unter Anwendung geographischer Kenntnisse zu beurteilen",
"quote": "S2 geographische Kenntnisse und die o.g. Kriterien anwenden, um ausgewählte geographisch relevante Sachverhalte, Ereignisse, Herausforderungen und Risiken (z.B. Migration, Hochwasser, Entwicklungshilfe, Flächennutzungskonflikte, Konflikte beim Zusammentreffen von Kulturen, Bürgerkriege, Ressourcenkonflikte) zu beurteilen.",
"seite": "S. 23-24",
"sim_vorschlag": ["fluss", "klima", "eu-werkstatt", "weltkueche"],
"kompetenz_vorschlag": 2,
"begruendung": "Hochwasser ausdruecklich genannt — naturgefahren-hochwasser."
},
{
"code": "DGfG-B4",
"kompetenzbereich": "Beurteilung/Bewertung",
"titel": "B4 Fähigkeit, ausgewählte Sachverhalte/Prozesse unter Einbeziehung fachbasierter und fachübergreifender Werte und Normen zu bewerten",
"quote": "S7 geographisch relevante Werte und Normen (z.B. Menschenrechte, Naturschutz, Nachhaltigkeit) nennen; S8 geographisch relevante Sachverhalte und Prozesse (z.B. Flussregulierung, Tourismus, globale Ordnungen, Entwicklungshilfe/wirtschaftliche Zusammenarbeit, Ressourcennutzung) in Hinblick auf diese Normen und Werte bewerten.",
"seite": "S. 24-25",
"sim_vorschlag": ["eu-werkstatt", "weltkueche", "farmer", "klima"],
"kompetenz_vorschlag": 13,
"begruendung": "Globale Ordnungen, Werte/Normen — EU-Werkstatt-Kern. Auch Welternaehrung."
},
{
"code": "DGfG-H3",
"kompetenzbereich": "Handlung",
"titel": "H3 Bereitschaft zum konkreten Handeln in geographisch relevanten Situationen",
"quote": "S8 fachlich fundiert raumpolitische Entscheidungsprozesse nachzuvollziehen und daran zu partizipieren (z.B. Planungsvorschläge an den Gemeinderat, Beteiligung an der Lokalen Agenda des Heimatortes); S9 sich in ihrem Alltag für eine bessere Qualität der Umwelt, eine nachhaltige Entwicklung, für eine interkulturelle Verständigung und eine Begegnung auf Augenhöhe mit Menschen anderer Regionen sowie ein friedliches und gerechtes Zusammenleben in der Einen Welt einzusetzen.",
"seite": "S. 27",
"sim_vorschlag": ["eu-werkstatt", "klima", "weltkueche"],
"kompetenz_vorschlag": 13,
"begruendung": "Raumpolitische Beteiligung — Kern der EU-Werkstatt-Buergerinitiative."
}
]
},
"nrw_kernlehrplan_erdkunde_gymnasium_g9": {
"quelle": "Ministerium für Schule und Bildung des Landes Nordrhein-Westfalen: Kernlehrplan für die Sekundarstufe I Gymnasium in Nordrhein-Westfalen — Erdkunde. Heft 3408. Düsseldorf 2019 (aufsteigend ab 2019/20).",
"url": "https://lehrplannavigator.nrw.de/system/files/media/document/file/g9_ek_klp_3408_2019_06_23.pdf",
"schulform": "Gymnasium (G9)",
"klassen": "5-10",
"anker": [
{
"code": "NRW-IF1",
"titel": "Inhaltsfeld 1: Unterschiedlich strukturierte Siedlungen",
"quote": "Das Inhaltsfeld umfasst die Funktion, Struktur und Veränderung ländlicher bzw. suburbaner und städtischer Siedlungen sowie die funktionale und verkehrliche Beziehung zwischen diesen. Inhaltliche Schwerpunkte: physiognomische Merkmale von Siedlungen; Daseinsgrundfunktionen in Siedlungen: Wohnen, Arbeit, Versorgung, Erholung, Bildung und Mobilität; Stadt-Umlandbeziehungen; funktionsräumliche Gliederung städtischer Teilräume.",
"seite": "S. 14, 18-19",
"klassenstufe": "Jg. 5-6 (Erprobungsstufe)",
"sim_vorschlag": ["busfahrt"],
"kompetenz_vorschlag": 4,
"begruendung": "Stadt-Umland, Daseinsgrundfunktionen — stadtplanung-raum. Busfahrt-Sim deckt Pendler-/Verkehrsachsen."
},
{
"code": "NRW-IF2",
"titel": "Inhaltsfeld 2: Räumliche Voraussetzungen und Auswirkungen des Tourismus",
"quote": "In diesem Inhaltsfeld wird die naturräumliche Ausstattung in ihrer Bedeutung für eine wirtschaftliche Nutzung durch den Tourismus behandelt. Inhaltliche Schwerpunkte: Formen des Tourismus: Erholungs-, Öko- und Städtetourismus; touristisches Potenzial: Temperatur und Niederschlag, Küsten- und Gebirgslandschaft, touristische Infrastruktur; Merkmale des sanften Tourismus. Topographisches Orientierungsraster: Tourismus- und Erholungsregionen in Deutschland und Europa.",
"seite": "S. 14, 20",
"klassenstufe": "Jg. 5-6",
"sim_vorschlag": ["heli", "busfahrt"],
"kompetenz_vorschlag": 3,
"begruendung": "Gebirgslandschaft + touristische Infrastruktur — Heli-Bergrettung in Alpen-Tourismusregion."
},
{
"code": "NRW-IF3",
"titel": "Inhaltsfeld 3: Arbeit und Versorgung in Wirtschaftsräumen unterschiedlicher Ausstattung",
"quote": "Inhaltliche Schwerpunkte: Standortfaktoren des primären Sektors: Boden, Klima; Produktionskette von Nahrungsmitteln: Herstellung, Verarbeitung, Transport, Handel; strukturelle Veränderungsprozesse in der Landwirtschaft: Intensivierung, Spezialisierung; nachhaltiges Wirtschaften in der Landwirtschaft; Standortfaktoren des sekundären Sektors: Rohstoffe, Arbeitskräfte, Verkehrsinfrastruktur.",
"seite": "S. 14, 21-22",
"klassenstufe": "Jg. 5-6",
"sim_vorschlag": ["farmer", "weltkueche", "logistik", "busfahrt"],
"kompetenz_vorschlag": 14,
"begruendung": "Standortfaktoren primaer (Boden, Klima) — farmer. Produktionskette Nahrungsmittel — weltkueche."
},
{
"code": "NRW-IF4",
"titel": "Inhaltsfeld 4: Aufbau und Dynamik der Erde",
"quote": "Lebens- und Wirtschaftsräume im Bereich der Schwächezonen der Erde sind in besonderer Weise Naturrisiken ausgesetzt. Inhaltliche Schwerpunkte: Schalenbau der Erde: Erdkern, -mantel, -kruste; Plattentektonik: Konvergenz, Divergenz, Subduktion; Naturereignisse: Erd- und Seebeben, Vulkanismus; Leben und Wirtschaften in Risikoräumen: Landwirtschaft, Rohstoffe, Tourismus, Energie.",
"seite": "S. 14, 25-26",
"klassenstufe": "Jg. 7-10 (Sek I)",
"sim_vorschlag": ["sonnensystem"],
"kompetenz_vorschlag": 12,
"begruendung": "Plattentektonik/Vulkanismus eng verwandt mit erdmechanik-klimazonen — Sonnensystem deckt Erdaufbau peripher."
},
{
"code": "NRW-IF5",
"titel": "Inhaltsfeld 5: Wetter und Klima",
"quote": "Inhaltliche Schwerpunkte: Himmelskörper Erde: Schrägstellung der Erdachse, Beleuchtungszonen, Temperaturzonen, Jahreszeiten; Klima und Klimasystem: Aufbau der Atmosphäre, Klimaelemente, Wasserkreislauf, Luftbewegungen, planetarische Zirkulation; Ursachen und Auswirkungen globaler Klimaschwankungen: Treibhauseffekt, Meeresspiegelanstieg, Wetterextreme. Topographisches Orientierungsraster: Klimazonen der Erde.",
"seite": "S. 14, 26-27",
"klassenstufe": "Jg. 7-10",
"sim_vorschlag": ["klima", "klima-3d", "sonnensystem"],
"kompetenz_vorschlag": 1,
"begruendung": "Treibhauseffekt + Meeresspiegelanstieg + Wetterextreme = klima/klima-3d-Kern. Beleuchtungszonen = sonnensystem."
},
{
"code": "NRW-IF6",
"titel": "Inhaltsfeld 6: Landwirtschaftliche Produktion in unterschiedlichen Landschaftszonen",
"quote": "Inhaltliche Schwerpunkte: naturräumliche Bedingungen in den Tropen, Subtropen und Mittelbreiten; Wirtschaftsformen und ökonomische Rahmenbedingungen: Ackerbau, Viehwirtschaft, Plantagenwirtschaft, Subsistenzwirtschaft, marktorientierte Produktion; Möglichkeiten der Überwindung natürlicher Grenzen: Agroforstwirtschaft, Bewässerung, Treibhauskulturen; Folgen unangepasster Nutzung: Regenwaldzerstörung, Desertifikation, Bodenversalzung, Erosion.",
"seite": "S. 14, 27-28",
"klassenstufe": "Jg. 7-10",
"sim_vorschlag": ["farmer", "weltkueche"],
"kompetenz_vorschlag": 14,
"begruendung": "Direkter Match zu landwirtschaft-klimazonen. Farmer-Sim deckt Tropen/Subtropen/Mittelbreiten und Bewaesserung."
},
{
"code": "NRW-IF7",
"titel": "Inhaltsfeld 7: Innerstaatliche und globale Disparitäten",
"quote": "Inhaltliche Schwerpunkte: Entwicklungsindikatoren in den Bereichen Bildung, Demographie, Ernährung, Gesundheit, Infrastruktur, Wirtschaft, Human Development Index (HDI), Gender Development Index (GDI); Länder und Regionen unterschiedlichen Entwicklungsstandes; Möglichkeiten zur Entwicklung strukturschwacher und wenig entwickelter Räume; Projekte der Entwicklungszusammenarbeit, Handelsabkommen.",
"seite": "S. 14-15, 28-29",
"klassenstufe": "Jg. 7-10",
"sim_vorschlag": ["weltkueche", "farmer"],
"kompetenz_vorschlag": null,
"begruendung": "Entwicklungsdisparitaeten + Welternaehrung — kein 1:1 Match in den 14 Plattform-Kompetenzen. Vorschlag: neue Plattform-Kompetenz 'welternaehrung-disparitaeten' oder welternaehrung als Subkategorie unter landwirtschaft-klimazonen."
},
{
"code": "NRW-IF9",
"titel": "Inhaltsfeld 9: Verstädterung und Stadtentwicklung",
"quote": "Inhaltliche Schwerpunkte: grundlegende genetische, funktionale und soziale Merkmale, innere Differenzierung und Wandel von Städten; Phänomene der Verstädterung: Urbanisierung, Herausbildung von Megacities, Metropolisierung, Segregation; Schwerpunkte aktueller Stadtentwicklung: Mobilität, Umweltbelastung, demographischer und sozialer Wandel, Wohnraumverfügbarkeit. Topographisches Orientierungsraster: Agglomerationsräume Europas und der Erde.",
"seite": "S. 15, 31",
"klassenstufe": "Jg. 7-10",
"sim_vorschlag": ["busfahrt"],
"kompetenz_vorschlag": 4,
"begruendung": "Mobilitaet, Urbanisierung, Agglomerationsraeume Europas — Busfahrt deckt EU-Metropolen, stadtplanung-raum als Sekundaer-Kompetenz."
},
{
"code": "NRW-IF10",
"titel": "Inhaltsfeld 10: Räumliche Strukturen unter dem Einfluss von Globalisierung und Digitalisierung",
"quote": "Inhaltliche Schwerpunkte: Merkmale von Globalisierung in Gesellschaft, Ökologie, Ökonomie und Politik; Raumwirksamkeit von Globalisierung: veränderte Standortgefüge, multinationale Konzerne, Global Cities; Wandel von Unternehmen im Zuge der Digitalisierung: Just-in-time-Produktion, Outsourcing; Raumwirksamkeit von Digitalisierung: Standortfaktor digitale Infrastruktur, Onlinehandel, Verlagerung von Arbeitsplätzen, digital vernetzte Güter- und Personenverkehre.",
"seite": "S. 15-16, 31-32",
"klassenstufe": "Jg. 7-10",
"sim_vorschlag": ["logistik", "busfahrt", "eu-werkstatt", "weltkueche"],
"kompetenz_vorschlag": 7,
"begruendung": "Just-in-time + Gueterverkehre = wirtschaft-logistikverstaendnis und systemisches-denken — Logistik-Kern."
},
{
"code": "NRW-SK-Sek1",
"titel": "Übergeordnete Sachkompetenz Sek I (bis Ende Jg. 10)",
"quote": "SK1 beschreiben einzelne Geofaktoren und deren Zusammenwirken sowie ihren Einfluss auf den menschlichen Lebensraum; SK2 verdeutlichen Wirkungen und Folgen von Eingriffen des Menschen in das Geofaktorengefüge; SK3 analysieren durch wirtschaftliche, soziale und politische Faktoren beeinflusste räumliche Strukturen und Entwicklungsprozesse; SK4 erläutern Raumnutzungsansprüche und -konflikte.",
"seite": "S. 22-23",
"klassenstufe": "Jg. 7-10",
"sim_vorschlag": ["klima", "klima-3d", "fluss", "farmer"],
"kompetenz_vorschlag": 10,
"begruendung": "Geofaktoren-Zusammenwirken = systemisches-denken; gilt als Querschnitt fuer mehrere Sims."
},
{
"code": "NRW-MK12-Sek1",
"titel": "MK12 — GIS-Analysen",
"quote": "MK12 führen einfache Analysen mithilfe interaktiver Kartendienste und Geographischer Informationssysteme (GIS) durch.",
"seite": "S. 23",
"klassenstufe": "Jg. 7-10",
"sim_vorschlag": ["heli", "logistik", "busfahrt"],
"kompetenz_vorschlag": 6,
"begruendung": "Interaktive Kartendienste — Kartenkompetenz-logistik; Heli nutzt 2D/3D-Karten interaktiv."
},
{
"code": "NRW-HK1",
"titel": "Übergeordnete Handlungskompetenz Erprobungsstufe (Jg. 5/6)",
"quote": "HK1 vertreten probehandelnd in Raumnutzungskonflikten eigene bzw. fremde Positionen unter Nutzung von Sachargumenten; HK2 beteiligen sich an Planungsaufgaben im Rahmen von Unterrichtsgängen.",
"seite": "S. 17-18",
"klassenstufe": "Jg. 5-6",
"sim_vorschlag": ["eu-werkstatt", "klima"],
"kompetenz_vorschlag": 13,
"begruendung": "Probehandeln in Raumnutzungskonflikten — Kern der EU-Werkstatt (Buergerbeteiligung)."
}
]
},
"niedersachsen_kerncurriculum_erdkunde_gymnasium": {
"quelle": "Niedersächsisches Kultusministerium: Kerncurriculum für das Gymnasium Schuljahrgänge 5-10 — Erdkunde. Hannover (NIBIS). Aktuelle Ausgabe.",
"url_primary": "https://cuvo.nibis.de/cuvo.php?p=download&upload=61",
"url_mirror": "https://caeci.de/files/seiten/erdkunde/kc%20Sek%201%20Ek.pdf",
"schulform": "Gymnasium",
"klassen": "5-10",
"anker": [
{
"code": "NI-FW-J10-Klima",
"kompetenzbereich": "Fachwissen",
"titel": "Klimazonen und Klimawandel (Schuljahrgang 10)",
"quote": "erklären Grundzüge der tropischen Zirkulation; erläutern Ursachen und Auswirkungen von Klimaveränderungen (Rückgriff auf erarbeitetes Fachwissen, z.B. über den Kohlenstoffdioxid-Kreislauf, aus den Fächern Chemie, Physik, Biologie); erläutern Anforderungen an nachhaltige Raumnutzungen im lokalen und globalen Kontext (z.B. bei Dürregefährdung, Übersiedlung, Übernutzung).",
"seite": "S. 12",
"klassenstufe": "Schuljahrgang 10 (Doppeljahrgang 9/10)",
"sim_vorschlag": ["klima", "klima-3d", "farmer"],
"kompetenz_vorschlag": 1,
"begruendung": "Direkter Klimawandel-Anker + tropische Zirkulation als Klimazonen-Grundlage."
},
{
"code": "NI-FW-J8-Naturgefahren",
"kompetenzbereich": "Fachwissen",
"titel": "Naturgefahren und Naturlandschaften (Schuljahrgang 8)",
"quote": "erklären Naturlandschaften im Zusammenhang erdgeschichtlicher Vorgänge als Ergebnis endogener Prozesse (Plattentektonik, Vulkanismus, Erdbeben) — exogener Prozesse (Verwitterung, Erosion, Sedimentation); erläutern die Notwendigkeit schadens- und risikomindernder Maßnahmen bei natürlichen Vorgängen (z.B. Vulkanismus, Erdbeben, Tsunami).",
"seite": "S. 12",
"klassenstufe": "Schuljahrgang 8 (Doppeljahrgang 7/8)",
"sim_vorschlag": ["fluss", "heli", "sonnensystem"],
"kompetenz_vorschlag": 2,
"begruendung": "Schadens-/risikomindernde Massnahmen = Hochwasserschutz-Logik; Plattentektonik = sonnensystem peripher."
},
{
"code": "NI-FW-J10-Mobilitaet",
"kompetenzbereich": "Fachwissen",
"titel": "Mobilität, Migration und Wirtschaftsräume (Schuljahrgang 10)",
"quote": "erläutern Ursachen und raumstrukturelle Auswirkungen von Mobilität und Migration (z.B. räumliche Disparitäten, Bevölkerungsentwicklung, Megastädte); erläutern regionale und globale wirtschaftsräumliche Verflechtungen in Landwirtschaft (z.B. Veredlungswirtschaft, Agrobusiness) und Industrie (z.B. Ruhrgebiet, Manufacturing Belt, Luft- und Raumfahrtstandorte, Automobilstandorte, Energieverbünde).",
"seite": "S. 13",
"klassenstufe": "Schuljahrgang 10",
"sim_vorschlag": ["logistik", "busfahrt", "weltkueche"],
"kompetenz_vorschlag": 7,
"begruendung": "Globale wirtschaftsraeumliche Verflechtungen = Logistik-Kern; Agrobusiness = weltkueche-Ueberlapp."
},
{
"code": "NI-RO-J6-Karte",
"kompetenzbereich": "Räumliche Orientierung",
"titel": "Orientierung mit Karte und Gradnetz (Schuljahrgang 6)",
"quote": "orientieren sich im Raum und auf Karten anhand der Himmelsrichtungen (Sonnenstand, Kompass, natürliche Gegebenheiten); berechnen Entfernungen mit Hilfe des Maßstabs (Absprache mit dem Fach Mathematik); lokalisieren geographische Objekte im Nahraum, in Deutschland und Europa im Gradnetz und auf Karten; benennen in stummen Karten Gewässer, Gebirge, Großlandschaften, Staaten und Siedlungen.",
"seite": "S. 14",
"klassenstufe": "Schuljahrgang 6 (Doppeljahrgang 5/6)",
"sim_vorschlag": ["heli", "busfahrt", "logistik"],
"kompetenz_vorschlag": 6,
"begruendung": "Karten + Gradnetz + Entfernungen = kartenkompetenz-logistik; Himmelsrichtungen mit Kompass = heli."
},
{
"code": "NI-RO-J10-Massstaebe",
"kompetenzbereich": "Räumliche Orientierung",
"titel": "Maßstabsebenen, Luftbilder, GPS (Schuljahrgang 10)",
"quote": "orientieren sich gegebenenfalls auch mit GPS; vergleichen Entfernungen und räumliche Beziehungen auf unterschiedlichen Maßstabsebenen; lokalisieren geographische Objekte in Luftbildern und Satellitenaufnahmen; lokalisieren Regionen unterschiedlichen Entwicklungsstandes.",
"seite": "S. 14",
"klassenstufe": "Schuljahrgang 10",
"sim_vorschlag": ["heli", "logistik"],
"kompetenz_vorschlag": 6,
"begruendung": "GPS + Luftbilder + Massstabsebenen = kartenkompetenz-logistik fortgeschritten; Heli-Sim nutzt 3D-Satellitenaufnahme."
},
{
"code": "NI-Beurt-J8-Klima",
"kompetenzbereich": "Beurteilung/Bewertung",
"titel": "Tragweite menschlicher Eingriffe (Schuljahrgang 8)",
"quote": "beurteilen Nutzungsmöglichkeiten und Lebensbedingungen in verschiedenen Klimazonen; bewerten die Tragweite menschlicher Eingriffe in natürliche Systeme (z.B. Anpflanzung von Eukalyptusbäumen im Mittelmeerraum, Bewässerung, Flussbegradigung, Rodung); nennen Vor- und Nachteile des Lebens in von Naturkatastrophen bedrohten Siedlungsräumen.",
"seite": "S. 17",
"klassenstufe": "Schuljahrgang 8",
"sim_vorschlag": ["fluss", "farmer", "klima"],
"kompetenz_vorschlag": 14,
"begruendung": "Klimazonen-Nutzung = landwirtschaft-klimazonen; Flussbegradigung = naturgefahren-hochwasser sekundaer."
},
{
"code": "NI-Beurt-J10-Welt",
"kompetenzbereich": "Beurteilung/Bewertung",
"titel": "Welthandel, Entwicklung, Klimaprognosen (Schuljahrgang 10)",
"quote": "bewerten geographisch relevante Sachverhalte und Prozesse (z.B. Entwicklungshilfe / wirtschaftliche Zusammenarbeit, Welthandel, Ressourcennutzung) in differenzierender Weise; nehmen Stellung zu Prognosen (z.B. Klimawandel, demographische Entwicklungen); erörtern Möglichkeiten nachhaltiger Nutzung (z.B. Landwirtschaft, Energie, Tourismus).",
"seite": "S. 17-18",
"klassenstufe": "Schuljahrgang 10",
"sim_vorschlag": ["weltkueche", "klima", "eu-werkstatt", "energiemanager"],
"kompetenz_vorschlag": 13,
"begruendung": "Welthandel-Bewertung + Klima-Prognosen — politisch-bewertend = eu-politische-beteiligung; Energie = energieversorgung-speicherung."
}
]
},
"bayern_lehrplanplus": {
"quelle": "Bayerisches Staatsministerium für Unterricht und Kultus: LehrplanPLUS — Fachlehrpläne Geographie Gymnasium und Realschule.",
"url_basis": "https://www.lehrplanplus.bayern.de/",
"anker": [
{
"code": "BY-Gym-Geo5-LB1",
"titel": "Geographie 5 Gymnasium — Lernbereich 1: Geographische Arbeitstechniken",
"quote": "Schülerinnen und Schüler arbeiten grundlegende Informationen aus verschiedenen Kartenarten und dem Atlas heraus; bestimmen Himmelsrichtungen, Lage im Gradnetz, Maßstab und Distanzen; unterscheiden topografische, physische und thematische Karten.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/5/geographie",
"schulform": "Gymnasium",
"klassenstufe": "5",
"sim_vorschlag": ["heli", "busfahrt", "logistik"],
"kompetenz_vorschlag": 6,
"begruendung": "Direkter Match zu kartenkompetenz-logistik in der Einstiegsklasse."
},
{
"code": "BY-Gym-Geo5-LB3",
"titel": "Geographie 5 Gymnasium — Lernbereich 3: Naturräume in Bayern und Deutschland",
"quote": "beschreiben überblicksartig die naturräumliche Gliederung Bayerns und Deutschlands; Alpen und Alpenvorland: Entstehung und Überformung, Naturpotenzial und touristische Nutzung; Mittelgebirge: Entstehung von Bruchschollengebirgen oder Stufenländern, Landschaftsschutz.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/5/geographie",
"schulform": "Gymnasium",
"klassenstufe": "5",
"sim_vorschlag": ["heli", "fluss"],
"kompetenz_vorschlag": 3,
"begruendung": "Alpen-Entstehung + touristische Nutzung = karten-navigation-alpen; Heli-Sim Bergrettung Alpen."
},
{
"code": "BY-Gym-Geo5-LB5",
"titel": "Geographie 5 Gymnasium — Lernbereich 5: Städtische Räume in Bayern und Deutschland",
"quote": "verorten Verdichtungsräume und kennen die politische Gliederung Bayerns und Deutschlands; orientieren sich mit geeigneten Karten in städtischen Räumen.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/5/geographie",
"schulform": "Gymnasium",
"klassenstufe": "5",
"sim_vorschlag": ["busfahrt"],
"kompetenz_vorschlag": 4,
"begruendung": "Verdichtungsraeume + staedtische Orientierung = stadtplanung-raum; Busfahrt deckt Stadt-Stationen."
},
{
"code": "BY-Gym-Geo7-LB1",
"titel": "Geographie 7 Gymnasium — Lernbereich 1: Geographische Arbeitstechniken",
"quote": "werten physische und thematische Karten aus und fertigen eigene Kartenskizzen an; analysieren und zeichnen Klimadiagramme sowie einfache Liniendiagramme; nutzen digitale Geomedien, um geographische Sachverhalte strukturiert darzustellen.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/7/geographie",
"schulform": "Gymnasium",
"klassenstufe": "7",
"sim_vorschlag": ["klima", "farmer", "heli"],
"kompetenz_vorschlag": 6,
"begruendung": "Klimadiagramme + thematische Karten = Kartenkompetenz; klima/farmer arbeiten mit Klimadiagrammen."
},
{
"code": "BY-Gym-Geo7-LB2",
"titel": "Geographie 7 Gymnasium — Lernbereich 2: Einheit und Vielfalt in Europa",
"quote": "ordnen Staaten und Regionen Europas einzelnen Teilräumen richtig zu; stellen die europäische Idee in Grundzügen dar und reflektieren grundlegende Auswirkungen; erläutern beispielhaft die politische, wirtschaftliche und kulturelle Zusammenarbeit in Europa.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/7/geographie",
"schulform": "Gymnasium",
"klassenstufe": "7",
"sim_vorschlag": ["busfahrt", "logistik", "eu-werkstatt"],
"kompetenz_vorschlag": 5,
"begruendung": "Staaten/Regionen Europas zuordnen = raeumliche-orientierung-europa direkt; europaeische Idee + politisch = eu-werkstatt."
},
{
"code": "BY-Gym-Geo7-LB3",
"titel": "Geographie 7 Gymnasium — Lernbereich 3: Naturgeographie Europas im Überblick",
"quote": "arbeiten Kriterien für eine naturräumliche Gliederung Europas heraus; stellen die Grundzüge des Klimas und der Vegetation in Europa dar und zeigen Zusammenhänge; erläutern den Zusammenhang zwischen Plattengrenzen, Vulkanismus und Erdbeben.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/7/geographie",
"schulform": "Gymnasium",
"klassenstufe": "7",
"sim_vorschlag": ["klima", "farmer", "sonnensystem"],
"kompetenz_vorschlag": 14,
"begruendung": "Klima + Vegetation Europa = landwirtschaft-klimazonen Europa-Fokus."
},
{
"code": "BY-Gym-Geo7-LB5",
"titel": "Geographie 7 Gymnasium — Lernbereich 5: Metropolen und Verdichtungsräume in Europa",
"quote": "erläutern Merkmale, Strukturen und Herausforderungen von Metropolen; erklären und vergleichen die Bedeutung von Standortfaktoren unterschiedlicher Branchen.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/7/geographie",
"schulform": "Gymnasium",
"klassenstufe": "7",
"sim_vorschlag": ["busfahrt", "logistik"],
"kompetenz_vorschlag": 4,
"begruendung": "Europaeische Metropolen + Standortfaktoren = stadtplanung-raum auf EU-Ebene; Busfahrt-Stadt-Stationen."
},
{
"code": "BY-Gym-Geo10-LB2",
"titel": "Geographie 10 Gymnasium — Lernbereich 2: Leben in der Einen Welt",
"quote": "vergleichen Lebensrealitäten von Kindern und Jugendlichen in verschiedenen Ländern niedrigen Entwicklungsstandes; charakterisieren anhand unterschiedlicher kartografischer Darstellungen und Statistiken Regionen unterschiedlichen Entwicklungsstandes; nehmen kritisch Stellung zu gängigen Indikatoren zur Bestimmung des Entwicklungsstandes; stellen Merkmale und Probleme von Ländern mit niedrigem Entwicklungsstand dar.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/10/geographie",
"schulform": "Gymnasium",
"klassenstufe": "10",
"sim_vorschlag": ["weltkueche", "farmer"],
"kompetenz_vorschlag": null,
"begruendung": "Eine-Welt-Perspektive + Entwicklungsstand — kein 1:1-Match. Empfehlung: neue Plattform-Kompetenz 'welternaehrung-entwicklung' oder als zentrale Inhaltsdimension der weltkueche-Sim explizit machen."
},
{
"code": "BY-Gym-Geo10-LB4",
"titel": "Geographie 10 Gymnasium — Lernbereich 4: Klima im Wandel",
"quote": "erschließen die Ursachen für den zunehmenden anthropogenen Treibhauseffekt; beurteilen Folgen des Klimawandels auf unterschiedlichen Maßstabsebenen und stellen Anpassungsstrategien dar. Inhalte: Folgen des globalen Klimawandels im Überblick sowie Wetter-/Witterungsextreme und Klimaflüchtlinge.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/10/geographie",
"schulform": "Gymnasium",
"klassenstufe": "10",
"sim_vorschlag": ["klima", "klima-3d"],
"kompetenz_vorschlag": 1,
"begruendung": "Anthropogener Treibhauseffekt + Klimaanpassung = klimawandel-Kern. Klimafluechtlinge ergaenzt durch klima-3d Geflutet-Szenario."
},
{
"code": "BY-Gym-Geo10-LB6",
"titel": "Geographie 10 Gymnasium — Lernbereich 6: Afrika südlich der Sahara",
"quote": "stellen am Beispiel der Desertifikation die Fragilität von Ökosystemen dar; erörtern nachhaltige Nutzungs- und Entwicklungskonzepte; bewerten an einem konkreten Fallbeispiel Möglichkeiten nachhaltiger Entwicklung. Inhalte: Desertifikation in der Sahelzone: Ursachen, Folgen und Gegenmaßnahmen; ausländische Direktinvestitionen.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/10/geographie",
"schulform": "Gymnasium",
"klassenstufe": "10",
"sim_vorschlag": ["farmer", "klima"],
"kompetenz_vorschlag": 14,
"begruendung": "Desertifikation = Klimazone Sahelzone = landwirtschaft-klimazonen direkt."
},
{
"code": "BY-Gym-Geo10-LB7",
"titel": "Geographie 10 Gymnasium — Lernbereich 7: Mittel-/Südamerika",
"quote": "analysieren regionale und globale Folgen menschlicher Eingriffe in Naturräume; erörtern nachhaltige Konzepte der Raumerschließung und -nutzung; überprüfen an geeigneten Raumbeispielen die wirtschaftliche Integration in den globalen Markt. Inhalte: Amazonien: Ursachen und Folgen der Abholzung; Plantagenwirtschaft und Ferntourismus.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/10/geographie",
"schulform": "Gymnasium",
"klassenstufe": "10",
"sim_vorschlag": ["farmer", "weltkueche", "klima"],
"kompetenz_vorschlag": 14,
"begruendung": "Plantagenwirtschaft + Abholzung = landwirtschaft-klimazonen tropische Zone."
},
{
"code": "BY-Rs-Geo7-LB-Klima",
"titel": "Geographie 7 Realschule — Lernbereich: Planetarische Grundlagen im Zusammenhang mit Klima und Vegetation",
"quote": "Erklären planetarischer Grundlagen für unterschiedliche Beleuchtungsverhältnisse und deren Auswirkungen wie Zeitzonen skizzieren; wetterrelevante Vorgänge in der Atmosphäre beschreiben; Wechselbeziehungen zwischen Vegetation und Klima erläutern zur Gliederung der Erde in Geozonen; Klimadiagramme analysieren und räumlich zuordnen. Inhalte: Größe und Gestalt der Erde; Neigung zur Umlaufbahn, Erdrotation (Tag/Nacht, Zeitzonen), Erdrevolution (Jahreszeiten); Klima- und Vegetationszonen der Erde.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/lernbereich/66374",
"schulform": "Realschule",
"klassenstufe": "7",
"sim_vorschlag": ["sonnensystem", "klima", "farmer"],
"kompetenz_vorschlag": 12,
"begruendung": "Erdrotation/Revolution + Beleuchtungszonen + Zeitzonen = erdmechanik-klimazonen direkt — Sonnensystem-Sim deckt alles. Klimadiagramme = klima/farmer Bruecke."
},
{
"code": "BY-Rs-Geo8-LB1",
"titel": "Geographie 8 Realschule — Lernbereich 1: Doppelkontinent Amerika",
"quote": "1.1 beschreiben die physisch-geographischen sowie politischen Strukturen; analysieren an einem Beispiel die Verwundbarkeit. 1.3 Landwirtschaft, Rohstoffe und ökologische Verwundbarkeit: bewerten an je einem Beispiel aus Nord- und Südamerika ökologische Besonderheiten; reflektieren, dass ihr Konsumverhalten Auswirkungen auf Produktionsbedingungen hat.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/realschule/8/geographie",
"schulform": "Realschule",
"klassenstufe": "8",
"sim_vorschlag": ["farmer", "weltkueche"],
"kompetenz_vorschlag": 14,
"begruendung": "Landwirtschaft Nord-/Suedamerika + Konsum-Wirkung = landwirtschaft-klimazonen + weltkueche Klimabilanz."
},
{
"code": "BY-Rs-Geo8-LB4",
"titel": "Geographie 8 Realschule — Lernbereich 4: Globale Verflechtungen im Alltag",
"quote": "stellen wirtschaftliche Verflechtungen anhand der Produktion von zwei unterschiedlichen Konsumgütern dar; beschreiben und bewerten deren Produktionskette unter ökonomischen, ökologischen und sozialen Gesichtspunkten; reflektieren ihr Konsumverhalten; beschreiben Indikatoren für die Bewertung des Entwicklungsstandes von Staaten; erörtern ihre Möglichkeiten als Verbraucher, auf die Einhaltung nachhaltiger Standards einzuwirken.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/realschule/8/geographie",
"schulform": "Realschule",
"klassenstufe": "8",
"sim_vorschlag": ["weltkueche", "logistik", "busfahrt"],
"kompetenz_vorschlag": 7,
"begruendung": "Produktionskette + Konsumreflexion = wirtschaft-logistikverstaendnis; weltkueche-Klimabilanz-Bezug."
},
{
"code": "BY-Rs-Geo9-LB-Klima",
"titel": "Geographie 9 Realschule — Lernbereich Klima und Klimawandel (ca. 10 Std.)",
"quote": "das komplexe Zusammenspiel der Klimafaktoren anhand von Raumbeispielen aus Deutschland und Europa erklären und vergleichen; das Phänomen, die Ursachen und Auswirkungen des globalen Klimawandels beschreiben und die Rolle menschlicher Einflüsse analysieren; zu Klimawandelthemen materialgestützt aus unterschiedlichen Perspektiven Stellung nehmen; lokale und globale Maßnahmen zur Eindämmung des Klimawandels diskutieren; meteorologisch bedingte Naturgefahren und Extremereignisse analysieren.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/lernbereich/66557",
"schulform": "Realschule",
"klassenstufe": "9",
"sim_vorschlag": ["klima", "klima-3d", "energiemanager"],
"kompetenz_vorschlag": 1,
"begruendung": "Klimawandel + Klimapolitik + Extremereignisse (Hochwasser explizit als Inhalt) = klimawandel + naturgefahren-hochwasser sekundaer."
},
{
"code": "BY-Rs-Geo9-LB-Ernaehrung",
"titel": "Geographie 9 Realschule — Lernbereich Landwirtschaft, Ernährung, Boden",
"quote": "Themen sind konventionelle vs. ökologische Landwirtschaft, Wirkungsgefüge zwischen Landwirtschaft und Umwelt, sowie Welternährungssituation und Nahrungsmittelverfügbarkeit. Schüler ordnen Hunger- und Ernährungskrisen naturgeographischen und anthropogenen Ursachen zu.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/realschule/9/geographie",
"schulform": "Realschule",
"klassenstufe": "9",
"sim_vorschlag": ["farmer", "weltkueche"],
"kompetenz_vorschlag": 14,
"begruendung": "Welternaehrungssituation + Landwirtschaft = direkter Anker fuer Farmer + Weltkueche. Bedarfsempfehlung: neue Plattform-Kompetenz 'welternaehrung'."
},
{
"code": "BY-Rs-Geo9-LB-Grenzen",
"titel": "Geographie 9 Realschule — Lernbereich Grenzen im Wandel",
"quote": "Behandelt Europäische Union, Begriff und Auswirkungen der Globalisierung in Umwelt, Wirtschaft, Kommunikation und Kultur sowie Global Player und Grenzentstehung.",
"url": "https://www.lehrplanplus.bayern.de/fachlehrplan/realschule/9/geographie",
"schulform": "Realschule",
"klassenstufe": "9",
"sim_vorschlag": ["eu-werkstatt", "logistik", "busfahrt"],
"kompetenz_vorschlag": 13,
"begruendung": "Europaeische Union + Globalisierung = eu-politische-beteiligung primaer; raeumliche-orientierung-europa sekundaer."
}
]
},
"luecken_und_offene_fragen": [
{
"luecke": "Bayern Gymnasium 8 und 9 (G9-Geographie)",
"details": "WebFetch lieferte fuer https://www.lehrplanplus.bayern.de/fachlehrplan/gymnasium/8/geographie und /9/geographie nur die Startseite, nicht den Lernbereich-Inhalt. Workaround: ueber die Realschule 8/9 abgedeckt (sehr aehnliche Themen) plus Lernbereich 'Klima und Klimawandel' (66557) und 'Planetarische Grundlagen' (66374). Fuer Gym G8/G9 manuell nachpruefen."
},
{
"luecke": "Hauswirtschaft / Verbraucherbildung / AWT in Bundeslaendern",
"details": "Nicht systematisch recherchiert — fuer Weltkueche-Sim relevant, aber Faecher-Bezeichnungen variieren stark (AWT BY, WiB BW, WiB NRW Hauptschule, Verbraucherbildung Berlin etc.). Empfehlung: separater Mini-Auftrag fuer Hauswirtschaft/AWT-Anker."
},
{
"luecke": "Politik/SoWi/Wirtschaft fuer EU-Werkstatt",
"details": "Wird in NRW als 'Wirtschaft-Politik' (KLP separat), in Bayern als Sozialkunde 10 (LehrplanPLUS) und in Niedersachsen als 'Politik-Wirtschaft' angeboten. Fuer eu-werkstatt-Sim wertvoll. Nicht recherchiert in diesem Durchgang."
},
{
"luecke": "Sachunterricht 4. Klasse",
"details": "Als optionaler Bonus angefragt, aber nicht recherchiert — Sachunterricht-Lehrplaene sind extrem bundeslaender-spezifisch und decken nur 9-10-Jaehrige (vor Sek I). Fuer den GeoGraSim-Hauptscope 10-15 J. nicht prioritaer."
},
{
"luecke": "Hessen, Baden-Wuerttemberg, Berlin/Brandenburg, Rheinland-Pfalz, Sachsen",
"details": "Existierende Anker in der DB (id 1085, 1089, 1106, 1109, 1110, 1147) verweisen auf diese Laender, sind aber stichprobenhaft. Eine systematische Recherche pro Land wuerde den Scope dieser Aufgabe sprengen. Empfehlung: nach Atlas-Review der hier dokumentierten Anker entscheiden, ob Hessen+BW+BE/BB als 'big four' Sued/Sued-West-Cluster ergaenzt werden."
},
{
"luecke": "Plattform-Kompetenz 'Welternaehrung / Disparitaeten'",
"details": "Drei zentrale Lehrplan-Anker (NRW IF7, BY-Gym-Geo10-LB2, BY-Rs-Geo9-LB-Ernaehrung) thematisieren Welternaehrung, Hungerkrisen und Entwicklungs-Disparitaeten als Kernthema. In den 14 GeoGraSim-Kompetenzen gibt es dafuer keinen direkten Anker — am naechsten kommt landwirtschaft-klimazonen (#14). Empfehlung an Thomas: entweder #14 explizit um 'Welternaehrung und Disparitaeten' erweitern oder neue Kompetenz #15 'welternaehrung-disparitaeten' anlegen — passend fuer Weltkueche-Sim als Primaer-Anker und Farmer-Sim als Sekundaer-Anker."
}
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,849 @@
HTML geschrieben: 50432 Bytes
">
<head>
<meta charset="UTF-8">
<title>Lehrplan-Coverage Phase 3 · GeoGraSim</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;background:#f5f2eb;color:#2a2a2a;line-height:1.5;padding:24px;max-width:1400px;margin:0 auto}
h1{color:#1f4e5a;font-size:1.6rem;margin-bottom:4px}
.sub{font-size:.82rem;color:#8a8a8a;margin-bottom:18px}
h2{color:#1f4e5a;font-size:1.15rem;margin:28px 0 10px;padding-bottom:6px;border-bottom:2px solid #4a7c8a;display:flex;align-items:center;gap:8px}
h3{color:#4a7c8a;font-size:.98rem;margin:18px 0 8px}
table{width:100%;border-collapse:collapse;font-size:.84rem;background:#fff;border-radius:6px;overflow:hidden;margin-bottom:10px}
th,td{text-align:left;padding:7px 10px;border-bottom:1px solid #e0ddd4;vertical-align:top}
th{background:#1f4e5a;color:#fff;font-weight:600;font-size:.74rem;text-transform:uppercase;letter-spacing:.04em}
tr:hover{background:#fafaf5}
tr:last-child td{border-bottom:none}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums;font-weight:600}
td.num .delta{color:#4a7c4e;font-size:.78em;margin-left:4px}
td.num .delta.zero{color:#bbb}
td.zero{color:#cfcfcf}
code{background:#f5f2eb;padding:2px 6px;border-radius:3px;font-family:'JetBrains Mono',Consolas,monospace;font-size:.85em}
a{color:#1f4e5a}
.badge{display:inline-block;font-size:.68rem;font-weight:700;padding:2px 7px;border-radius:9999px;margin-right:4px}
.b-new{background:#e3e9d6;color:#4a6b3f}
.b-existing{background:#d6e3e9;color:#1f4e5a}
.b-country-at{background:#fde0db;color:#9c4d2e}
.b-country-ch{background:#fff7e0;color:#8a6516}
.b-country-de{background:#d6e3e9;color:#1f4e5a}
.b-country-li{background:#e0ddd4;color:#666}
.top-nav{background:#fff;border:1px solid #e0ddd4;border-radius:8px;padding:10px 14px;margin-bottom:18px;font-size:.86rem;display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.top-nav a{text-decoration:none;padding:5px 11px;border-radius:6px;color:#1f4e5a}
.top-nav a:hover{background:#f5f2eb}
.top-nav a.active{background:#1f4e5a;color:#fff}
.info-box{background:#fff7e0;border-left:4px solid #d2a24c;padding:12px 16px;margin:14px 0;border-radius:0 6px 6px 0;font-size:.88rem}
.stat-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-bottom:14px}
.stat-card{background:#fff;border-radius:8px;padding:12px 16px;border:1px solid #e0ddd4}
.stat-card .label{font-size:.72rem;text-transform:uppercase;color:#888;font-weight:700;letter-spacing:.04em}
.stat-card .value{font-size:1.8rem;font-weight:800;color:#1f4e5a;line-height:1.1;margin-top:2px}
.stat-card .delta{font-size:.85rem;color:#4a7c4e;font-weight:600}
.sim-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px;margin-top:14px}
.sim-card{background:#fff;border:1px solid #e0ddd4;border-radius:8px;padding:14px 16px}
.sim-card h4{color:#1f4e5a;font-size:.98rem;margin-bottom:8px;display:flex;align-items:center;gap:8px}
.sim-card .counts{font-size:.78rem;color:#666;margin-bottom:10px}
.sim-card .counts b{color:#1f4e5a}
.sim-card ul{list-style:none;padding:0;font-size:.84rem}
.sim-card li{padding:6px 0;border-bottom:1px dotted #e0ddd4}
.sim-card li:last-child{border-bottom:none}
.gap-list .gap-item{background:#fff;border:1px solid #e0ddd4;border-left:4px solid #d2a24c;border-radius:0 6px 6px 0;padding:12px 16px;margin-bottom:10px}
.gap-item h4{color:#1f4e5a;font-size:.95rem;margin-bottom:6px}
.gap-item .gap-line{font-size:.84rem;margin-top:4px}
.gap-item .gap-line b{color:#1f4e5a}
.gap-item .status{display:inline-block;font-size:.72rem;font-weight:700;padding:3px 10px;border-radius:9999px;background:#fff7e0;color:#8a6516;margin-top:6px}
.gap-item .status.full{background:#e3e9d6;color:#4a6b3f}
.gap-item .status.partial{background:#fde0db;color:#9c4d2e}
.matrix-table th.col-sim,.matrix-table td.col-sim{background:#fafaf5;font-weight:600;color:#1f4e5a}
.matrix-table td.cell{text-align:center;font-variant-numeric:tabular-nums}
.matrix-table td.cell.zero{color:#cfcfcf}
.matrix-table td.cell .new{display:inline-block;color:#4a7c4e;font-weight:700;font-size:.78em;margin-left:3px}
details{background:#fff;border:1px solid #e0ddd4;border-radius:6px;margin-bottom:8px}
details summary{padding:10px 14px;cursor:pointer;font-weight:600;color:#1f4e5a;font-size:.92rem}
details[open] summary{border-bottom:1px solid #e0ddd4}
details > div{padding:0 14px 12px}
.note{font-size:.78rem;color:#888;font-style:italic;margin-top:6px}
</style>
</head>
<body>
<nav class="top-nav">
<strong>Entwurfswerkzeug</strong>
<a href="index.html">Uebersicht</a>
<a href="../entwurf.html">Entwurf</a>
<a href="../review.html">Review</a>
<a href="accounts.html">Accounts</a>
<a href="standards.html">Standards</a>
<a href="lehrplan-uebersicht.html" class="active">Lehrplan-Coverage</a>
<a href="../../App/admin-sitemap.html" target="_blank">Sitemap</a>
</nav>
<h1>Lehrplan-Coverage Phase 3</h1>
<p class="sub">Stand: 2026-06-08 · Synthese-Snapshot AT/CH/DE/LI · Quelle: <code>.dontDeploy/entwurfswerkzeug/lehrplan-synthese-phase3.json</code></p>
<div style="background:#fff;border-radius:8px;padding:14px 18px;margin:14px 0;border:1px solid #e0ddd4">
<h3 style="color:#1f4e5a;margin:0 0 8px;font-size:1rem">📖 Was du auf dieser Seite siehst — kurz erklärt</h3>
<ul style="font-size:.9rem;line-height:1.6;margin:0;padding-left:1.4em;color:#3a3a3a">
<li><b>Anker</b> = ein wörtlicher Verweis aus einem Schul-Lehrplan auf eines unserer Sim-Themen.
Beispiel: AT-Lehrplan GW Kompetenz 3.3 sagt wörtlich „Import/Export — Außenwirtschaft Österreichs". Das ist ein Anker für unsere Busfahrt/Logistik/Weltküche-Sims.
Jeder Anker hat Land · Fach · Klassenstufe · wörtliches Zitat · Quelle-URL.</li>
<li><b>Plattform-Kompetenz</b> = einer von 14 (mit der neuen 15) intern definierten Sammelbegriffen wie „Klimawandel", „Welthandel", „Kartenkompetenz". Jede Sim ist mehreren zugeordnet. Anker werden indirekt über die Plattform-Kompetenz mit der Sim verknüpft.</li>
<li><b>Coverage / Abdeckung</b> = wie viele Anker wir pro Sim und pro Land haben. Wir zeigen <b>keine Prozente</b>, weil es keine geschlossene Soll-Liste „Lehrplan = 100 Anker" gibt. Stattdessen <b>Ampel</b>:
<span style="background:#e3e9d6;color:#4a6b3f;padding:2px 8px;border-radius:9999px;font-weight:700;font-size:.8em">🟢 gut ≥ 6</span>
<span style="background:#fff7e0;color:#a37e1a;padding:2px 8px;border-radius:9999px;font-weight:700;font-size:.8em">🟡 ok 35</span>
<span style="background:#fde0db;color:#9c4d2e;padding:2px 8px;border-radius:9999px;font-weight:700;font-size:.8em">🟠 dünn 12</span>
<span style="background:#fbcfcf;color:#9c2e2e;padding:2px 8px;border-radius:9999px;font-weight:700;font-size:.8em">🔴 leer 0</span></li>
<li><b>Bestand vs. neu:</b> „Bestand" = was schon in der DB war. „Neu" = was Phase-3-Recherche zusätzlich vorschlägt (noch nicht in DB — muss erst per SQL-Migration übernommen werden).</li>
</ul>
</div>
<div class="info-box">
<b>Quellen:</b> Bestand-Zahlen aus DB (<code>lehrplan_anchors</code>, 14 Plattform-Kompetenzen).
Neu-Zahlen aus Phase-2-Synthese: AT-RIS BGBl. II 1/2023 (BMBWF Sek I + VS), CH-LP21 (Sankt Gallen-Quelle <code>sg.lehrplan.ch</code>), DE-DGfG/NRW/Niedersachsen/Bayern (alles wörtlich aus PDFs extrahiert).
</div>
<h2>Gesamt-Statistik vor und nach Phase 3</h2>
<div class="stat-cards">
<div class="stat-card">
<div class="label">Anker AT (bestand &rarr; neu)</div>
<div class="value">103 &rarr; 134</div>
<div class="delta">+31 aus BGBl. II 1/2023</div>
</div>
<div class="stat-card">
<div class="label">Anker CH (bestand &rarr; neu)</div>
<div class="value">16 &rarr; 57</div>
<div class="delta">+41 aus LP21 (SG)</div>
</div>
<div class="stat-card">
<div class="label">Anker DE (bestand &rarr; neu)</div>
<div class="value">18 &rarr; 64</div>
<div class="delta">+46 DGfG/NRW/NI/BY</div>
</div>
<div class="stat-card">
<div class="label">Anker LI</div>
<div class="value">7 &rarr; 7</div>
<div class="delta" style="color:#888">unveraendert</div>
</div>
<div class="stat-card">
<div class="label">Total Anker</div>
<div class="value">144 &rarr; 262</div>
<div class="delta">+118 Anker</div>
</div>
<div class="stat-card">
<div class="label">Plattform-Kompetenzen</div>
<div class="value">14 &rarr; 15</div>
<div class="delta">+1 (welternaehrung-konsum-disparitaeten)</div>
</div>
</div>
<h2>Sim &times; Land Matrix</h2>
<p class="sub">Anker pro Sim und Land. Format: <code>bestand</code> mit <span style="color:#4a7c4e;font-weight:700">+neu</span>.</p>
<table class="matrix-table">
<thead>
<tr>
<th class="col-sim">Sim</th>
<th class="num">AT</th>
<th class="num">CH (SG)</th>
<th class="num">DE</th>
<th class="num">LI</th>
<th class="num">Sum bestand</th>
<th class="num">Sum neu</th>
</tr>
</thead>
<tbody>
<tr>
<td class="col-sim">Busfahrt</td>
<td class="cell ">16<span class="new">+17</span></td>
<td class="cell ">0<span class="new">+11</span></td>
<td class="cell ">0<span class="new">+18</span></td>
<td class="cell zero">0</td>
<td class="num">16</td>
<td class="num"><span class="delta">+46</span></td>
</tr>
<tr>
<td class="col-sim">Energiemanager</td>
<td class="cell ">22<span class="new">+5</span></td>
<td class="cell ">4<span class="new">+6</span></td>
<td class="cell ">5<span class="new">+2</span></td>
<td class="cell ">2</td>
<td class="num">33</td>
<td class="num"><span class="delta">+13</span></td>
</tr>
<tr>
<td class="col-sim">EU-Werkstatt</td>
<td class="cell ">14<span class="new">+6</span></td>
<td class="cell ">0<span class="new">+4</span></td>
<td class="cell ">0<span class="new">+9</span></td>
<td class="cell zero">0</td>
<td class="num">14</td>
<td class="num"><span class="delta">+19</span></td>
</tr>
<tr>
<td class="col-sim">Farmer</td>
<td class="cell ">32<span class="new">+17</span></td>
<td class="cell ">4<span class="new">+15</span></td>
<td class="cell ">5<span class="new">+16</span></td>
<td class="cell ">2</td>
<td class="num">43</td>
<td class="num"><span class="delta">+48</span></td>
</tr>
<tr>
<td class="col-sim">Fluss</td>
<td class="cell ">18<span class="new">+6</span></td>
<td class="cell ">8<span class="new">+6</span></td>
<td class="cell ">9<span class="new">+7</span></td>
<td class="cell ">4</td>
<td class="num">39</td>
<td class="num"><span class="delta">+19</span></td>
</tr>
<tr>
<td class="col-sim">Heli</td>
<td class="cell ">30<span class="new">+4</span></td>
<td class="cell ">8<span class="new">+6</span></td>
<td class="cell ">9<span class="new">+10</span></td>
<td class="cell ">3</td>
<td class="num">50</td>
<td class="num"><span class="delta">+20</span></td>
</tr>
<tr>
<td class="col-sim">Klima</td>
<td class="cell ">11<span class="new">+14</span></td>
<td class="cell ">4<span class="new">+8</span></td>
<td class="cell ">5<span class="new">+18</span></td>
<td class="cell ">2</td>
<td class="num">22</td>
<td class="num"><span class="delta">+40</span></td>
</tr>
<tr>
<td class="col-sim">Klima 3D</td>
<td class="cell ">11<span class="new">+10</span></td>
<td class="cell ">4<span class="new">+1</span></td>
<td class="cell ">5<span class="new">+7</span></td>
<td class="cell ">2</td>
<td class="num">22</td>
<td class="num"><span class="delta">+18</span></td>
</tr>
<tr>
<td class="col-sim">Logistik</td>
<td class="cell ">26<span class="new">+15</span></td>
<td class="cell ">0<span class="new">+12</span></td>
<td class="cell ">0<span class="new">+14</span></td>
<td class="cell zero">0</td>
<td class="num">26</td>
<td class="num"><span class="delta">+41</span></td>
</tr>
<tr>
<td class="col-sim">Sonnensystem</td>
<td class="cell ">32<span class="new">+2</span></td>
<td class="cell ">8<span class="new">+2</span></td>
<td class="cell ">9<span class="new">+7</span></td>
<td class="cell ">3</td>
<td class="num">52</td>
<td class="num"><span class="delta">+11</span></td>
</tr>
<tr>
<td class="col-sim">Weltkueche</td>
<td class="cell ">0<span class="new">+20</span></td>
<td class="cell ">0<span class="new">+12</span></td>
<td class="cell ">0<span class="new">+15</span></td>
<td class="cell zero">0</td>
<td class="num">0</td>
<td class="num"><span class="delta">+47</span></td>
</tr>
</tbody>
</table>
<h2>Plattform-Kompetenz &times; Land Matrix</h2>
<p class="sub">Anker pro Plattform-Kompetenz und Land.</p>
<table class="matrix-table">
<thead>
<tr>
<th class="col-sim">Kompetenz</th>
<th class="num">AT</th>
<th class="num">CH (SG)</th>
<th class="num">DE</th>
<th class="num">LI</th>
<th class="num">Sum bestand</th>
<th class="num">Sum neu</th>
</tr>
</thead>
<tbody>
<tr>
<td class="col-sim">Klimawandel verstehen und gestalten <br><span class="note"><code>klimawandel</code></span></td>
<td class="cell ">11<span class="new">+2</span></td>
<td class="cell ">4<span class="new">+3</span></td>
<td class="cell ">5<span class="new">+5</span></td>
<td class="cell ">2</td>
<td class="num">22</td>
<td class="num"><span class="delta">+10</span></td>
</tr>
<tr>
<td class="col-sim">Naturgefahren und Hochwasserschutz <br><span class="note"><code>naturgefahren-hochwasser</code></span></td>
<td class="cell ">10<span class="new">+1</span></td>
<td class="cell ">4<span class="new">+2</span></td>
<td class="cell ">5<span class="new">+3</span></td>
<td class="cell ">2</td>
<td class="num">21</td>
<td class="num"><span class="delta">+6</span></td>
</tr>
<tr>
<td class="col-sim">Kartenkompetenz und alpine Geografie <br><span class="note"><code>karten-navigation-alpen</code></span></td>
<td class="cell ">13</td>
<td class="cell ">4<span class="new">+2</span></td>
<td class="cell ">4<span class="new">+3</span></td>
<td class="cell ">1</td>
<td class="num">22</td>
<td class="num"><span class="delta">+5</span></td>
</tr>
<tr>
<td class="col-sim">Stadtplanung und Raumordnung <br><span class="note"><code>stadtplanung-raum</code></span></td>
<td class="cell ">8<span class="new">+2</span></td>
<td class="cell ">4<span class="new">+4</span></td>
<td class="cell ">4<span class="new">+4</span></td>
<td class="cell ">2</td>
<td class="num">18</td>
<td class="num"><span class="delta">+10</span></td>
</tr>
<tr>
<td class="col-sim">Räumliche Orientierung in Europa <br><span class="note"><code>raeumliche-orientierung-europa</code></span></td>
<td class="cell ">6<span class="new">+1</span></td>
<td class="cell ">0<span class="new">+2</span></td>
<td class="cell ">0<span class="new">+2</span></td>
<td class="cell zero">0</td>
<td class="num">6</td>
<td class="num"><span class="delta">+5</span></td>
</tr>
<tr>
<td class="col-sim">Kartenkompetenz im europäischen Maßstab <br><span class="note"><code>kartenkompetenz-logistik</code></span></td>
<td class="cell ">5<span class="new">+1</span></td>
<td class="cell ">0<span class="new">+2</span></td>
<td class="cell ">0<span class="new">+6</span></td>
<td class="cell zero">0</td>
<td class="num">5</td>
<td class="num"><span class="delta">+9</span></td>
</tr>
<tr>
<td class="col-sim">Wirtschafts- und Logistikverständnis <br><span class="note"><code>wirtschaft-logistikverstaendnis</code></span></td>
<td class="cell ">5<span class="new">+4</span></td>
<td class="cell ">0<span class="new">+5</span></td>
<td class="cell ">0<span class="new">+4</span></td>
<td class="cell zero">0</td>
<td class="num">5</td>
<td class="num"><span class="delta">+13</span></td>
</tr>
<tr>
<td class="col-sim">Planungs- und Entscheidungskompetenz <br><span class="note"><code>planungs-entscheidungskompetenz</code></span></td>
<td class="cell ">3</td>
<td class="cell ">0<span class="new">+1</span></td>
<td class="cell zero">0</td>
<td class="cell zero">0</td>
<td class="num">3</td>
<td class="num"><span class="delta">+1</span></td>
</tr>
<tr>
<td class="col-sim">Verkehrserziehung und Sicherheitsbewusstsein <br><span class="note"><code>verkehrserziehung-sicherheit</code></span></td>
<td class="cell ">3<span class="new">+1</span></td>
<td class="cell ">0<span class="new">+2</span></td>
<td class="cell zero">0</td>
<td class="cell zero">0</td>
<td class="num">3</td>
<td class="num"><span class="delta">+3</span></td>
</tr>
<tr>
<td class="col-sim">Systemisches Denken <br><span class="note"><code>systemisches-denken</code></span></td>
<td class="cell ">4<span class="new">+2</span></td>
<td class="cell zero">0</td>
<td class="cell ">0<span class="new">+1</span></td>
<td class="cell zero">0</td>
<td class="num">4</td>
<td class="num"><span class="delta">+3</span></td>
</tr>
<tr>
<td class="col-sim">Energieversorgung und Energiespeicherung <br><span class="note"><code>energieversorgung-speicherung</code></span></td>
<td class="cell ">11<span class="new">+2</span></td>
<td class="cell ">0<span class="new">+4</span></td>
<td class="cell zero">0</td>
<td class="cell zero">0</td>
<td class="num">11</td>
<td class="num"><span class="delta">+6</span></td>
</tr>
<tr>
<td class="col-sim">Erdmechanik, Klimazonen und Astronomie <br><span class="note"><code>erdmechanik-klimazonen</code></span></td>
<td class="cell ">8<span class="new">+1</span></td>
<td class="cell ">0<span class="new">+2</span></td>
<td class="cell ">0<span class="new">+3</span></td>
<td class="cell zero">0</td>
<td class="num">8</td>
<td class="num"><span class="delta">+6</span></td>
</tr>
<tr>
<td class="col-sim">Europäische Union und politische Beteiligung <br><span class="note"><code>eu-politische-beteiligung</code></span></td>
<td class="cell ">8<span class="new">+5</span></td>
<td class="cell ">0<span class="new">+3</span></td>
<td class="cell ">0<span class="new">+5</span></td>
<td class="cell zero">0</td>
<td class="num">8</td>
<td class="num"><span class="delta">+13</span></td>
</tr>
<tr>
<td class="col-sim">Landwirtschaft und globale Klimazonen <br><span class="note"><code>landwirtschaft-klimazonen</code></span></td>
<td class="cell ">8<span class="new">+3</span></td>
<td class="cell ">0<span class="new">+3</span></td>
<td class="cell ">0<span class="new">+7</span></td>
<td class="cell zero">0</td>
<td class="num">8</td>
<td class="num"><span class="delta">+13</span></td>
</tr>
<tr>
<td class="col-sim">Welternaehrung, Konsum und globale Disparitaeten (NEU) <span class="badge b-new">NEU</span><br><span class="note"><code>welternaehrung-konsum-disparitaeten</code></span></td>
<td class="cell ">0<span class="new">+6</span></td>
<td class="cell ">0<span class="new">+6</span></td>
<td class="cell ">0<span class="new">+3</span></td>
<td class="cell zero">0</td>
<td class="num">0</td>
<td class="num"><span class="delta">+15</span></td>
</tr>
</tbody>
</table>
<h2>Pro Sim — Abdeckung mit Ampel-System</h2>
<p class="sub">Pro Sim und Land ein Beispiel-Anker (AT/CH/DE) — plus Ampel für die Gesamtzahl der Anker pro Land. <b>🟢 grün</b> ≥ 6 Anker (gut abgedeckt) · <b>🟡 gelb</b> 3-5 (ok) · <b>🟠 orange</b> 1-2 (dünn) · <b>🔴 rot</b> 0 (gar nicht).</p>
<div class="sim-grid">
<div class="sim-card">
<h4>Busfahrt</h4>
<div class="counts">bestand insgesamt: <b>16</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+46</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 33 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+17 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Welternährung, Landwirtschaft und Klimawandel <div class="note" style="margin-top:2px">GW Kompetenzbereich 1.3 (BGBl. II 1/2023, S. 103)</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>CH — 11 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+11 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Bevölkerung und Migration <div class="note" style="margin-top:2px">LP21 RZG.2.1</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 18 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+18 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> F3 Humangeographische Systeme — Welthandel, Megastädte, Globalisierung <div class="note" style="margin-top:2px">DGfG Bildungsstandards 2020, F3, S. 14</div>
</div>
</div>
</div>
<div class="sim-card">
<h4>Energiemanager</h4>
<div class="counts">bestand insgesamt: <b>33</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+13</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 27 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+5 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Erneuerbare vs. nicht erneuerbare Energieträger, Klimawandel, Ressourcenverteilung <div class="note" style="margin-top:2px">GW Kompetenzbereich 2.1 (BGBl. II 1/2023, S. 103-104)</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>CH — 10 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+6 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Rohstoffe und Energieträger — Vergleich erneuerbar/nicht erneuerbar <div class="note" style="margin-top:2px">LP21 RZG.1.4</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 7 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+2 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Welthandel, Entwicklung, Klimaprognosen <div class="note" style="margin-top:2px">NI KC Erdkunde Gym Sek I, Beurteilung J10, S. 17-18</div>
</div>
</div>
</div>
<div class="sim-card">
<h4>EU-Werkstatt</h4>
<div class="counts">bestand insgesamt: <b>14</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+19</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 20 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+6 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Import/Export — Außenwirtschaft Österreichs mit EU und Welt <div class="note" style="margin-top:2px">GW Kompetenzbereich 3.3 (BGBl. II 1/2023, S. 105)</div>
</div>
</div>
<div style="background:#fff7e0;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#a37e1a">
<span style="font-size:1.1em">🟡</span>
<span>CH — 4 Anker (ok)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+4 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Schweizer Demokratie — Gewaltenteilung, Föderalismus, direkte Demokratie <div class="note" style="margin-top:2px">LP21 RZG.8.1</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 9 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+9 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> F3 Humangeographische Systeme — Welthandel, Megastädte, Globalisierung <div class="note" style="margin-top:2px">DGfG Bildungsstandards 2020, F3, S. 14</div>
</div>
</div>
</div>
<div class="sim-card">
<h4>Farmer</h4>
<div class="counts">bestand insgesamt: <b>43</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+48</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 49 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+17 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Bedarfsgerechte Ernährung, Lebensmittelqualität, ressourcenschonende Zubereitung <div class="note" style="margin-top:2px">EH Kompetenzbereich 1 (BGBl. II 1/2023, S. 132)</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>CH — 19 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+15 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Wetter, Klima, Klimazonen, Klimawandel <div class="note" style="margin-top:2px">LP21 RZG.1.2</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 21 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+16 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> F4 Mensch-Umwelt-Beziehungen — Klimawandel, Wassermangel, Bodenversalzung <div class="note" style="margin-top:2px">DGfG Bildungsstandards 2020, F4, S. 14-15</div>
</div>
</div>
</div>
<div class="sim-card">
<h4>Fluss</h4>
<div class="counts">bestand insgesamt: <b>39</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+19</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 24 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+6 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Plattentektonik und Lebensräume <div class="note" style="margin-top:2px">BU Anwendungsbereiche 3. Kl. (BGBl. II 1/2023, S. 91)</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>CH — 14 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+6 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Naturphänomene und Naturereignisse — Plattentektonik, Erosion, Vulkanismus <div class="note" style="margin-top:2px">LP21 RZG.1.3</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 16 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+7 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> F2 Naturgeographische Systeme — Wasserkreislauf, Klima, Ökosysteme <div class="note" style="margin-top:2px">DGfG Bildungsstandards 2020, F2, S. 13-14</div>
</div>
</div>
</div>
<div class="sim-card">
<h4>Heli</h4>
<div class="counts">bestand insgesamt: <b>50</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+20</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 34 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+4 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Naturgefahren, Wechselwirkung Natur/Wirtschaft, solidarisches Handeln <div class="note" style="margin-top:2px">GW Kompetenzbereich 1.4 (BGBl. II 1/2023, S. 103)</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>CH — 14 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+6 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Tourismus — Alpenraum, Mittelmeerraum, nachhaltiger Tourismus <div class="note" style="margin-top:2px">LP21 RZG.2.5</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 19 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+10 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> O3 Kartenkompetenz — physische, thematische Karten, GIS <div class="note" style="margin-top:2px">DGfG Bildungsstandards 2020, O3, S. 17-18</div>
</div>
</div>
</div>
<div class="sim-card">
<h4>Klima</h4>
<div class="counts">bestand insgesamt: <b>22</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+40</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 25 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+14 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Ernährung individuell/sozial/global; Nachhaltigkeit Ökologie/Ökonomie/Soziales <div class="note" style="margin-top:2px">EH Zentrale Konzepte (BGBl. II 1/2023, S. 131-132)</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>CH — 12 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+8 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Wetter, Klima, Klimazonen, Klimawandel <div class="note" style="margin-top:2px">LP21 RZG.1.2</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 23 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+18 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> F2 Naturgeographische Systeme — Wasserkreislauf, Klima, Ökosysteme <div class="note" style="margin-top:2px">DGfG Bildungsstandards 2020, F2, S. 13-14</div>
</div>
</div>
</div>
<div class="sim-card">
<h4>Klima 3D</h4>
<div class="counts">bestand insgesamt: <b>22</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+18</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 21 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+10 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Landwirtschaft, Biodiversität, anthropogene Einflüsse, CO2 und Klimawandel <div class="note" style="margin-top:2px">BU Anwendungsbereiche 4. Kl. (BGBl. II 1/2023, S. 91-92)</div>
</div>
</div>
<div style="background:#fff7e0;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#a37e1a">
<span style="font-size:1.1em">🟡</span>
<span>CH — 5 Anker (ok)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+1 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Wetter, Klima, Klimazonen, Klimawandel <div class="note" style="margin-top:2px">LP21 RZG.1.2</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 12 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+7 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> F2 Naturgeographische Systeme — Wasserkreislauf, Klima, Ökosysteme <div class="note" style="margin-top:2px">DGfG Bildungsstandards 2020, F2, S. 13-14</div>
</div>
</div>
</div>
<div class="sim-card">
<h4>Logistik</h4>
<div class="counts">bestand insgesamt: <b>26</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+41</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 41 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+15 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Regional, saisonal, globale Produktionsketten, Nachhaltigkeit <div class="note" style="margin-top:2px">EH Anwendungsbereiche Kb 2 (BGBl. II 1/2023, S. 132-133)</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>CH — 12 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+12 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Mobilität und Transport — nachhaltiges Mobilitätsverhalten <div class="note" style="margin-top:2px">LP21 RZG.2.4</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 14 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+14 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> F3 Humangeographische Systeme — Welthandel, Megastädte, Globalisierung <div class="note" style="margin-top:2px">DGfG Bildungsstandards 2020, F3, S. 14</div>
</div>
</div>
</div>
<div class="sim-card">
<h4>Sonnensystem</h4>
<div class="counts">bestand insgesamt: <b>52</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+11</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 34 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+2 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Plattentektonik und Lebensräume <div class="note" style="margin-top:2px">BU Anwendungsbereiche 3. Kl. (BGBl. II 1/2023, S. 91)</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>CH — 10 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+2 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Erde als Planet — Gestalt, Rotation, Revolution <div class="note" style="margin-top:2px">LP21 RZG.1.1</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 16 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+7 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> F1 Erde als Planet — Größe, Gestalt, Aufbau, Achsneigung, Gravitation <div class="note" style="margin-top:2px">DGfG Bildungsstandards 2020, F1, S. 12-13</div>
</div>
</div>
</div>
<div class="sim-card">
<h4>Weltkueche</h4>
<div class="counts">bestand insgesamt: <b>0</b> Anker · neu aus Phase 3: <b style="color:#4a7c4e">+47</b></div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>AT — 20 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+20 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Bedarfsgerechte Ernährung, Lebensmittelqualität, ressourcenschonende Zubereitung <div class="note" style="margin-top:2px">EH Kompetenzbereich 1 (BGBl. II 1/2023, S. 132)</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>CH — 12 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+12 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> Lebensweisen und Ungleichheiten — Armut, Hunger, Bildung <div class="note" style="margin-top:2px">LP21 RZG.2.2</div>
</div>
</div>
<div style="background:#e3e9d6;border-radius:6px;padding:8px 10px;margin-top:6px">
<div style="display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:#4a7c4e">
<span style="font-size:1.1em">🟢</span>
<span>DE — 15 Anker (gut)</span>
<span style="margin-left:auto;color:#4a7c4e;font-weight:700">+15 neu</span> </div>
<div style="font-size:.8rem;color:#444;margin-top:4px;line-height:1.4">
<b>Beispiel:</b> F3 Humangeographische Systeme — Welthandel, Megastädte, Globalisierung <div class="note" style="margin-top:2px">DGfG Bildungsstandards 2020, F3, S. 14</div>
</div>
</div>
</div>
</div>
<h2>Themen-Luecken und Sim-Abdeckung</h2>
<p class="sub">Lehrplan-Begriffe, die in Phase 1 oder 2 als "nicht woertlich abgedeckt" gemeldet wurden — mit Sim-Querverweis.</p>
<div class="gap-list">
<div class="gap-item">
<h4>Plantage / Monokultur (Begriff)</h4>
<div class="gap-line"><b>Sim-Abdeckung:</b> JA: Farmer-Sim modelliert Cluster-Risiko bei Monokultur; weltkueche zeigt Plantage-Klimabilanz.</div>
<div class="gap-line"><b>Lehrplan-Verankerung:</b> AT: nicht woertlich. BU 4. Kl. AT spricht von &quot;Charakteristika der konventionellen Landwirtschaft&quot;. DE: NRW IF6 + BY-Gym-Geo10-LB7 nennen &quot;Plantagenwirtschaft&quot; woertlich. CH: WAH/RZG.3.2 nennt &quot;Plantage&quot; als Produktionsform.</div>
<div class="status partial">teilweise abgedeckt — AT-Lehrplan-Luecke. DE+CH wortwoertlich.</div>
</div>
<div class="gap-item">
<h4>Fair Trade / fairer Handel (Begriff)</h4>
<div class="gap-line"><b>Sim-Abdeckung:</b> NEIN — kein Sim-Konzept dafuer. Sinngemaess: weltkueche Klimabilanz + Farmer Importpreise.</div>
<div class="gap-line"><b>Lehrplan-Verankerung:</b> Begriff in keinem der Lehrplaene woertlich (AT/CH/DE). Sinngemaess ueber Globalisierungsbereiche.</div>
<div class="status ">Sim-Luecke — Vorschlag: Fair-Trade-Badges in weltkueche-Rezepten.</div>
</div>
<div class="gap-item">
<h4>Klimazonen explizit (Koeppen-Geiger)</h4>
<div class="gap-line"><b>Sim-Abdeckung:</b> JA: Farmer (Sahel-Cluster, Tropen), klima (Klimazonen-Karte), klima-3d.</div>
<div class="gap-line"><b>Lehrplan-Verankerung:</b> AT: Begriff &quot;Klimazonen&quot; nicht woertlich im GW Sek I. DE: NRW IF5 &quot;Klimazonen der Erde&quot; + BY-Rs-Geo7 &quot;Klima-/Vegetationszonen&quot;. CH: RZG.1.2 &quot;Landschaftszonen&quot; + &quot;Kalte/gemaessigte/Subtropen-/Tropenzone&quot;.</div>
<div class="status ">AT-Luecke. DE+CH stark verankert. Sim deckt es ab.</div>
</div>
<div class="gap-item">
<h4>EU-Institutionen (Kommission, Parlament, Ministerrat) namentlich</h4>
<div class="gap-line"><b>Sim-Abdeckung:</b> JA: EU-Werkstatt thematisiert Buergerinitiative — Institutionsnamen jedoch implizit.</div>
<div class="gap-line"><b>Lehrplan-Verankerung:</b> AT: GW 4.2 nennt nur &quot;vier Grundfreiheiten, Euro, Bildungskooperationen&quot;. GPB 4. Kl. spricht von EU als Wirtschafts-/Friedensprojekt. Keine Institutionsnamen woertlich. DE: NRW IF7 + BY-Gym-Geo10 thematisieren EU-Politik. CH: RZG.8.3 spricht nur abstrakt von &quot;europaeischer Einigung&quot; + Schweiz-Position.</div>
<div class="status ">Plattform-weite Luecke — kein Lehrplan nennt Kommission/Parlament/Rat woertlich. Atlas-Empfehlung: EU-Werkstatt kann Institutionsnamen einfuehren als Plus.</div>
</div>
<div class="gap-item">
<h4>Systemisches Denken explizit</h4>
<div class="gap-line"><b>Sim-Abdeckung:</b> JA: Plattform-Kompetenz #46 systemisches-denken existiert; logistik + klima nutzen sie.</div>
<div class="gap-line"><b>Lehrplan-Verankerung:</b> AT: GW 4.3 &quot;Wandel der Machtverhaeltnisse zwischen Staaten ... NGOs, Konzernen&quot;. DE: DGfG-F2 &quot;Zusammenwirken von Geofaktoren ... als System darstellen&quot;; NRW SK1 &quot;Geofaktoren-Zusammenwirken&quot;. CH: keine eigene Kompetenz, implizit in RZG.3.x.</div>
<div class="status ">DE explizit, AT sinngemaess, CH implizit.</div>
</div>
<div class="gap-item">
<h4>Logistik / Disposition als LP-Anker</h4>
<div class="gap-line"><b>Sim-Abdeckung:</b> JA: logistik-Sim Kern.</div>
<div class="gap-line"><b>Lehrplan-Verankerung:</b> AT: GW 2.2 + GW 3.3 (Import/Export). DE: NRW IF10 &quot;Just-in-time-Produktion&quot; + DGfG-F3 &quot;Welthandel&quot;. CH: WAH.2.2 &quot;globale Warenstroeme, Import/Export, Logistik&quot; woertlich.</div>
<div class="status ">Stark verankert. Plattform-Kompetenz #43 wirtschaft-logistikverstaendnis matcht direkt.</div>
</div>
<div class="gap-item">
<h4>Welternaehrung &amp; Disparitaeten</h4>
<div class="gap-line"><b>Sim-Abdeckung:</b> JA: Weltkueche + Farmer.</div>
<div class="gap-line"><b>Lehrplan-Verankerung:</b> AT: EH 2. Kl. + UT 13 + GW 1.3. DE: NRW IF7 + BY-Gym-Geo10-LB2 + BY-Rs-Geo9. CH: WAH.4.5 + NMG.7.4 + RZG.2.2.</div>
<div class="status ">In ALLEN drei Laendern explizit. Loest neue Plattform-Kompetenz #108 welternaehrung-konsum-disparitaeten aus.</div>
</div>
<div class="gap-item">
<h4>Plantage / Monokultur als Sim-Mechanik</h4>
<div class="gap-line"><b>Sim-Abdeckung:</b> JA: Farmer-Cluster-Risiko, Schaedlingsanfaelligkeit. Welternaehrung-Sim Rezepte.</div>
<div class="gap-line"><b>Lehrplan-Verankerung:</b> Siehe oben.</div>
<div class="status full">Sim deckt es voll ab.</div>
</div>
</div>
<h2>Methodik und Quellen</h2>
<details>
<summary>Datenquellen, Validierungsstatus, Fall-Back-Hinweise</summary>
<div>
<h3>AT — BGBl. II 1/2023</h3>
<p>RIS Rechtsinformationssystem des Bundes. Direkter PDF-Download + pdftotext-Extraktion. Wortwoertliche Zitate. 31 neue Anker.</p>
<p class="note">Quick-Fixes: 9 Subject-Korrekturen (5x "Politische Bildung" -> "Geschichte und Politische Bildung — Querschnitt", 4x "Informatische Bildung" -> "Digitale Grundbildung"). Optional: 33 GW-Anker mit Subject "GW" -> "Geografie und wirtschaftliche Bildung".</p>
<h3>CH — Lehrplan 21 / Kanton St. Gallen</h3>
<p>Quelle: <code>sg.lehrplan.ch</code> (Live-Auftritt Kanton St. Gallen, bestaetigt 2026-06-08 via WebSearch). LP21-Texte national einheitlich.</p>
<p class="note">URL-Verifikation: WebFetch HTTP 403 (Bot-Block) — wie bei v-fe.lehrplan.ch und gr.lehrplan.ch. URL-Schema (Codes b|id|4 fuer Fachbereiche) identisch. Bei manueller Pruefung von Thomas: 1:1-Match zur Stamm-LP21-Struktur erwartet. 41 neue Anker.</p>
<h3>DE — DGfG / NRW / NI / BY</h3>
<p>4 Quellen:</p>
<ul style="padding-left:18px">
<li><b>DGfG Bildungsstandards 2020</b> (10. Aufl., VGDH-PDF): 11 Anker (10 verwertet, DGfG-M2 als Querschnitt weggelassen)</li>
<li><b>NRW Kernlehrplan G9 Erdkunde 2019</b>: 14 Anker</li>
<li><b>Niedersachsen Kerncurriculum Gymnasium Erdkunde</b>: 7 Anker</li>
<li><b>Bayern LehrplanPLUS (Gym 5/7/10 + RS 7/8/9)</b>: 15 Anker</li>
</ul>
<p class="note">Quick-Fix: 4 Anker mit DGfG-Tot-URL <code>geographie.de/publikationen/</code> auf <code>vgdh.de/wp-content/uploads/2023/08/Bildungsstandards_Geographie_2020_Web.pdf</code> aktualisiert.</p>
<h3>Neue Plattform-Kompetenz</h3>
<p><code>welternaehrung-konsum-disparitaeten</code> — konsolidiert die AT-Vorschlagskompetenz "ernaehrungs-und-konsumkompetenz" und die DE-Vorschlagskompetenz "welternaehrung-disparitaeten" zu einer Plattform-Kompetenz mit wortwoertlichen Belegen aus EH 2. Kl. AT, UT 13 AT, NRW IF7, BY-Gym-Geo10-LB2, BY-Rs-Geo9, WAH.2.2 CH, WAH.4.5 CH, NMG.7.4 CH.</p>
</div>
</details>
</body>
</html>
@@ -0,0 +1,776 @@
[
{
"id": 1082,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1083,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1084,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1085,
"status": "needs_human_review",
"details": "reference_url ist Portal-\/Uebersichtsseite (bildungsserver.berlin-brandenburg.de\/unterricht\/rahmenlehrplaene), nicht der RLP-Teil C Geografie direkt. Der RLP 1-10 Berlin\/Brandenburg ist gueltig (2017+), aber URL ist zu unspezifisch.",
"fix_suggestion": "Auf Direkt-PDF zu \"Teil C Geografie\" des Rahmenlehrplans 1-10 Berlin-Brandenburg verlinken (z. B. bildungsserver.berlin-brandenburg.de\/...\/rlp-1-10-online)."
},
{
"id": 1086,
"status": "ok",
"details": "reference_url ist live (Portal-\/Plan-Seite). Subject \"Geographie\" entspricht offizieller Bezeichnung des verlinkten Bundesland-Lehrplans (HE\/NRW: Erdkunde; BW\/BY\/HH\/SN: Geographie; RP: Erdkunde im Verbund Gesellschaftswissenschaften)."
},
{
"id": 1087,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1088,
"status": "needs_human_review",
"details": "LiLe basiert auf Lehrplan 21 und ist seit 2019 gueltig. Subject RZG passt. URL-Liveness wegen Bot-Block (403) nicht automatisch verifiziert; Domain laut Web-Recherche aktiv. Quote ist redaktionelle Bruecke, nicht woertlich aus LiLe."
},
{
"id": 1089,
"status": "ok",
"details": "reference_url ist live (Portal-\/Plan-Seite). Subject \"Erdkunde\" entspricht offizieller Bezeichnung des verlinkten Bundesland-Lehrplans (HE\/NRW: Erdkunde; BW\/BY\/HH\/SN: Geographie; RP: Erdkunde im Verbund Gesellschaftswissenschaften). Hinweis: Reference-URL ist Uebersichtsseite; das eigentliche KC-PDF (HS\/RS\/Gym) wird von dort verlinkt, aber nicht direkt angesteuert. Praeziserer Direkt-Link zu HE-Erdkunde-KC waere besser."
},
{
"id": 1090,
"status": "needs_human_review",
"details": "reference_url WebFetch HTTP 403 (Bot-Block). Portal cuvo.nibis.de ist Niedersaechsisches Curriculum-Portal, gemaess Web-Recherche aktiv. URL-Liveness manuell verifizieren; Quote manuell pruefen."
},
{
"id": 1091,
"status": "url_dead",
"details": "reference_url liefert HTTP 404 (verifiziert 2026-06-08 via WebFetch).",
"fix_suggestion": "reference_url ersetzen durch https:\/\/vgdh.de\/wp-content\/uploads\/2023\/08\/Bildungsstandards_Geographie_2020_Web.pdf (DGfG 2020, 10. Auflage, durch WebSearch bestaetigt)"
},
{
"id": 1092,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1093,
"status": "subject_wrong",
"details": "Im AT-Lehrplan 2023 (BGBl. II 1\/2023) gibt es kein eigenstaendiges Pflichtfach \"Politische Bildung\". Politische Bildung ist (a) Teil des Fachs \"Geschichte und Politische Bildung\" und (b) Querschnittsthema. Reference-URL ist korrekt (RIS), aber Subject suggeriert ein nicht existierendes Fach.",
"fix_suggestion": "Subject auf \"Geschichte und Politische Bildung\" aendern ODER Quelle als Querschnittsthema kennzeichnen (z. B. \"Querschnitt: Politische Bildung\")."
},
{
"id": 1094,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1095,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1096,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1097,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1098,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1099,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1100,
"status": "needs_human_review",
"details": "BNE ist im Lehrplan 21 als Leitidee\/Querschnitt verankert, nicht als eigenstaendiger Fachbereich. Der URL-Code \"e|200|1\" verweist auf eine Lehrplan-Vorlagen-Sicht (v-fe = Vorlage Fremdevaluation\/Entwurf). Aktuellster Ankerpunkt zu BNE ware lehrplan.ch\/index.php?nav=overview oder kantonale Spezifizierungen. URL-Liveness wegen Bot-Block 403 nicht automatisch verifizierbar.",
"fix_suggestion": "Pruefen, ob ein expliziter LP21-BNE-Querverweis (z. B. https:\/\/www.lehrplan21.ch\/grundlagen-bne) sinnvoller ist als der v-fe-Code."
},
{
"id": 1101,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1102,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1103,
"status": "needs_human_review",
"details": "BNE im LiLe als Querschnittsperspektive vorhanden, aber URL-Code b|6|4 zeigt RZG (Fachbereich), nicht den BNE-Querverweis. Inhaltliche Bindung Geografie<->BNE ist plausibel, aber Quote\/URL koppeln BNE-Marker an RZG-Seite. Manuell pruefen. URL-Liveness wegen Bot-Block nicht automatisch verifizierbar (fl.lehrplan.ch laut Web-Recherche aktiv)."
},
{
"id": 1104,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1105,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1106,
"status": "ok",
"details": "reference_url ist live (Portal-\/Plan-Seite). Subject \"Geographie\" entspricht offizieller Bezeichnung des verlinkten Bundesland-Lehrplans (HE\/NRW: Erdkunde; BW\/BY\/HH\/SN: Geographie; RP: Erdkunde im Verbund Gesellschaftswissenschaften)."
},
{
"id": 1107,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1108,
"status": "needs_human_review",
"details": "LiLe basiert auf Lehrplan 21 und ist seit 2019 gueltig. Subject RZG passt. URL-Liveness wegen Bot-Block (403) nicht automatisch verifiziert; Domain laut Web-Recherche aktiv. Quote ist redaktionelle Bruecke, nicht woertlich aus LiLe."
},
{
"id": 1109,
"status": "needs_human_review",
"details": "reference_url liefert PDF (200 OK), Volltext aus Bot-Sicht nicht extrahierbar. Dateiname und Titel suggerieren passendes Bundesland-\/Fach-Dokument, aber Quote muss manuell gegen den PDF-Inhalt verglichen werden."
},
{
"id": 1110,
"status": "ok",
"details": "reference_url ist live (Portal-\/Plan-Seite). Subject \"Geographie\" entspricht offizieller Bezeichnung des verlinkten Bundesland-Lehrplans (HE\/NRW: Erdkunde; BW\/BY\/HH\/SN: Geographie; RP: Erdkunde im Verbund Gesellschaftswissenschaften). Hinweis: SN-Lehrplan ist 2004 erstmals erlassen, mit Ueberarbeitungen 2009\/2011\/2019. Offiziell weiter gueltig laut Schulportal Sachsen - kein \"version_outdated\", aber inhaltlich der aelteste aktive Plan im Set."
},
{
"id": 1111,
"status": "ok",
"details": "reference_url ist live (Portal-\/Plan-Seite). Subject \"Erdkunde\" entspricht offizieller Bezeichnung des verlinkten Bundesland-Lehrplans (HE\/NRW: Erdkunde; BW\/BY\/HH\/SN: Geographie; RP: Erdkunde im Verbund Gesellschaftswissenschaften). Hinweis: Reference-URL ist Uebersichtsseite; das eigentliche KC-PDF (HS\/RS\/Gym) wird von dort verlinkt, aber nicht direkt angesteuert. Praeziserer Direkt-Link zu HE-Erdkunde-KC waere besser."
},
{
"id": 1112,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1113,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1114,
"status": "subject_wrong",
"details": "Im AT-Lehrplan 2023 (BGBl. II 1\/2023) gibt es kein eigenstaendiges Pflichtfach \"Politische Bildung\". Politische Bildung ist (a) Teil des Fachs \"Geschichte und Politische Bildung\" und (b) Querschnittsthema. Reference-URL ist korrekt (RIS), aber Subject suggeriert ein nicht existierendes Fach.",
"fix_suggestion": "Subject auf \"Geschichte und Politische Bildung\" aendern ODER Quelle als Querschnittsthema kennzeichnen (z. B. \"Querschnitt: Politische Bildung\")."
},
{
"id": 1115,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1116,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1117,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1118,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1119,
"status": "needs_human_review",
"details": "BNE ist im Lehrplan 21 als Leitidee\/Querschnitt verankert, nicht als eigenstaendiger Fachbereich. Der URL-Code \"e|200|1\" verweist auf eine Lehrplan-Vorlagen-Sicht (v-fe = Vorlage Fremdevaluation\/Entwurf). Aktuellster Ankerpunkt zu BNE ware lehrplan.ch\/index.php?nav=overview oder kantonale Spezifizierungen. URL-Liveness wegen Bot-Block 403 nicht automatisch verifizierbar.",
"fix_suggestion": "Pruefen, ob ein expliziter LP21-BNE-Querverweis (z. B. https:\/\/www.lehrplan21.ch\/grundlagen-bne) sinnvoller ist als der v-fe-Code."
},
{
"id": 1120,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1121,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1122,
"status": "needs_human_review",
"details": "BNE im LiLe als Querschnittsperspektive vorhanden, aber URL-Code b|6|4 zeigt RZG (Fachbereich), nicht den BNE-Querverweis. Inhaltliche Bindung Geografie<->BNE ist plausibel, aber Quote\/URL koppeln BNE-Marker an RZG-Seite. Manuell pruefen. URL-Liveness wegen Bot-Block nicht automatisch verifizierbar (fl.lehrplan.ch laut Web-Recherche aktiv)."
},
{
"id": 1123,
"status": "url_dead",
"details": "reference_url liefert HTTP 404 (verifiziert 2026-06-08 via WebFetch).",
"fix_suggestion": "reference_url ersetzen durch https:\/\/vgdh.de\/wp-content\/uploads\/2023\/08\/Bildungsstandards_Geographie_2020_Web.pdf (DGfG 2020, 10. Auflage, durch WebSearch bestaetigt)"
},
{
"id": 1124,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1125,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1126,
"status": "ok",
"details": "reference_url ist live (Portal-\/Plan-Seite). Subject \"Erdkunde\" entspricht offizieller Bezeichnung des verlinkten Bundesland-Lehrplans (HE\/NRW: Erdkunde; BW\/BY\/HH\/SN: Geographie; RP: Erdkunde im Verbund Gesellschaftswissenschaften)."
},
{
"id": 1127,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1128,
"status": "needs_human_review",
"details": "LiLe basiert auf Lehrplan 21 und ist seit 2019 gueltig. Subject RZG passt. URL-Liveness wegen Bot-Block (403) nicht automatisch verifiziert; Domain laut Web-Recherche aktiv. Quote ist redaktionelle Bruecke, nicht woertlich aus LiLe."
},
{
"id": 1129,
"status": "needs_human_review",
"details": "reference_url WebFetch HTTP 403 (Bot-Block). Portal cuvo.nibis.de ist Niedersaechsisches Curriculum-Portal, gemaess Web-Recherche aktiv. URL-Liveness manuell verifizieren; Quote manuell pruefen."
},
{
"id": 1130,
"status": "needs_human_review",
"details": "reference_url liefert PDF (200 OK), Volltext aus Bot-Sicht nicht extrahierbar. Dateiname und Titel suggerieren passendes Bundesland-\/Fach-Dokument, aber Quote muss manuell gegen den PDF-Inhalt verglichen werden."
},
{
"id": 1131,
"status": "subject_wrong",
"details": "Im AT-Lehrplan 2023 ist \"Informatische Bildung\" Querschnittsthema; das eigenstaendige Pflichtfach heisst \"Digitale Grundbildung\". Wenn der Anker ein Fach meinen soll, ist die Bezeichnung falsch.",
"fix_suggestion": "Subject auf \"Digitale Grundbildung\" aendern (wenn Fach gemeint) ODER explizit als \"Querschnitt: Informatische Bildung\" auszeichnen."
},
{
"id": 1132,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1133,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1134,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1135,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1136,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1137,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1138,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1139,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1140,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1141,
"status": "url_dead",
"details": "reference_url liefert HTTP 404 (verifiziert 2026-06-08 via WebFetch).",
"fix_suggestion": "reference_url ersetzen durch https:\/\/vgdh.de\/wp-content\/uploads\/2023\/08\/Bildungsstandards_Geographie_2020_Web.pdf (DGfG 2020, 10. Auflage, durch WebSearch bestaetigt)"
},
{
"id": 1142,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1143,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1144,
"status": "ok",
"details": "reference_url ist live (Portal-\/Plan-Seite). Subject \"Geographie\" entspricht offizieller Bezeichnung des verlinkten Bundesland-Lehrplans (HE\/NRW: Erdkunde; BW\/BY\/HH\/SN: Geographie; RP: Erdkunde im Verbund Gesellschaftswissenschaften)."
},
{
"id": 1145,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1146,
"status": "needs_human_review",
"details": "LiLe basiert auf Lehrplan 21 und ist seit 2019 gueltig. Subject RZG passt. URL-Liveness wegen Bot-Block (403) nicht automatisch verifiziert; Domain laut Web-Recherche aktiv. Quote ist redaktionelle Bruecke, nicht woertlich aus LiLe."
},
{
"id": 1147,
"status": "ok",
"details": "reference_url ist live (Portal-\/Plan-Seite). Subject \"Erdkunde\" entspricht offizieller Bezeichnung des verlinkten Bundesland-Lehrplans (HE\/NRW: Erdkunde; BW\/BY\/HH\/SN: Geographie; RP: Erdkunde im Verbund Gesellschaftswissenschaften). Hinweis: Reference-URL ist Uebersichtsseite; das eigentliche KC-PDF (HS\/RS\/Gym) wird von dort verlinkt, aber nicht direkt angesteuert. Praeziserer Direkt-Link zu HE-Erdkunde-KC waere besser."
},
{
"id": 1148,
"status": "needs_human_review",
"details": "reference_url liefert PDF (200 OK), Volltext aus Bot-Sicht nicht extrahierbar. Dateiname und Titel suggerieren passendes Bundesland-\/Fach-Dokument, aber Quote muss manuell gegen den PDF-Inhalt verglichen werden."
},
{
"id": 1149,
"status": "subject_wrong",
"details": "Im AT-Lehrplan 2023 (BGBl. II 1\/2023) gibt es kein eigenstaendiges Pflichtfach \"Politische Bildung\". Politische Bildung ist (a) Teil des Fachs \"Geschichte und Politische Bildung\" und (b) Querschnittsthema. Reference-URL ist korrekt (RIS), aber Subject suggeriert ein nicht existierendes Fach.",
"fix_suggestion": "Subject auf \"Geschichte und Politische Bildung\" aendern ODER Quelle als Querschnittsthema kennzeichnen (z. B. \"Querschnitt: Politische Bildung\")."
},
{
"id": 1150,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1151,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1152,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1153,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1154,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1155,
"status": "needs_human_review",
"details": "BNE ist im Lehrplan 21 als Leitidee\/Querschnitt verankert, nicht als eigenstaendiger Fachbereich. Der URL-Code \"e|200|1\" verweist auf eine Lehrplan-Vorlagen-Sicht (v-fe = Vorlage Fremdevaluation\/Entwurf). Aktuellster Ankerpunkt zu BNE ware lehrplan.ch\/index.php?nav=overview oder kantonale Spezifizierungen. URL-Liveness wegen Bot-Block 403 nicht automatisch verifizierbar.",
"fix_suggestion": "Pruefen, ob ein expliziter LP21-BNE-Querverweis (z. B. https:\/\/www.lehrplan21.ch\/grundlagen-bne) sinnvoller ist als der v-fe-Code."
},
{
"id": 1156,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1157,
"status": "needs_human_review",
"details": "Subject ist Lehrplan-21-Fachbereich, plausibel. URL-Liveness fuer v-fe.lehrplan.ch nicht automatisch verifizierbar (WebFetch 403, Bot-Block). Domain laut Web-Recherche aktiv, Code-Schema (b|<id>|4) ist Lehrplan-21-konform. Quote ist redaktionelle Bruecke (kein woertliches LP21-Zitat) und muss manuell gegen den offiziellen LP21-Wortlaut geprueft werden."
},
{
"id": 1158,
"status": "needs_human_review",
"details": "BNE im LiLe als Querschnittsperspektive vorhanden, aber URL-Code b|6|4 zeigt RZG (Fachbereich), nicht den BNE-Querverweis. Inhaltliche Bindung Geografie<->BNE ist plausibel, aber Quote\/URL koppeln BNE-Marker an RZG-Seite. Manuell pruefen. URL-Liveness wegen Bot-Block nicht automatisch verifizierbar (fl.lehrplan.ch laut Web-Recherche aktiv)."
},
{
"id": 1159,
"status": "url_dead",
"details": "reference_url liefert HTTP 404 (verifiziert 2026-06-08 via WebFetch).",
"fix_suggestion": "reference_url ersetzen durch https:\/\/vgdh.de\/wp-content\/uploads\/2023\/08\/Bildungsstandards_Geographie_2020_Web.pdf (DGfG 2020, 10. Auflage, durch WebSearch bestaetigt)"
},
{
"id": 1160,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1161,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1162,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1163,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1164,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1165,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1166,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1167,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1168,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1169,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1170,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1171,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1172,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1173,
"status": "subject_wrong",
"details": "Im AT-Lehrplan 2023 (BGBl. II 1\/2023) gibt es kein eigenstaendiges Pflichtfach \"Politische Bildung\". Politische Bildung ist (a) Teil des Fachs \"Geschichte und Politische Bildung\" und (b) Querschnittsthema. Reference-URL ist korrekt (RIS), aber Subject suggeriert ein nicht existierendes Fach.",
"fix_suggestion": "Subject auf \"Geschichte und Politische Bildung\" aendern ODER Quelle als Querschnittsthema kennzeichnen (z. B. \"Querschnitt: Politische Bildung\")."
},
{
"id": 1174,
"status": "subject_wrong",
"details": "Im AT-Lehrplan 2023 ist \"Informatische Bildung\" Querschnittsthema; das eigenstaendige Pflichtfach heisst \"Digitale Grundbildung\". Wenn der Anker ein Fach meinen soll, ist die Bezeichnung falsch.",
"fix_suggestion": "Subject auf \"Digitale Grundbildung\" aendern (wenn Fach gemeint) ODER explizit als \"Querschnitt: Informatische Bildung\" auszeichnen."
},
{
"id": 1175,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1176,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1177,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1178,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1179,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1180,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1181,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1182,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1183,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1184,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1185,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1186,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1187,
"status": "subject_wrong",
"details": "Im AT-Lehrplan 2023 ist \"Informatische Bildung\" Querschnittsthema; das eigenstaendige Pflichtfach heisst \"Digitale Grundbildung\". Wenn der Anker ein Fach meinen soll, ist die Bezeichnung falsch.",
"fix_suggestion": "Subject auf \"Digitale Grundbildung\" aendern (wenn Fach gemeint) ODER explizit als \"Querschnitt: Informatische Bildung\" auszeichnen."
},
{
"id": 1188,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1189,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1190,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1191,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1192,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1193,
"status": "subject_wrong",
"details": "Im AT-Lehrplan 2023 ist \"Informatische Bildung\" Querschnittsthema; das eigenstaendige Pflichtfach heisst \"Digitale Grundbildung\". Wenn der Anker ein Fach meinen soll, ist die Bezeichnung falsch.",
"fix_suggestion": "Subject auf \"Digitale Grundbildung\" aendern (wenn Fach gemeint) ODER explizit als \"Querschnitt: Informatische Bildung\" auszeichnen."
},
{
"id": 1194,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1195,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1196,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1197,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1198,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1199,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1200,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1201,
"status": "subject_wrong",
"details": "Im AT-Lehrplan 2023 (BGBl. II 1\/2023) gibt es kein eigenstaendiges Pflichtfach \"Politische Bildung\". Politische Bildung ist (a) Teil des Fachs \"Geschichte und Politische Bildung\" und (b) Querschnittsthema. Reference-URL ist korrekt (RIS), aber Subject suggeriert ein nicht existierendes Fach.",
"fix_suggestion": "Subject auf \"Geschichte und Politische Bildung\" aendern ODER Quelle als Querschnittsthema kennzeichnen (z. B. \"Querschnitt: Politische Bildung\")."
},
{
"id": 1202,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1203,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1204,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1205,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1206,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1207,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1208,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1209,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1210,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1211,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1212,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1213,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1214,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1215,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1216,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1217,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1218,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1219,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1220,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1221,
"status": "needs_human_review",
"details": "Subject \"GW\" ist gaengiges Kuerzel, offizieller Fachname seit BGBl. II 1\/2023 lautet \"Geografie und wirtschaftliche Bildung\". Reference passt zur aktuellen Verordnung (RIS Gesetzesnummer 20007850 in GeltendeFassung).",
"fix_suggestion": "Optional: Subject auf \"Geografie und wirtschaftliche Bildung\" angleichen (offizielle Bezeichnung 2023)."
},
{
"id": 1222,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1223,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1224,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
},
{
"id": 1225,
"status": "needs_human_review",
"details": "Subject und URL OK. Quote ist eine redaktionelle Brueckenformulierung (kein direktes RIS-Zitat). Wortlaut muss gegen den Volltext der jeweiligen Fachlehrplan-Anlage manuell abgeglichen werden."
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+152
View File
@@ -0,0 +1,152 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Plattform-Standards · Entwurfswerkzeug</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;background:#f5f2eb;color:#2a2a2a;line-height:1.5;padding:24px;max-width:1100px;margin:0 auto}
h1{color:#1f4e5a;font-size:1.5rem;margin-bottom:4px}
.sub{font-size:.82rem;color:#8a8a8a;margin-bottom:14px}
h2{color:#1f4e5a;font-size:1.15rem;margin:28px 0 8px;padding-bottom:4px;border-bottom:2px solid #4a7c8a}
h3{color:#4a7c8a;font-size:1rem;margin:18px 0 8px}
h4{color:#3a3a3a;font-size:.92rem;margin:14px 0 6px;font-weight:700}
p{margin:8px 0}
code{background:#f5f2eb;padding:2px 6px;border-radius:3px;font-family:'JetBrains Mono',Consolas,monospace;font-size:.85em}
pre{background:#1f2a30;color:#e8e4d8;padding:14px 18px;border-radius:8px;overflow:auto;font-family:'JetBrains Mono',Consolas,monospace;font-size:.82rem;line-height:1.5;margin:10px 0}
pre code{background:transparent;color:inherit;padding:0;font-size:inherit}
a{color:#1f4e5a}
.demo{background:#fff;border:1px solid #e0ddd4;border-radius:6px;padding:14px 18px;margin:10px 0}
.demo b{color:#1f4e5a}
.ok-box{background:#dff0e6;border-left:4px solid #5a8a5e;padding:10px 14px;margin:10px 0;border-radius:0 6px 6px 0;font-size:.88rem}
.warn-box{background:#fff7e0;border-left:4px solid #d2a24c;padding:10px 14px;margin:10px 0;border-radius:0 6px 6px 0;font-size:.88rem}
table{width:100%;border-collapse:collapse;font-size:.88rem;background:#fff;border-radius:6px;overflow:hidden;margin:10px 0}
th,td{text-align:left;padding:8px 12px;border-bottom:1px solid #e0ddd4;vertical-align:top}
th{background:#1f4e5a;color:#fff;font-weight:600;font-size:.74rem;text-transform:uppercase;letter-spacing:.04em}
tr:last-child td{border-bottom:none}
/* Demo: real Glossar-Tooltip-Klasse wie in Prod */
.ggs-g{cursor:help;border-bottom:1px dashed currentColor;text-decoration:none;color:inherit;background:rgba(0,0,0,.02);padding:0 2px;border-radius:2px}
.ggs-g:hover{background:rgba(31,75,55,.10);border-bottom-style:solid}
/* einheitliches Entwurfswerkzeug-Menü */
.top-nav{background:#fff;border:1px solid #e0ddd4;border-radius:8px;padding:10px 14px;margin-bottom:18px;font-size:.86rem;display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.top-nav strong{color:#1f4e5a}
.top-nav a{text-decoration:none;padding:5px 11px;border-radius:6px;color:#1f4e5a}
.top-nav a:hover{background:#f5f2eb}
.top-nav a.active{background:#1f4e5a;color:#fff}
</style>
</head>
<body>
<nav class="top-nav">
<strong>🛠 Entwurfswerkzeug</strong>
<a href="index.html">Übersicht</a>
<a href="../entwurf.html">Entwurf</a>
<a href="../review.html">Review</a>
<a href="accounts.html">🔑 Accounts</a>
<a href="standards.html" class="active">📐 Standards</a>
<a href="legacy/" style="color:#6b6448">📂 legacy/</a>
<a href="toDelete/" style="color:#9c4d2e">📂 toDelete/</a>
<a href="../../App/admin-sitemap.html" target="_blank">📊 Sitemap</a>
</nav>
<h1>📐 Plattform-Standards</h1>
<p class="sub">UI-Patterns, die in allen Sims gleich aussehen + funktionieren sollen. Pflegt Atlas, hier dokumentiert.</p>
<!-- ============================ -->
<h2>🏷️ Glossar-Tooltip-Pattern</h2>
<p>Bei Fachbegriffen in Sim-Texten: kleines Popup mit Kurzerklärung + Link zum vollen Glossar-Eintrag. Erprobt seit Heli + Klima 2D/3D.</p>
<h3>So sieht's aus</h3>
<div class="demo">
Das ist ein Beispieltext mit einem
<span class="ggs-g" data-glossar="welthandel">Welthandel</span>-Begriff drin
und auch <span class="ggs-g" data-glossar="klimazone">Klimazone</span> ist klickbar.
Beim Klick öffnet sich rechts unter dem Begriff ein kleiner Popup mit Definition.
</div>
<h3>Markup-Standard</h3>
<pre><code>&lt;span class="ggs-g" data-glossar="welthandel"&gt;Welthandel&lt;/span&gt;</code></pre>
<p>Regel: <code>data-glossar</code>-Attribut hält den <code>key_slug</code> des Glossar-Eintrags. Die CSS-Klasse <code>ggs-g</code> sorgt für die gestrichelte Unterlinie + Hover-Effekt + ist der Trigger für den JS-Handler.</p>
<h3>Einbindung im Sim-Wrapper (PHP)</h3>
<pre><code>// in App/pages/&lt;sim&gt;.php nach dem Live-Heartbeat-Script:
$glossarScript = '&lt;script src="' . BASE_PATH . '/assets/js/glossar-tooltip.js" defer&gt;&lt;/script&gt;'
. '&lt;script&gt;window.addEventListener("DOMContentLoaded",function(){'
. 'if(window.GlossarTooltip)GlossarTooltip.init({apiBase:' . json_encode(BASE_PATH . '/api') . ',moduleId:"&lt;sim-slug&gt;"});'
. '});&lt;/script&gt;';
$html = str_replace('&lt;/body&gt;', $glossarScript . "\n&lt;/body&gt;", $html);</code></pre>
<h3>API der Lib</h3>
<table>
<thead><tr><th>Funktion</th><th>Zweck</th></tr></thead>
<tbody>
<tr><td><code>GlossarTooltip.init({apiBase, moduleId, fallback})</code></td><td>Aktiviert das Pattern auf der Seite. Lädt die Glossar-Snippets pro Modul via API-Call <code>/api/glossar.php?module=&lt;moduleId&gt;</code>.</td></tr>
<tr><td><code>GlossarTooltip.addFallback(key, {title, text})</code></td><td>Für Begriffe ohne DB-Eintrag (oder bei Offline-Demo): lokales Snippet hinzufügen.</td></tr>
<tr><td><code>GlossarTooltip.markup(label, slug)</code></td><td>Hilfsfunktion für dynamisch generierte HTML-Strings: liefert das fertige <code>&lt;span&gt;</code>-Markup.</td></tr>
</tbody>
</table>
<h3>Wo schon im Einsatz</h3>
<table>
<thead><tr><th>Sim</th><th>Status</th><th>Pattern-Variante</th></tr></thead>
<tbody>
<tr><td>Heli</td><td>✓ aktiv</td><td>eigene <code>heli-glossar-term</code>-Klasse + lokale JS-Implementierung (Vorbild für das Pattern)</td></tr>
<tr><td>Klima 2D/3D</td><td>✓ aktiv</td><td>eigene Klima-Variante</td></tr>
<tr><td>Weltküche</td><td>✓ seit 2026-06-08</td><td><b>Plattform-Standard via <code>/assets/js/glossar-tooltip.js</code></b></td></tr>
<tr><td>Andere Sims</td><td>⏳ ausstehend</td><td>Migration auf Plattform-Lib empfohlen (siehe „Migrations-Plan" unten)</td></tr>
</tbody>
</table>
<div class="ok-box">
<b>Plattform-Lib:</b> <code>App/assets/js/glossar-tooltip.js</code> (Stand 2026-06-08).
Pure Vanilla-JS, kein Framework, mobile-/iPad-tauglich. CSS wird automatisch injiziert.
</div>
<h3>Migrations-Plan für andere Sims</h3>
<ol style="margin:6px 0 10px 24px">
<li>Im jeweiligen Wrapper (<code>App/pages/&lt;sim&gt;.php</code>) das Script einbinden (Snippet oben).</li>
<li>Eigene Glossar-CSS-Klassen (<code>.heli-glossar-term</code>, <code>.klima-glossar-term</code> usw.) durch <code>ggs-g</code> ersetzen.</li>
<li>Eigene JS-Click-Handler entfernen — die Plattform-Lib übernimmt.</li>
<li>Optional: lokale Fallback-Snippets via <code>GlossarTooltip.addFallback()</code> nachreichen.</li>
</ol>
<div class="warn-box">
<b>Wichtig:</b> Beim Migrieren NICHTS löschen, bevor das neue Pattern getestet ist. Erst neuen Code dazu, beide laufen lassen, dann altes raus.
</div>
<!-- ============================ -->
<h2>🎵 Audio-Signal „Atlas fertig"</h2>
<p>Atlas spielt das MP3 <code>App/sims/heli/sounds/dev/atlas-fertig.mp3</code> ab — IMMER wenn der Ball bei Thomas liegt (Aufgabe fertig ODER warten auf Genehmigung). Details: <code>memory/feedback_atlas_fertig_audio.md</code>.</p>
<!-- ============================ -->
<h2>📦 Sim-Wrapper-Standard</h2>
<p>Jede Sim hat einen <code>App/pages/&lt;slug&gt;.php</code>-Wrapper:</p>
<ul style="margin:6px 0 10px 24px">
<li>Lädt Plattform-Session-Kontext (<code>WK_SESSION_ID</code>, <code>WK_STUDENT_NAME</code>, <code>WK_CLASS_ID</code>)</li>
<li>Injiziert <code>BASE_PATH</code> als <code>window.&lt;SIM&gt;_BASE</code> + <code>window.&lt;SIM&gt;_API_BASE</code></li>
<li>Hängt <code>live-client.js</code> + <code>glossar-tooltip.js</code> vor <code>&lt;/body&gt;</code></li>
<li>Setzt <code>Cache-Control: no-store</code> (game.html-Updates wirken sofort)</li>
<li>Biegt Asset-Pfade um: <code>preg_replace</code> auf <code>href="assets/..."</code> und <code>src="assets/..."</code></li>
</ul>
<!-- ============================ -->
<h2>🌍 GGS_LIVE_STATE-Hook</h2>
<p>Jede Sim definiert <code>window.GGS_LIVE_STATE = function(){…}</code> → liefert State-Snapshot für Lehrer-Live-Cockpit. Mindestens:</p>
<pre><code>{
phase: 'browsing' | 'recipe' | 'researching' | …,
score: 0,
completed: 0, // erledigte Aufgaben/Gerichte
completedTotal: 30, // max
badgesEarned: 0,
badgesTotal: 10,
levelWon: false
}</code></pre>
<p>Der Live-Heartbeat schickt das alle 4 s an <code>/api/live.php</code>.</p>
<footer style="margin:32px 0 24px;text-align:center;font-size:.75rem;color:#8a8a8a">
Liegt unter <code>.dontDeploy/entwurfswerkzeug/standards.html</code> · wird nicht deployed.
</footer>
</body>
</html>
@@ -0,0 +1,297 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Lehrkräfte-Dashboard</title>
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="stylesheet" href="assets/fonts/inter.css">
<style>
*, *::before, *::after { margin:0; padding:0; box-sizing:border-box; }
:root {
--bg: #f7f6f3; --white: #fff; --text: #1a1a1a; --text2: #4a4a4a; --text3: #8a8a8a;
--fjord: #4a7c8a; --fjord-l: #dae8ec; --fjord-d: #3a6470;
--moss: #5a8a5e; --moss-l: #dceadd;
--sand: #c4a35a; --sand-l: #f2eacc;
--coral: #c07a6b; --coral-l: #f2ddd8;
}
body { font-family:'Inter',system-ui,sans-serif; background:var(--bg); color:var(--text); line-height:1.55; }
.nav { padding:.7rem 2rem; display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid rgba(0,0,0,.04); background:var(--white); }
.nav-brand { display:flex; align-items:center; gap:.5rem; font-weight:800; font-size:1rem; color:var(--fjord); text-decoration:none; }
.nav-brand img { width:28px; height:28px; border-radius:6px; }
.nav-user { display:flex; align-items:center; gap:.6rem; font-size:.85rem; }
.avatar { width:32px; height:32px; border-radius:50%; background:var(--fjord); color:#fff; display:flex; align-items:center; justify-content:center; font-weight:700; font-size:.78rem; }
.layout { display:grid; grid-template-columns:240px 1fr; min-height:calc(100vh - 60px); }
.sidebar { background:var(--white); border-right:1px solid rgba(0,0,0,.05); padding:1.2rem 1rem; }
.sidebar h4 { font-size:.65rem; font-weight:700; color:var(--text3); text-transform:uppercase; letter-spacing:.08em; margin:1rem 0 .4rem; }
.sidebar h4:first-child { margin-top:0; }
.sidebar a { display:block; padding:.4rem .7rem; color:var(--text2); text-decoration:none; border-radius:8px; font-size:.82rem; font-weight:500; margin-bottom:1px; transition:all .2s; }
.sidebar a:hover, .sidebar a.active { background:var(--fjord-l); color:var(--fjord); }
.main { padding:1.5rem 2rem; }
.page-header { display:flex; justify-content:space-between; align-items:center; margin-bottom:1.2rem; }
.page-header h1 { font-size:1.6rem; font-weight:800; letter-spacing:-.02em; }
.page-header p { color:var(--text3); font-size:.85rem; }
.btn { display:inline-flex; align-items:center; gap:.4rem; padding:.5rem 1.1rem; border-radius:10px; font-weight:600; font-size:.82rem; border:none; cursor:pointer; transition:all .2s; text-decoration:none; }
.btn-p { background:var(--fjord); color:#fff; }
.btn-p:hover { background:var(--fjord-d); }
.btn-o { background:var(--white); color:var(--fjord); border:1.5px solid rgba(74,124,138,.3); }
.grid { display:grid; gap:1rem; }
.grid-3 { grid-template-columns:repeat(3,1fr); }
.grid-2 { grid-template-columns:1fr 1fr; }
.grid-4 { grid-template-columns:repeat(4,1fr); }
.card { background:var(--white); border-radius:14px; border:1px solid rgba(0,0,0,.04); padding:1.1rem; box-shadow:0 2px 10px rgba(0,0,0,.03); }
.card h3 { font-size:.78rem; font-weight:700; color:var(--text3); text-transform:uppercase; letter-spacing:.06em; margin-bottom:.6rem; }
.card-big-num { font-size:2rem; font-weight:900; letter-spacing:-.03em; }
.card-trend { font-size:.75rem; color:var(--moss); font-weight:600; margin-top:.2rem; }
/* Class table */
.classes-table { width:100%; border-collapse:collapse; font-size:.85rem; }
.classes-table th, .classes-table td { padding:.6rem .8rem; text-align:left; border-bottom:1px solid rgba(0,0,0,.05); }
.classes-table th { font-size:.7rem; font-weight:700; color:var(--text3); text-transform:uppercase; letter-spacing:.06em; }
.classes-table tr:hover { background:#fafaf8; }
.student-row { display:flex; align-items:center; gap:.5rem; }
.progress-bar { width:80px; height:6px; background:#e8e5dc; border-radius:3px; overflow:hidden; }
.progress-fill { height:100%; border-radius:3px; }
.badge { display:inline-block; padding:2px 9px; border-radius:6px; font-size:.68rem; font-weight:600; }
.b-on { background:var(--moss-l); color:var(--moss); }
.b-off { background:#f0eeea; color:var(--text3); }
.b-progress { background:var(--fjord-l); color:var(--fjord); }
.b-warn { background:var(--sand-l); color:var(--sand); }
/* Sim card with toggle */
.sim-toggle-card { display:flex; align-items:center; gap:.8rem; padding:.8rem; border-radius:10px; background:#fafaf8; border:1px solid rgba(0,0,0,.04); margin-bottom:.5rem; }
.sim-toggle-card-icon { font-size:1.5rem; }
.sim-toggle-card-info { flex:1; }
.sim-toggle-card-info h5 { font-size:.85rem; font-weight:700; }
.sim-toggle-card-info p { font-size:.72rem; color:var(--text3); }
.toggle { width:36px; height:20px; background:#d4d4d0; border-radius:10px; position:relative; cursor:pointer; transition:all .2s; }
.toggle.on { background:var(--moss); }
.toggle::after { content:''; position:absolute; top:2px; left:2px; width:16px; height:16px; background:#fff; border-radius:50%; transition:all .2s; box-shadow:0 1px 3px rgba(0,0,0,.2); }
.toggle.on::after { left:18px; }
.alert { background:var(--coral-l); border-left:3px solid var(--coral); padding:.6rem .8rem; border-radius:8px; font-size:.78rem; margin-bottom:.5rem; }
.alert strong { color:var(--coral); }
.demo-banner { background:var(--sand-l); color:var(--sand); padding:.5rem 1rem; text-align:center; font-size:.78rem; font-weight:600; }
</style>
</head>
<body>
<div class="demo-banner">⚠️ Demo-Mockup — alle Daten sind erfunden, kein Backend angeschlossen</div>
<nav class="nav">
<a href="index.html" class="nav-brand"><img src="assets/img/logo.png" alt="">GeoGraSim · Dashboard</a>
<div class="nav-user">
<span style="color:var(--text2)">Mag. Schroffenegger</span>
<div class="avatar">TS</div>
</div>
</nav>
<div class="layout">
<aside class="sidebar">
<h4>Übersicht</h4>
<a href="#" class="active">📊 Dashboard</a>
<a href="#">👥 Klassen</a>
<a href="#">🎯 Simulationen</a>
<a href="#">📈 Berichte</a>
<h4>Meine Klassen</h4>
<a href="#">1A — 24 Schüler*innen</a>
<a href="#">1B — 22 Schüler*innen</a>
<a href="#">2A — 25 Schüler*innen</a>
<a href="#">3B — 23 Schüler*innen</a>
<h4>Tools</h4>
<a href="#">🗳️ Live-Abstimmung</a>
<a href="#">📥 Export (PDF)</a>
<a href="#">⚙️ Einstellungen</a>
</aside>
<main class="main">
<div class="page-header">
<div>
<h1>Klasse 1A — Übersicht</h1>
<p>24 Schüler*innen · Letzte Aktivität: vor 2 Stunden</p>
</div>
<div style="display:flex;gap:.5rem">
<button class="btn btn-o">📥 Bericht exportieren</button>
<button class="btn btn-p">🚀 Simulation starten</button>
</div>
</div>
<!-- Top stats row -->
<div class="grid grid-4" style="margin-bottom:1rem">
<div class="card">
<h3>Aktive Schüler*innen</h3>
<div class="card-big-num" style="color:var(--fjord)">18 / 24</div>
<div class="card-trend">↗ +3 seit gestern</div>
</div>
<div class="card">
<h3>Abgeschlossene Simulationen</h3>
<div class="card-big-num" style="color:var(--moss)">142</div>
<div class="card-trend">↗ 87% Erfolgsquote</div>
</div>
<div class="card">
<h3>Durchschn. Lernzeit</h3>
<div class="card-big-num" style="color:var(--sand)">23 min</div>
<div class="card-trend" style="color:var(--text3)">pro Simulation</div>
</div>
<div class="card">
<h3>Identifizierte Fehlkonzepte</h3>
<div class="card-big-num" style="color:var(--coral)">5</div>
<div class="card-trend" style="color:var(--coral)">⚠ Aufmerksamkeit nötig</div>
</div>
</div>
<!-- Two column section -->
<div class="grid grid-2" style="margin-bottom:1rem">
<!-- Active simulation -->
<div class="card">
<h3>🔴 Live: Treibhauseffekt-Simulator</h3>
<p style="font-size:.85rem;margin:.5rem 0 .8rem;color:var(--text2)">12 von 24 Schüler*innen bearbeiten gerade die Simulation.</p>
<table class="classes-table">
<thead>
<tr><th>Schüler*in</th><th>Phase</th><th>Fortschritt</th><th>Zeit</th></tr>
</thead>
<tbody>
<tr>
<td><div class="student-row">👤 Anna B.</div></td>
<td><span class="badge b-progress">Observe</span></td>
<td><div class="progress-bar"><div class="progress-fill" style="width:75%;background:var(--fjord)"></div></div></td>
<td>14:32</td>
</tr>
<tr>
<td><div class="student-row">👤 Lukas F.</div></td>
<td><span class="badge b-on">Reflect</span></td>
<td><div class="progress-bar"><div class="progress-fill" style="width:90%;background:var(--moss)"></div></div></td>
<td>18:45</td>
</tr>
<tr>
<td><div class="student-row">👤 Sara M.</div></td>
<td><span class="badge b-progress">Predict</span></td>
<td><div class="progress-bar"><div class="progress-fill" style="width:30%;background:var(--fjord)"></div></div></td>
<td>05:12</td>
</tr>
<tr>
<td><div class="student-row">👤 Tim H.</div></td>
<td><span class="badge b-warn">Pausiert</span></td>
<td><div class="progress-bar"><div class="progress-fill" style="width:55%;background:var(--sand)"></div></div></td>
<td>09:20</td>
</tr>
<tr>
<td><div class="student-row">👤 Emma K.</div></td>
<td><span class="badge b-on">Reflect</span></td>
<td><div class="progress-bar"><div class="progress-fill" style="width:95%;background:var(--moss)"></div></div></td>
<td>21:08</td>
</tr>
</tbody>
</table>
</div>
<!-- Insights -->
<div class="card">
<h3>💡 Erkannte Fehlkonzepte</h3>
<div class="alert">
<strong>Ozonloch ≠ Treibhauseffekt</strong><br>
7 Schüler*innen haben in ihrer Vorhersage erwähnt, dass das "Ozonloch" für die Erwärmung verantwortlich sei.
→ Empfehlung: Im Plenum klarstellen.
</div>
<div class="alert">
<strong>Lineare statt logarithmische Beziehung</strong><br>
5 Schüler*innen haben "doppelt so viel CO₂ = doppelt so warm" angenommen.
→ Empfehlung: Klimasensitivität gemeinsam besprechen.
</div>
<div class="alert">
<strong>Albedo nicht verstanden</strong><br>
3 Schüler*innen haben Albedo nicht verändert. → Optionale Vertiefung anbieten.
</div>
<h3 style="margin-top:1rem">🎯 Lernziele-Erreichung (Klasse)</h3>
<div style="font-size:.78rem;margin-top:.4rem">
<div style="display:flex;justify-content:space-between;margin-bottom:.3rem"><span>Treibhauseffekt verstehen</span><strong>83%</strong></div>
<div class="progress-bar" style="width:100%"><div class="progress-fill" style="width:83%;background:var(--moss)"></div></div>
<div style="display:flex;justify-content:space-between;margin:.6rem 0 .3rem"><span>CO₂-Temperatur-Zusammenhang</span><strong>71%</strong></div>
<div class="progress-bar" style="width:100%"><div class="progress-fill" style="width:71%;background:var(--fjord)"></div></div>
<div style="display:flex;justify-content:space-between;margin:.6rem 0 .3rem"><span>Abgrenzung zum Ozonloch</span><strong>54%</strong></div>
<div class="progress-bar" style="width:100%"><div class="progress-fill" style="width:54%;background:var(--sand)"></div></div>
</div>
</div>
</div>
<!-- Module freischaltung -->
<div class="card" style="margin-bottom:1rem">
<h3>⚙️ Module für Klasse 1A freigeschaltet</h3>
<div class="grid grid-2" style="margin-top:.8rem">
<div class="sim-toggle-card">
<div class="sim-toggle-card-icon">🌡️</div>
<div class="sim-toggle-card-info">
<h5>Treibhauseffekt-Simulator</h5>
<p>1. Klasse · 20 min · Schwierigkeit: Standard</p>
</div>
<div class="toggle on"></div>
</div>
<div class="sim-toggle-card">
<div class="sim-toggle-card-icon">🌋</div>
<div class="sim-toggle-card-info">
<h5>Erdbeben-Simulator</h5>
<p>1. Klasse · 25 min · Schwierigkeit: Standard</p>
</div>
<div class="toggle on"></div>
</div>
<div class="sim-toggle-card">
<div class="sim-toggle-card-icon"></div>
<div class="sim-toggle-card-info">
<h5>Energiemix-Simulator</h5>
<p>2. Klasse · 25 min · Erst nächstes Jahr</p>
</div>
<div class="toggle"></div>
</div>
<div class="sim-toggle-card">
<div class="sim-toggle-card-icon">🏗️</div>
<div class="sim-toggle-card-info">
<h5>Raumplanung Gemeinde</h5>
<p>3. Klasse · 45 min · Erst in 2 Jahren</p>
</div>
<div class="toggle"></div>
</div>
</div>
</div>
<!-- Live abstimmung -->
<div class="card">
<h3>🗳️ Live-Abstimmung starten</h3>
<p style="font-size:.85rem;color:var(--text2);margin:.4rem 0 .8rem">Stelle deiner Klasse eine Frage. Das Ergebnis fließt direkt in die laufende Simulation ein.</p>
<div style="display:flex;gap:.5rem">
<input type="text" placeholder="z.B.: Sollten wir die CO₂-Werte von 2050 simulieren?" style="flex:1;padding:.6rem 1rem;border:1px solid rgba(0,0,0,.1);border-radius:10px;font-family:inherit;font-size:.85rem">
<button class="btn btn-p">Abstimmung starten</button>
</div>
</div>
</main>
</div>
<script>
// Toggle functionality
document.querySelectorAll('.toggle').forEach(t => {
t.addEventListener('click', () => t.classList.toggle('on'))
})
</script>
</body>
</html>
@@ -0,0 +1,239 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Energiewende-Planer*in</title>
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="stylesheet" href="assets/fonts/inter.css">
<style>
*, *::before, *::after { margin:0; padding:0; box-sizing:border-box; }
:root {
--bg: #f7f6f3; --bg2: #efeee9; --white: #fff;
--text: #1a1a1a; --text2: #4a4a4a; --text3: #8a8a8a;
--fjord: #4a7c8a; --fjord-l: #dae8ec; --fjord-d: #3a6470;
--moss: #5a8a5e; --moss-l: #dceadd;
--sand: #c4a35a; --sand-l: #f2eacc;
--coral: #c07a6b; --coral-l: #f2ddd8;
--danger: #b04a3a;
}
body { font-family:'Inter',system-ui,sans-serif; background:var(--bg); color:var(--text); line-height:1.5; }
.nav { padding:.6rem 1.2rem; display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid rgba(0,0,0,.05); background:var(--white); }
.nav-brand { display:flex; align-items:center; gap:.5rem; font-weight:800; font-size:1rem; color:var(--fjord); text-decoration:none; }
.nav-brand img { width:28px; height:28px; border-radius:6px; }
.nav-actions { display:flex; gap:.4rem; }
.nav-btn { padding:.4rem .8rem; border:1px solid rgba(0,0,0,.08); border-radius:8px; background:var(--white); color:var(--text2); font-size:.78rem; font-weight:600; cursor:pointer; text-decoration:none; }
.nav-btn:hover { background:var(--fjord-l); border-color:var(--fjord); color:var(--fjord); }
.game-layout { display:grid; grid-template-columns:280px 1fr 300px; gap:.8rem; padding:.8rem; max-width:1400px; margin:0 auto; align-items:start; }
.panel { background:var(--white); border-radius:12px; border:1px solid rgba(0,0,0,.05); padding:.9rem; box-shadow:0 2px 8px rgba(0,0,0,.03); }
.panel-h { font-size:.66rem; font-weight:700; color:var(--fjord); text-transform:uppercase; letter-spacing:.06em; margin-bottom:.5rem; }
.left { display:flex; flex-direction:column; gap:.8rem; }
.resource { display:flex; align-items:center; gap:.6rem; padding:.4rem .2rem; border-bottom:1px solid rgba(0,0,0,.04); }
.resource:last-child { border-bottom:none; }
.resource-icon { font-size:1.4rem; width:28px; text-align:center; }
.resource-info { flex:1; min-width:0; }
.resource-name { font-size:.7rem; color:var(--text3); font-weight:500; }
.resource-val { font-size:.95rem; font-weight:700; color:var(--text); }
.goals .goal { padding:.4rem 0; border-bottom:1px solid rgba(0,0,0,.04); }
.goals .goal:last-child { border:0; }
.goal-row { display:flex; align-items:center; gap:.4rem; font-size:.78rem; }
.goal-check { width:14px; height:14px; border:1.5px solid var(--text3); border-radius:3px; display:flex; align-items:center; justify-content:center; font-size:.6rem; flex-shrink:0; }
.goal-check.done { background:var(--moss); border-color:var(--moss); color:#fff; }
.goal-bar { height:3px; background:#e8e5dc; border-radius:2px; margin-top:.3rem; overflow:hidden; }
.goal-bar-fill { height:100%; background:var(--moss); transition:width .3s; }
.center { display:flex; flex-direction:column; gap:.6rem; }
.canvas-wrap { background:var(--white); border-radius:14px; border:1px solid rgba(0,0,0,.05); overflow:hidden; box-shadow:0 4px 16px rgba(0,0,0,.05); position:relative; min-height:440px; }
.tutorial-overlay { position:absolute; inset:0; background:rgba(15,30,40,.85); backdrop-filter:blur(8px); display:flex; align-items:center; justify-content:center; z-index:10; padding:1.5rem; }
.tutorial-card { background:var(--white); border-radius:16px; padding:1.5rem 1.8rem; max-width:500px; box-shadow:0 20px 60px rgba(0,0,0,.3); }
.tutorial-card h3 { font-size:1.2rem; font-weight:800; margin-bottom:.6rem; color:var(--fjord); }
.tutorial-card p { font-size:.88rem; color:var(--text2); white-space:pre-line; margin-bottom:1rem; }
.tutorial-card .progress-dots { display:flex; gap:.3rem; justify-content:center; margin:.8rem 0; }
.dot { width:8px; height:8px; border-radius:50%; background:#dadad0; }
.dot.active { background:var(--fjord); }
.tut-btn { width:100%; padding:.7rem; background:var(--fjord); color:#fff; border:none; border-radius:10px; font-weight:700; font-size:.9rem; cursor:pointer; }
.tut-btn:hover { background:var(--fjord-d); }
.speed-bar { display:flex; gap:.3rem; padding:.5rem; background:var(--white); border-radius:10px; border:1px solid rgba(0,0,0,.05); }
.speed-btn { flex:1; padding:.5rem; border:none; background:transparent; color:var(--text2); font-size:.8rem; font-weight:600; cursor:pointer; border-radius:6px; }
.speed-btn:hover { background:#f0eeea; }
.speed-btn.active { background:var(--fjord); color:#fff; }
.graphs { display:grid; grid-template-columns:1fr 1fr; gap:.6rem; }
.graph-card { background:var(--white); border-radius:10px; border:1px solid rgba(0,0,0,.05); padding:.6rem; }
.graph-title { font-size:.65rem; color:var(--text3); font-weight:600; text-transform:uppercase; letter-spacing:.06em; margin-bottom:.2rem; }
.graph-svg { width:100%; height:50px; }
.right { display:flex; flex-direction:column; gap:.8rem; }
.measure-card { display:flex; gap:.6rem; padding:.6rem; border-radius:10px; border:1px solid rgba(0,0,0,.06); margin-bottom:.4rem; cursor:pointer; transition:all .2s; background:var(--white); }
.measure-card:hover { border-color:var(--fjord); background:var(--fjord-l); }
.measure-card.disabled { opacity:.5; cursor:not-allowed; }
.measure-card.disabled:hover { border-color:rgba(0,0,0,.06); background:var(--white); }
.measure-icon { font-size:1.5rem; width:32px; text-align:center; }
.measure-info { flex:1; min-width:0; }
.measure-name { font-size:.8rem; font-weight:700; }
.measure-desc { font-size:.66rem; color:var(--text3); line-height:1.3; }
.measure-stats { display:flex; gap:.3rem; flex-wrap:wrap; margin-top:.2rem; }
.measure-stat { font-size:.6rem; padding:1px 5px; border-radius:4px; background:#f0eeea; color:var(--text3); font-weight:600; }
.measure-cost { background:var(--sand-l); color:var(--sand); }
.measure-reduction { background:var(--moss-l); color:var(--moss); }
.measure-protection { background:var(--fjord-l); color:var(--fjord); }
.measure-capacity { background:#e8e3f2; color:#7a5ea8; }
.measure-quality { background:var(--coral-l); color:var(--danger); }
.measure-neutral { background:#f0eeea; color:var(--text3); }
.events-list { max-height:180px; overflow-y:auto; }
.event { padding:.4rem .5rem; border-radius:8px; margin-bottom:.3rem; font-size:.74rem; line-height:1.3; }
.event-info { background:#f0eeea; color:var(--text2); }
.event-success { background:var(--moss-l); color:var(--moss); }
.event-warning { background:var(--sand-l); color:var(--sand); }
.event-danger { background:var(--coral-l); color:var(--danger); }
.end-screen { position:fixed; inset:0; background:rgba(15,30,40,.9); backdrop-filter:blur(10px); display:flex; align-items:center; justify-content:center; z-index:100; padding:2rem; }
.end-card { background:var(--white); border-radius:20px; padding:2rem 2.5rem; max-width:500px; text-align:center; box-shadow:0 30px 80px rgba(0,0,0,.4); }
.end-icon { font-size:4rem; margin-bottom:.5rem; }
.end-card h2 { font-size:1.8rem; font-weight:900; letter-spacing:-.02em; margin-bottom:.3rem; }
.end-card .end-summary { color:var(--text2); margin-bottom:1rem; }
.end-stats { display:grid; grid-template-columns:1fr 1fr; gap:.8rem; margin:1rem 0; }
.end-stat { background:#f7f6f3; border-radius:10px; padding:.8rem; }
.end-stat-val { font-size:1.4rem; font-weight:800; color:var(--fjord); }
.end-stat-lbl { font-size:.7rem; color:var(--text3); text-transform:uppercase; }
@media (max-width: 1100px) {
.game-layout { grid-template-columns:1fr; }
.left, .right { order:2; }
}
</style>
</head>
<body>
<nav class="nav">
<a href="index.html" class="nav-brand"><img src="assets/img/logo.png" alt="">GeoGraSim · Energiewende</a>
<div class="nav-actions">
<button class="nav-btn" id="btn-save">💾 Speichern</button>
<button class="nav-btn" id="btn-load">📂 Laden</button>
<button class="nav-btn" id="btn-finish">⏹ Beenden</button>
<button class="nav-btn" id="btn-reset" title="Stand löschen und neu starten">🔄 Reset</button>
<a href="index.html" class="nav-btn">← Zurück</a>
</div>
</nav>
<div class="game-layout">
<aside class="left">
<div class="panel">
<div class="panel-h">📊 Status</div>
<div id="resources"></div>
</div>
<div class="panel goals">
<div class="panel-h">🎯 Ziele</div>
<div id="goals"></div>
</div>
</aside>
<main class="center">
<div class="canvas-wrap" id="canvas-wrap">
<div class="tutorial-overlay" id="tutorial">
<div class="tutorial-card">
<h3 id="tut-title"></h3>
<p id="tut-text"></p>
<div class="progress-dots" id="tut-dots"></div>
<button class="tut-btn" id="tut-next">Weiter →</button>
</div>
</div>
</div>
<div class="speed-bar">
<button class="speed-btn" data-speed="0"></button>
<button class="speed-btn active" data-speed="1">▶ Normal</button>
<button class="speed-btn" data-speed="2">⏩ Schnell</button>
<button class="speed-btn" data-speed="4">⏭ Sehr schnell</button>
</div>
<div class="graphs">
<div class="graph-card">
<div class="graph-title">🌱 Erneuerbar-Anteil</div>
<svg class="graph-svg" id="graph-renewable"></svg>
</div>
<div class="graph-card">
<div class="graph-title">🌫 CO₂-Ausstoß</div>
<svg class="graph-svg" id="graph-co2"></svg>
</div>
<div class="graph-card">
<div class="graph-title">⚡ Erzeugung vs. Bedarf</div>
<svg class="graph-svg" id="graph-supply"></svg>
</div>
<div class="graph-card">
<div class="graph-title">💰 Budget</div>
<svg class="graph-svg" id="graph-budget"></svg>
</div>
</div>
</main>
<aside class="right">
<div class="panel">
<div class="panel-h">⚡ Kraftwerke bauen</div>
<div id="measures"></div>
</div>
<div class="panel">
<div class="panel-h">📰 Ereignisse</div>
<div class="events-list" id="events"></div>
</div>
</aside>
</div>
<div id="end-screen-host"></div>
<script type="module">
import { EnergiemixGame, PLANT_TYPES } from './src/sims/sim-08-energiemix/game.ts'
import { EnergiemixRenderer } from './src/sims/sim-08-energiemix/game-renderer.ts'
import { GameUI } from './src/ui/game-ui.ts'
const game = new EnergiemixGame()
const shopItems = PLANT_TYPES.map(p => {
const badges = []
if (p.capacityMW > 0) {
badges.push({ label: `${p.capacityMW} MW`, type: 'capacity' })
badges.push({ label: `CF ${(p.capacityFactor * 100).toFixed(0)}%`, type: 'neutral' })
}
if (p.renewable) badges.push({ label: '🌱 erneuerbar', type: 'reduction' })
if (p.co2PerKWh > 300) badges.push({ label: `${p.co2PerKWh} g CO₂/kWh`, type: 'quality' })
else if (p.co2PerKWh > 0 && p.capacityMW > 0) badges.push({ label: `${p.co2PerKWh} g CO₂/kWh`, type: 'neutral' })
if (p.flexible && p.capacityMW > 0) badges.push({ label: '🔄 flexibel', type: 'protection' })
if (p.storage) badges.push({ label: `🔋 ${p.storage} MW Speicher`, type: 'protection' })
return {
id: p.id,
name: p.name,
emoji: p.emoji,
description: p.description,
cost: p.cost,
upkeep: p.upkeep,
badges,
}
})
new GameUI({
game,
Renderer: EnergiemixRenderer,
shopItems,
onBuy: (id) => game.buyPlant(id),
graphs: [
{ id: 'graph-renewable', title: 'Erneuerbar', field: 'renewable', yMin: 0, yMax: 100, color: '#5a8a5e', format: v => `${v.toFixed(0)}%` },
{ id: 'graph-co2', title: 'CO₂', field: 'co2', yMin: 0, yMax: 5000, color: '#8a7868' },
{ id: 'graph-supply', title: 'Erzeugung', field: 'supply', yMin: 0, yMax: 3000, color: '#4a7c8a' },
{ id: 'graph-budget', title: 'Budget', field: 'budget', yMin: -300, yMax: 2000, color: '#c4a35a' },
],
saveKey: 'energiemix-save',
yearOffset: 2025,
})
</script>
</body>
</html>
@@ -0,0 +1,230 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Stadtplaner*in in Erdbebenregion</title>
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="stylesheet" href="assets/fonts/inter.css">
<style>
*, *::before, *::after { margin:0; padding:0; box-sizing:border-box; }
:root {
--bg: #f7f6f3; --bg2: #efeee9; --white: #fff;
--text: #1a1a1a; --text2: #4a4a4a; --text3: #8a8a8a;
--fjord: #4a7c8a; --fjord-l: #dae8ec; --fjord-d: #3a6470;
--moss: #5a8a5e; --moss-l: #dceadd;
--sand: #c4a35a; --sand-l: #f2eacc;
--coral: #c07a6b; --coral-l: #f2ddd8;
--danger: #b04a3a;
}
body { font-family:'Inter',system-ui,sans-serif; background:var(--bg); color:var(--text); line-height:1.5; }
.nav { padding:.6rem 1.2rem; display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid rgba(0,0,0,.05); background:var(--white); }
.nav-brand { display:flex; align-items:center; gap:.5rem; font-weight:800; font-size:1rem; color:var(--fjord); text-decoration:none; }
.nav-brand img { width:28px; height:28px; border-radius:6px; }
.nav-actions { display:flex; gap:.4rem; }
.nav-btn { padding:.4rem .8rem; border:1px solid rgba(0,0,0,.08); border-radius:8px; background:var(--white); color:var(--text2); font-size:.78rem; font-weight:600; cursor:pointer; text-decoration:none; }
.nav-btn:hover { background:var(--fjord-l); border-color:var(--fjord); color:var(--fjord); }
.game-layout { display:grid; grid-template-columns:280px 1fr 280px; gap:.8rem; padding:.8rem; max-width:1400px; margin:0 auto; align-items:start; }
.panel { background:var(--white); border-radius:12px; border:1px solid rgba(0,0,0,.05); padding:.9rem; box-shadow:0 2px 8px rgba(0,0,0,.03); }
.panel-h { font-size:.66rem; font-weight:700; color:var(--fjord); text-transform:uppercase; letter-spacing:.06em; margin-bottom:.5rem; }
.left { display:flex; flex-direction:column; gap:.8rem; }
.resource { display:flex; align-items:center; gap:.6rem; padding:.4rem .2rem; border-bottom:1px solid rgba(0,0,0,.04); }
.resource:last-child { border-bottom:none; }
.resource-icon { font-size:1.4rem; width:28px; text-align:center; }
.resource-info { flex:1; min-width:0; }
.resource-name { font-size:.7rem; color:var(--text3); font-weight:500; }
.resource-val { font-size:.95rem; font-weight:700; color:var(--text); }
.goals .goal { padding:.4rem 0; border-bottom:1px solid rgba(0,0,0,.04); }
.goals .goal:last-child { border:0; }
.goal-row { display:flex; align-items:center; gap:.4rem; font-size:.78rem; }
.goal-check { width:14px; height:14px; border:1.5px solid var(--text3); border-radius:3px; display:flex; align-items:center; justify-content:center; font-size:.6rem; flex-shrink:0; }
.goal-check.done { background:var(--moss); border-color:var(--moss); color:#fff; }
.goal-bar { height:3px; background:#e8e5dc; border-radius:2px; margin-top:.3rem; overflow:hidden; }
.goal-bar-fill { height:100%; background:var(--moss); transition:width .3s; }
.center { display:flex; flex-direction:column; gap:.6rem; }
.canvas-wrap { background:var(--white); border-radius:14px; border:1px solid rgba(0,0,0,.05); overflow:hidden; box-shadow:0 4px 16px rgba(0,0,0,.05); position:relative; min-height:440px; }
.tutorial-overlay { position:absolute; inset:0; background:rgba(15,30,40,.85); backdrop-filter:blur(8px); display:flex; align-items:center; justify-content:center; z-index:10; padding:1.5rem; }
.tutorial-card { background:var(--white); border-radius:16px; padding:1.5rem 1.8rem; max-width:480px; box-shadow:0 20px 60px rgba(0,0,0,.3); }
.tutorial-card h3 { font-size:1.2rem; font-weight:800; margin-bottom:.6rem; color:var(--fjord); }
.tutorial-card p { font-size:.88rem; color:var(--text2); white-space:pre-line; margin-bottom:1rem; }
.tutorial-card .progress-dots { display:flex; gap:.3rem; justify-content:center; margin:.8rem 0; }
.dot { width:8px; height:8px; border-radius:50%; background:#dadad0; }
.dot.active { background:var(--fjord); }
.tut-btn { width:100%; padding:.7rem; background:var(--fjord); color:#fff; border:none; border-radius:10px; font-weight:700; font-size:.9rem; cursor:pointer; }
.tut-btn:hover { background:var(--fjord-d); }
.speed-bar { display:flex; gap:.3rem; padding:.5rem; background:var(--white); border-radius:10px; border:1px solid rgba(0,0,0,.05); }
.speed-btn { flex:1; padding:.5rem; border:none; background:transparent; color:var(--text2); font-size:.8rem; font-weight:600; cursor:pointer; border-radius:6px; }
.speed-btn:hover { background:#f0eeea; }
.speed-btn.active { background:var(--fjord); color:#fff; }
.graphs { display:grid; grid-template-columns:1fr 1fr; gap:.6rem; }
.graph-card { background:var(--white); border-radius:10px; border:1px solid rgba(0,0,0,.05); padding:.6rem; }
.graph-title { font-size:.65rem; color:var(--text3); font-weight:600; text-transform:uppercase; letter-spacing:.06em; margin-bottom:.2rem; }
.graph-svg { width:100%; height:50px; }
.right { display:flex; flex-direction:column; gap:.8rem; }
.measure-card { display:flex; gap:.6rem; padding:.6rem; border-radius:10px; border:1px solid rgba(0,0,0,.06); margin-bottom:.4rem; cursor:pointer; transition:all .2s; background:var(--white); }
.measure-card:hover { border-color:var(--fjord); background:var(--fjord-l); }
.measure-card.disabled { opacity:.5; cursor:not-allowed; }
.measure-card.disabled:hover { border-color:rgba(0,0,0,.06); background:var(--white); }
.measure-icon { font-size:1.5rem; width:32px; text-align:center; }
.measure-info { flex:1; min-width:0; }
.measure-name { font-size:.8rem; font-weight:700; }
.measure-desc { font-size:.66rem; color:var(--text3); line-height:1.3; }
.measure-stats { display:flex; gap:.3rem; flex-wrap:wrap; margin-top:.2rem; }
.measure-stat { font-size:.6rem; padding:1px 5px; border-radius:4px; background:#f0eeea; color:var(--text3); font-weight:600; }
.measure-cost { background:var(--sand-l); color:var(--sand); }
.measure-reduction { background:var(--moss-l); color:var(--moss); }
.measure-protection { background:var(--fjord-l); color:var(--fjord); }
.measure-capacity { background:#e8e3f2; color:#7a5ea8; }
.measure-quality { background:var(--coral-l); color:var(--danger); }
.measure-neutral { background:#f0eeea; color:var(--text3); }
.events-list { max-height:180px; overflow-y:auto; }
.event { padding:.4rem .5rem; border-radius:8px; margin-bottom:.3rem; font-size:.74rem; line-height:1.3; }
.event-info { background:#f0eeea; color:var(--text2); }
.event-success { background:var(--moss-l); color:var(--moss); }
.event-warning { background:var(--sand-l); color:var(--sand); }
.event-danger { background:var(--coral-l); color:var(--danger); }
.end-screen { position:fixed; inset:0; background:rgba(15,30,40,.9); backdrop-filter:blur(10px); display:flex; align-items:center; justify-content:center; z-index:100; padding:2rem; }
.end-card { background:var(--white); border-radius:20px; padding:2rem 2.5rem; max-width:500px; text-align:center; box-shadow:0 30px 80px rgba(0,0,0,.4); }
.end-icon { font-size:4rem; margin-bottom:.5rem; }
.end-card h2 { font-size:1.8rem; font-weight:900; letter-spacing:-.02em; margin-bottom:.3rem; }
.end-card .end-summary { color:var(--text2); margin-bottom:1rem; }
.end-stats { display:grid; grid-template-columns:1fr 1fr; gap:.8rem; margin:1rem 0; }
.end-stat { background:#f7f6f3; border-radius:10px; padding:.8rem; }
.end-stat-val { font-size:1.4rem; font-weight:800; color:var(--fjord); }
.end-stat-lbl { font-size:.7rem; color:var(--text3); text-transform:uppercase; }
@media (max-width: 1100px) {
.game-layout { grid-template-columns:1fr; }
.left, .right { order:2; }
}
</style>
</head>
<body>
<nav class="nav">
<a href="index.html" class="nav-brand"><img src="assets/img/logo.png" alt="">GeoGraSim · Erdbebenregion</a>
<div class="nav-actions">
<button class="nav-btn" id="btn-save">💾 Speichern</button>
<button class="nav-btn" id="btn-load">📂 Laden</button>
<button class="nav-btn" id="btn-finish">⏹ Beenden</button>
<button class="nav-btn" id="btn-reset" title="Stand löschen und neu starten">🔄 Reset</button>
<a href="index.html" class="nav-btn">← Zurück</a>
</div>
</nav>
<div class="game-layout">
<aside class="left">
<div class="panel">
<div class="panel-h">📊 Status</div>
<div id="resources"></div>
</div>
<div class="panel goals">
<div class="panel-h">🎯 Ziele</div>
<div id="goals"></div>
</div>
</aside>
<main class="center">
<div class="canvas-wrap" id="canvas-wrap">
<div class="tutorial-overlay" id="tutorial">
<div class="tutorial-card">
<h3 id="tut-title"></h3>
<p id="tut-text"></p>
<div class="progress-dots" id="tut-dots"></div>
<button class="tut-btn" id="tut-next">Weiter →</button>
</div>
</div>
</div>
<div class="speed-bar">
<button class="speed-btn" data-speed="0"></button>
<button class="speed-btn active" data-speed="1">▶ Normal</button>
<button class="speed-btn" data-speed="2">⏩ Schnell</button>
<button class="speed-btn" data-speed="4">⏭ Sehr schnell</button>
</div>
<div class="graphs">
<div class="graph-card">
<div class="graph-title">👥 Bevölkerung</div>
<svg class="graph-svg" id="graph-pop"></svg>
</div>
<div class="graph-card">
<div class="graph-title">🏠 Wohnraum</div>
<svg class="graph-svg" id="graph-housed"></svg>
</div>
<div class="graph-card">
<div class="graph-title">💰 Budget</div>
<svg class="graph-svg" id="graph-budget"></svg>
</div>
<div class="graph-card">
<div class="graph-title">🕯️ Opfer (kumulativ)</div>
<svg class="graph-svg" id="graph-deaths"></svg>
</div>
</div>
</main>
<aside class="right">
<div class="panel">
<div class="panel-h">🏗 Häuser bauen</div>
<div id="measures"></div>
</div>
<div class="panel">
<div class="panel-h">📰 Ereignisse</div>
<div class="events-list" id="events"></div>
</div>
</aside>
</div>
<div id="end-screen-host"></div>
<script type="module">
import { ErdbebenGame, BUILDING_TYPES } from './src/sims/sim-07-erdbeben/game.ts'
import { ErdbebenRenderer } from './src/sims/sim-07-erdbeben/game-renderer.ts'
import { GameUI } from './src/ui/game-ui.ts'
const game = new ErdbebenGame()
const shopItems = BUILDING_TYPES.map(b => ({
id: b.id,
name: b.name,
emoji: b.emoji,
description: b.description,
cost: b.cost,
upkeep: b.upkeep,
badges: [
b.capacity > 0 ? { label: `👥 ${b.capacity} Plätze`, type: 'capacity' } : null,
{ label: `🛡 Q ${(b.quality * 100).toFixed(0)}%`, type: b.quality >= 0.6 ? 'protection' : 'quality' },
].filter(Boolean),
}))
new GameUI({
game,
Renderer: ErdbebenRenderer,
shopItems,
onBuy: (id) => game.buyBuilding(id),
graphs: [
{ id: 'graph-pop', title: 'Bevölkerung', field: 'population', yMin: 0, yMax: 6000, color: '#4a7c8a' },
{ id: 'graph-housed', title: 'Wohnraum', field: 'housed', yMin: 0, yMax: 6000, color: '#5a8a5e' },
{ id: 'graph-budget', title: 'Budget', field: 'budget', yMin: -300, yMax: 1500, color: '#c4a35a' },
{ id: 'graph-deaths', title: 'Opfer', field: 'deaths', yMin: 0, yMax: 500, color: '#b04a3a' },
],
saveKey: 'erdbeben-save',
yearOffset: 1975,
})
</script>
</body>
</html>
@@ -0,0 +1,872 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Flussmanagement</title>
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="stylesheet" href="assets/fonts/inter.css">
<style>
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
:root{--bg:#f7f6f3;--white:#fff;--text:#1a1a1a;--text2:#4a4a4a;--text3:#8a8a8a;--fjord:#4a7c8a;--fjord-l:#dae8ec;--fjord-d:#3a6470;--moss:#5a8a5e;--moss-l:#dceadd;--sand:#c4a35a;--coral:#c07a6b}
html,body{height:100%;overflow:hidden}
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);display:flex;flex-direction:column}
.nav{padding:.4rem .8rem;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid rgba(0,0,0,.05);background:var(--white);flex-shrink:0}
.nav-brand{display:flex;align-items:center;gap:.4rem;font-weight:800;font-size:.9rem;color:var(--fjord);text-decoration:none}
.nav-brand img{width:24px;height:24px;border-radius:5px}
.nav-btn{padding:.25rem .6rem;border:1px solid rgba(0,0,0,.08);border-radius:6px;background:var(--white);color:var(--text2);font-size:.68rem;font-weight:600;cursor:pointer;text-decoration:none}
.nav-btn:hover{background:var(--fjord-l);color:var(--fjord)}
.main{flex:1;display:grid;grid-template-columns:190px 1fr 200px;gap:.4rem;padding:.4rem;min-height:0;overflow:hidden}
.panel{background:var(--white);border-radius:9px;border:1px solid rgba(0,0,0,.04);padding:.5rem;box-shadow:0 1px 4px rgba(0,0,0,.02);overflow-y:auto}
.panel-h{font-size:.58rem;font-weight:700;color:var(--fjord);text-transform:uppercase;letter-spacing:.05em;margin-bottom:.3rem}
.col-left,.col-right{display:flex;flex-direction:column;gap:.4rem;overflow-y:auto}
.col-center{display:flex;flex-direction:column;gap:.3rem;min-height:0}
.topbar{flex-shrink:0;display:flex;align-items:center;gap:.4rem;background:var(--white);border-radius:8px;padding:.3rem .6rem;border:1px solid rgba(0,0,0,.04);flex-wrap:wrap}
.topbar .stat{font-size:.68rem;font-weight:700;display:flex;align-items:center;gap:.2rem}
.topbar .stat .v{font-size:.85rem}
.topbar .gold .v{color:var(--sand)}
.topbar .yr .v{color:var(--fjord)}
.topbar .inc{font-size:.58rem;color:var(--moss);font-weight:600}
.topbar .wx{font-size:.78rem;padding:.1rem .35rem;border-radius:4px;background:var(--bg);font-weight:600}
.spd{display:flex;gap:.1rem;margin-left:auto}
.spd button{width:26px;height:22px;border:1px solid rgba(0,0,0,.07);border-radius:4px;background:var(--white);font-size:.62rem;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center}
.spd button.on{background:var(--fjord);color:#fff;border-color:var(--fjord)}
.canvas-wrap{flex:1;background:#dde6d4;border-radius:9px;overflow:hidden;position:relative;min-height:200px;cursor:crosshair}
.canvas-wrap canvas{width:100%;height:100%;display:block}
.param{display:flex;align-items:center;gap:.3rem;padding:.18rem 0;border-bottom:1px solid rgba(0,0,0,.02)}.param:last-child{border:none}
.param-i{font-size:.75rem;width:17px;text-align:center}.param-inf{flex:1}
.param-n{font-size:.55rem;color:var(--text3)}.param-bar{height:3.5px;background:#e8e5dc;border-radius:2px;margin-top:1px;overflow:hidden}
.param-bar-f{height:100%;border-radius:2px;transition:width .8s}.param-v{font-size:.62rem;font-weight:700;min-width:17px;text-align:right}
.tool{display:flex;align-items:center;gap:.3rem;padding:.3rem .4rem;border:1.5px solid rgba(0,0,0,.05);border-radius:6px;background:var(--white);cursor:pointer;width:100%;text-align:left;margin-bottom:.18rem;transition:all .12s}
.tool:hover{border-color:var(--fjord);background:var(--fjord-l)}.tool.on{border-color:var(--fjord);background:var(--fjord);color:#fff}
.tool.off{opacity:.3;pointer-events:none;filter:grayscale(100%);background:var(--bg)!important;border-color:rgba(0,0,0,.05)!important;color:var(--text3)!important}
.tool-e{font-size:.85rem}.tool-inf{flex:1}.tool-nm{font-size:.62rem;font-weight:600}.tool-ds{font-size:.5rem;color:var(--text3)}.tool-c{font-size:.52rem;font-weight:600;color:var(--sand)}
.log{font-size:.55rem;padding:.12rem 0;border-bottom:1px solid rgba(0,0,0,.02);color:var(--text2)}.log.bad{color:var(--coral)}.log.good{color:var(--moss)}
.ov{position:fixed;inset:0;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;z-index:200;backdrop-filter:blur(3px)}
.ov-c{background:var(--white);border-radius:14px;padding:1.4rem;max-width:400px;width:90%;text-align:center}
.ov-c h2{font-size:1rem;font-weight:800;margin-bottom:.2rem}.ov-c p{font-size:.75rem;color:var(--text2);margin-bottom:.5rem;line-height:1.4}
.ov-btn{padding:.4rem 1rem;border:none;border-radius:6px;background:var(--fjord);color:#fff;font-size:.75rem;font-weight:700;cursor:pointer}
.hidden{display:none!important}
@media(max-width:800px){.main{grid-template-columns:1fr}}
</style>
</head>
<body>
<nav class="nav">
<a href="index.html" class="nav-brand"><img src="assets/img/logo.png" alt="">Flussmanagement</a>
<div style="display:flex;gap:.2rem"><button id="btn-reset" class="nav-btn">🔄 Neu</button><a href="index.html" class="nav-btn">← Zurück</a></div>
</nav>
<div id="game" class="main">
<div class="col-left">
<div class="panel"><div class="panel-h">Zustand</div><div id="params"></div></div>
<div class="panel"><div class="panel-h">Verlauf</div><canvas id="graph" style="width:100%;height:90px;display:block;border-radius:6px;background:#f4f2ee"></canvas></div>
<div class="panel"><div class="panel-h">Ereignisse</div><div id="log" style="max-height:100px;overflow-y:auto"></div></div>
</div>
<div class="col-center">
<div class="topbar">
<div class="stat gold">💰 <span class="v" id="h-bud">30</span></div>
<span class="inc" id="h-inc">+0/J</span>
<span class="wx" id="h-wx"></span>
<div class="stat yr">📅 <span class="v" id="h-yr">0</span></div>
<div class="spd"><button data-s="0"></button><button data-s="1" class="on"></button><button data-s="2">▶▶</button><button data-s="4">▶▶▶</button></div>
</div>
<div class="canvas-wrap"><canvas id="cv"></canvas></div>
</div>
<div class="col-right">
<div class="panel"><div class="panel-h">Werkzeug</div><div id="tools"></div></div>
<div class="panel"><div class="panel-h">Info</div><div id="info" style="font-size:.6rem;color:var(--text2)">Maus über die Karte bewegen.</div></div>
</div>
</div>
<div id="ov-intro" class="ov">
<div class="ov-c">
<div style="font-size:1.8rem;margin-bottom:.15rem">🏞️</div>
<h2>Flussmanagement</h2>
<p>Der Fluss lebt — er ändert jedes Jahr seinen Lauf! Baue Siedlungen und Felder, aber schütze sie mit Deichen. Begradige den Fluss wenn nötig, aber die Natur rächt sich flussabwärts.</p>
<button class="ov-btn" id="btn-go">Los geht's!</button>
</div>
</div>
<div id="ov-end" class="ov hidden">
<div class="ov-c"><h2 id="end-t"></h2><p id="end-p"></p><button class="ov-btn" id="btn-again">Nochmal</button></div>
</div>
<script>
// =====================================================================
// FLUSSMANAGEMENT v3 — Punkt-basierter Fluss mit Kreisen
// Fluss = 16 Punkte (Spaltenposition), verbunden durch Kreise
// Pro Jahr wandert EIN Punkt 1 Feld links/rechts
// =====================================================================
const COLS = 20, ROWS = 16
const MAX_YEARS = 100, MS_PER_TICK = 2500, WX_INTERVAL = 3
// Fluss: Array von 16 Spaltenpositionen (0..COLS-1)
let riverCol = [] // Ganzzahlige Zielpositionen (Spalte)
let riverColSmooth = [] // Smooth-interpolierte Anzeigepositionen (fliessend)
let riverEng = [] // Befestigung pro Segment (0=natur, 1=holz, 2=stein, 3=beton)
let riverRadius = [] // Radius pro Segment (variiert)
// Land-Grid
let land = [] // ROWS x COLS
let tilePop = [], tileCap = [], tileYield = []
let floodIntensity = [] // 0..1 smooth per tile
let tileFloodYears = [] // wie viele Jahre am Stueck ueberschwemmt
// Game
let budget = 30, year = 0, speed = 1, gameOver = false
let tickAccum = 0, wxTimer = 0, selectedTool = null
let eventLog = [], floats = [] // floating emojis
let history = [] // [{year, pop, food, bio, safety, economy}]
const WX = [
{n:'Dürre',e:'☀️',f:0.2,p:.12},{n:'Trocken',e:'🌤️',f:0.4,p:.25},
{n:'Normal',e:'⛅',f:0.8,p:.33},{n:'Regen',e:'🌧️',f:1.5,p:.20},{n:'Starkregen',e:'⛈️',f:2.8,p:.10}
]
let weather = WX[2]
function rollWx(){let r=Math.random(),c=0;for(const w of WX){c+=w.p;if(r<c)return w}return WX[2]}
const TOOLS = [
{id:'inspect',nm:'Experte fragen',e:'🧑‍🏫',ds:'Klicke irgendwo — der Experte erklärt die Lage.',cost:0},
{id:'levee',nm:'Deich',e:'🧱',ds:'Hält Fluss auf. Kann bei Dauerflut brechen.',cost:6},
{id:'field',nm:'Feld',e:'🌾',ds:'Ertrag je nach Feuchtigkeit. Stirbt nach 2J Flut.',cost:4},
{id:'settlement',nm:'Siedlung',e:'🏘️',ds:'Bewohner zahlen Steuern. Fliehen bei Flut.',cost:10},
{id:'forest',nm:'Wald',e:'🌳',ds:'Bremst Erosion, schützt Grundwasser. Trocknet neben Begradigung aus.',cost:5},
{id:'demolish',nm:'Abreißen',e:'🗑️',ds:'Entfernt alles — auch Begradigung!',cost:2},
{id:'straighten',nm:'Begradigen',e:'📏',ds:'Zieht Fluss zur Mitte. Fixiert ihn dort. Trocknet Umgebung aus!',cost:60},
]
function initGame() {
// Fluss: startet als Maeander
riverCol = [8,9,10,11,10,9,8,7,8,9,10,11,10,9,8,7]
riverColSmooth = riverCol.map(c => c) // Kopie fuer Animation
riverEng = new Array(ROWS).fill(0)
// Variierende Radien — Fluss ist nicht ueberall gleich breit
riverRadius = riverCol.map((_, i) => 0.8 + Math.sin(i * 0.9) * 0.3 + Math.random() * 0.2)
land = []; floodIntensity = []; tilePop = []; tileCap = []; tileYield = []; tileFloodYears = []
for (let r = 0; r < ROWS; r++) {
land[r] = []; floodIntensity[r] = []; tilePop[r] = []; tileCap[r] = []; tileYield[r] = []; tileFloodYears[r] = []
for (let c = 0; c < COLS; c++) {
floodIntensity[r][c] = 0; tilePop[r][c] = 0; tileCap[r][c] = 0; tileYield[r][c] = 0; tileFloodYears[r][c] = 0
// Wald weit vom Fluss, Wiese nah dran
const dist = Math.abs(c - riverCol[r])
if (dist <= 1) land[r][c] = 'meadow'
else if (dist > 4 && Math.random() < 0.5) land[r][c] = 'forest'
else land[r][c] = 'meadow'
}
}
// Startgebaeude
land[4][13] = 'settlement'; land[5][13] = 'field'; land[5][14] = 'field'
tilePop[4][13] = 3; tileCap[4][13] = 8
budget = 30; year = 0; speed = 1; gameOver = false
weather = WX[2]; wxTimer = 0; tickAccum = 0
eventLog = [{t:'🏞️ Willkommen! Der Fluss wandert — plane voraus!',c:'good'}]; history = []
selectedTool = null; floats = []
}
// === FLUSS-GEOMETRIE ===
// Distance-Cache: wird einmal pro Tick aktualisiert statt pro Frame pro Tile
let distCache = []
function rebuildDistCache() {
for (let r = 0; r < ROWS; r++) {
if (!distCache[r]) distCache[r] = []
for (let c = 0; c < COLS; c++) distCache[r][c] = _calcDist(r, c)
}
}
function distToRiver(r, c) {
if (distCache[r] && distCache[r][c] !== undefined) return distCache[r][c]
return _calcDist(r, c)
}
function _calcDist(r, c) {
let minD = 999
for (let i = 0; i < ROWS; i++) {
const rc = riverColSmooth[i]
if (i < ROWS - 1) {
const rc2 = riverColSmooth[i + 1]
// Punkt-zu-Segment Abstand (vereinfacht: nehme naechsten Endpunkt)
const d1 = Math.sqrt((r - i) ** 2 + (c - rc) ** 2)
const d2 = Math.sqrt((r - (i + 1)) ** 2 + (c - rc2) ** 2)
minD = Math.min(minD, d1, d2)
} else {
minD = Math.min(minD, Math.sqrt((r - i) ** 2 + (c - rc) ** 2))
}
}
return minD
}
function isRiverTile(r, c) {
return distToRiver(r, c) < 1.3
}
// === TICK ===
function tick() {
if (gameOver) return
year++
rebuildDistCache() // Cache neu bauen (einmal pro Tick statt pro Frame pro Tile)
// EventLog cappen
if (eventLog.length > 50) eventLog.length = 50
// 1. Einkommen
let income = 0, totalPop = 0, totalFood = 0
for (let r = 0; r < ROWS; r++) for (let c = 0; c < COLS; c++) {
const dist = distToRiver(r, c)
const moisture = Math.max(0, 1 - Math.abs(dist - 2.5) / 3) // optimal bei ~2.5 Felder Abstand
const fi = floodIntensity[r][c]
if (land[r][c] === 'field') {
// Begradigung in der Naehe trocknet Feld aus
let dryPenalty = 0
for (let i = Math.max(0, r-1); i <= Math.min(ROWS-1, r+1); i++) {
if (riverEng[i] > 0) dryPenalty = Math.max(dryPenalty, riverEng[i] * 0.25)
}
const effMoisture = Math.max(0, moisture - dryPenalty)
// Ertrag: Basis-Feuchtigkeit + braucht Arbeiter in der Naehe
let workerBonus = 0
for (let dr=-2;dr<=2;dr++) for(let dc=-2;dc<=2;dc++){
const nr=r+dr,nc=c+dc
if(nr>=0&&nr<ROWS&&nc>=0&&nc<COLS&&land[nr][nc]==='settlement')
workerBonus += tilePop[nr][nc] * 0.15
}
workerBonus = Math.min(1, workerBonus) // max 100% Arbeiterbonus
const baseYield = (1 + effMoisture * 5) * (1 - fi * 0.9)
const y = Math.round(Math.max(0, Math.min(8, baseYield * (0.3 + workerBonus * 0.7))))
tileYield[r][c] = y; income += y * 0.5; totalFood += y
}
if (land[r][c] === 'settlement') {
tileCap[r][c] = 8
const cur = tilePop[r][c]
// Zuzug: braucht Nahrung UND trockenes Haus
const hasFood = totalFood > totalPop * 0.4
if (cur < 8 && hasFood && fi < 0.15) {
tilePop[r][c] = Math.min(8, cur + (Math.random() < 0.35 ? 1 : 0))
}
// Feuchtigkeit → Bewohner werden krank, fliehen langsam
if (fi > 0.2 && cur > 0 && Math.random() < fi * 0.2) {
tilePop[r][c] = Math.max(0, cur - 1)
}
totalPop += tilePop[r][c]
income += tilePop[r][c] * 0.8
}
}
budget += Math.round(income)
// 2. Wetter
wxTimer++; if (wxTimer >= WX_INTERVAL) { wxTimer = 0; weather = rollWx() }
// 3. FLUSS WANDERT — EIN Punkt pro Jahr
const idx = Math.floor(Math.random() * ROWS)
if (riverEng[idx] === 0) { // nur wenn nicht befestigt
const dir = Math.random() < 0.5 ? -1 : 1
const newCol = riverCol[idx] + dir
// Max 3 Felder Abstand zum Nachbarn
const prevOk = idx === 0 || Math.abs(newCol - riverCol[idx - 1]) <= 3
const nextOk = idx === ROWS - 1 || Math.abs(newCol - riverCol[idx + 1]) <= 3
if (newCol >= 1 && newCol < COLS - 1 && prevOk && nextOk) {
riverCol[idx] = newCol
// Was auf dem neuen Feld stand → zerstoeren
if (land[idx][newCol] === 'settlement') {
land[idx][newCol] = 'ruin'
eventLog.push({t: `🌊 Fluss verschlingt Siedlung! (Zeile ${idx+1})`, c: 'bad'})
} else if (land[idx][newCol] === 'field') {
land[idx][newCol] = 'meadow'
eventLog.push({t: `🌊 Fluss überspült Feld (Zeile ${idx+1})`, c: 'bad'})
} else if (land[idx][newCol] === 'levee') {
// Deich haelt — Fluss wird zurueckgedraengt
riverCol[idx] -= dir
eventLog.push({t: `🧱 Deich hält! (Zeile ${idx+1})`, c: 'good'})
}
}
}
// 4. Hochwasser + Trockenheit durch Begradigung
for (let r = 0; r < ROWS; r++) for (let c = 0; c < COLS; c++) {
const dist = distToRiver(r, c)
let target = 0
if (weather.f >= 1.0 && dist < 4) {
const base = Math.max(0, 1 - dist / 4)
const leveeProt = land[r][c] === 'levee' ? 0.9 : (hasLeveeNear(r, c) ? 0.5 : 0)
target = base * weather.f * 0.4 * (1 - leveeProt)
}
floodIntensity[r][c] += (target - floodIntensity[r][c]) * 0.2
// Flood-Jahre tracken
if (floodIntensity[r][c] > 0.35) {
tileFloodYears[r][c] = (tileFloodYears[r][c] || 0) + 1
} else {
tileFloodYears[r][c] = 0
}
// Feld stirbt nach 2 Jahren Dauerflut
if (land[r][c] === 'field' && tileFloodYears[r][c] >= 2) {
land[r][c] = 'meadow'
eventLog.push({t: `🌾💀 Feld ertrunken! (${c+1},${r+1})`, c: 'bad'})
}
// Siedlung: Bewohner fliehen bei Flut, Gebaeude wird Ruine nach 3J
if (land[r][c] === 'settlement' && floodIntensity[r][c] > 0.5 && Math.random() < 0.05) {
land[r][c] = 'ruin'; eventLog.push({t:`🏚️ Siedlung zerstört!`,c:'bad'})
}
// Wald stirbt bei Dauerflut
if (land[r][c] === 'forest' && tileFloodYears[r][c] >= 3) {
land[r][c] = 'dead_tree'
}
// Deich-Erosion bei Dauerflut
if (land[r][c] === 'levee' && floodIntensity[r][c] > 0.4 && Math.random() < 0.08) {
land[r][c] = 'meadow'; eventLog.push({t:'🧱💨 Deich weggespült!',c:'bad'})
}
}
// 4b. Begradigung trocknet Umgebung aus
for (let i = 0; i < ROWS; i++) {
if (riverEng[i] > 0) {
// 2 Felder links und rechts vom begradigten Fluss werden trocken
const rc = riverCol[i]
for (let dc = -3; dc <= 3; dc++) {
const cc = rc + dc
if (cc < 0 || cc >= COLS) continue
if (Math.abs(dc) <= 1) continue // direkt am Fluss ist eh Wasser
// Baeume sterben langsam neben Begradigung
if (land[i][cc] === 'forest' && Math.random() < 0.03 * riverEng[i]) {
land[i][cc] = 'dead_tree'
eventLog.push({t:'🌳💀 Baum vertrocknet neben Begradigung',c:'bad'})
}
// Felder neben Begradigung produzieren weniger (wird in Yield-Berechnung beruecksichtigt)
}
}
}
// 5. Flussbreite bei Hochwasser
for (let i = 0; i < ROWS; i++) {
riverRadius[i] += ((weather.f > 1.2 ? 1.8 : 1.2) - riverRadius[i]) * 0.1
}
// 6. Verfall
for (let r = 0; r < ROWS; r++) for (let c = 0; c < COLS; c++) {
if (land[r][c] === 'ruin' && Math.random() < 0.03) land[r][c] = 'meadow'
if (land[r][c] === 'dead_tree' && Math.random() < 0.03) land[r][c] = 'meadow'
}
// History fuer Graph speichern
const avgEng_=riverEng.reduce((s,v)=>s+v,0)/ROWS
const floodTot=floodIntensity.flat().reduce((s,v)=>s+v,0)/(ROWS*COLS)
const clp=v=>Math.min(100,Math.max(0,Math.round(v)))
let h_sett=0,h_fld=0,h_for=0,h_lev=0,h_ruin=0;for(let rr=0;rr<ROWS;rr++)for(let cc=0;cc<COLS;cc++){const tt=land[rr][cc];if(tt==='settlement')h_sett++;if(tt==='field')h_fld++;if(tt==='forest')h_for++;if(tt==='levee')h_lev++;if(tt==='ruin')h_ruin++}
history.push({
year,
pop:clp(totalPop*5-h_ruin*8),
food:clp(totalFood*1.5),
bio:clp(h_for*4-avgEng_*30-(h_sett+h_fld)*1.5),
safety:clp(15+h_lev*6+avgEng_*8-floodTot*80),
economy:clp(totalPop*3+h_fld*2-h_ruin*5)
})
if (year >= MAX_YEARS) { gameOver = true; showEnd() }
// Wetter-Emojis
if (weather.f >= 1.5) floats.push({x:Math.random(),y:-0.05,e:weather.f>=2.5?'⛈️':'🌧️',t:performance.now()})
if (weather.f <= 0.3) floats.push({x:Math.random()*0.5+0.25,y:-0.05,e:'☀️',t:performance.now()})
renderUI()
}
function hasLeveeNear(r, c) {
for (let dr=-1;dr<=1;dr++) for (let dc=-1;dc<=1;dc++) {
const nr=r+dr,nc=c+dc
if (nr>=0&&nr<ROWS&&nc>=0&&nc<COLS&&land[nr][nc]==='levee') return true
}
return false
}
// === APPLY TOOL ===
function applyAt(gr, gc) {
if (gameOver || !selectedTool || gr<0 || gr>=ROWS || gc<0 || gc>=COLS) return
const t = selectedTool, cur = land[gr][gc]
const onRiver = isRiverTile(gr, gc)
const nearRiver = distToRiver(gr, gc) < 2.5
if (t === 'inspect') {
const d = distToRiver(gr, gc)
const fi = floodIntensity[gr]?.[gc] || 0
const fy = tileFloodYears[gr]?.[gc] || 0
if (onRiver) {
const eng = riverEng[gr]
const engNames = ['natürlich','Holzverbau','Bruchstein','Beton']
let msg = `🌊 Fluss — Befestigung: ${engNames[eng] || 'natürlich'}.`
if (eng > 0) msg += ` Fluss ist hier fixiert. Umgebung trocknet aus.`
else msg += ` Fließt frei — kann jedes Jahr wandern!`
eventLog.push({t: msg, c: ''})
} else if (cur === 'field') {
const yi = tileYield[gr]?.[gc] || 0
let msg = `🌾 Feld — Ertrag: ${yi}/8.`
if (fi > 0.35) msg += ` ⚠️ Abgesoffen! Kaum Ertrag. Noch ${2-fy} Jahr(e) bis es stirbt.`
else if (fi > 0.15) msg += ` 💧 Zu feucht — Ertrag reduziert.`
else if (d > 5) msg += ` 🥵 Sehr trocken — weit vom Fluss. Wenig Ertrag.`
else if (d > 3.5) msg += ` Etwas trocken. Näherer Standort wäre besser.`
else msg += ` 👍 Gute Feuchtigkeit!`
let hasWorkers = false
for(let dr=-2;dr<=2;dr++)for(let dc=-2;dc<=2;dc++){const nr=gr+dr,nc=gc+dc;if(nr>=0&&nr<ROWS&&nc>=0&&nc<COLS&&land[nr][nc]==='settlement'&&tilePop[nr][nc]>0)hasWorkers=true}
if (!hasWorkers) msg += ` ⚠️ Keine Arbeiter! Siedlung in der Nähe nötig.`
eventLog.push({t: msg, c: yi > 4 ? 'good' : yi > 1 ? '' : 'bad'})
} else if (cur === 'settlement') {
const pop = tilePop[gr]?.[gc] || 0
let msg = `🏘️ Siedlung — ${pop}/8 Bewohner.`
if (fi > 0.3) msg += ` 🤒 Feucht! Bewohner werden krank und fliehen.`
else if (fi > 0.1) msg += ` 😰 Etwas feucht. Manche Bewohner sind unwohl.`
else msg += ` Trocken und sicher.`
if (pop < 8) msg += ` Neue Bewohner kommen, wenn genug Nahrung da ist.`
eventLog.push({t: msg, c: fi > 0.2 ? 'bad' : 'good'})
} else if (cur === 'levee') {
let msg = `🧱 Deich.`
if (fi > 0.3) msg += ` ⚠️ Unter Druck! Kann bei Dauerflut brechen.`
else msg += ` Intakt. Schützt dahinterliegende Flächen.`
eventLog.push({t: msg, c: fi > 0.3 ? 'bad' : 'good'})
} else if (cur === 'forest') {
let msg = `🌳 Wald — schützt vor Erosion, speichert Grundwasser.`
let nearStraight = false
for(let i=Math.max(0,gr-1);i<=Math.min(ROWS-1,gr+1);i++){if(riverEng[i]>0)nearStraight=true}
if (nearStraight) msg += ` ⚠️ Neben Begradigung — trocknet aus, Baum in Gefahr!`
if (fi > 0.2) msg += ` 💧 Feucht. Bei Dauerflut stirbt der Baum.`
eventLog.push({t: msg, c: nearStraight ? 'bad' : 'good'})
} else if (cur === 'ruin') {
eventLog.push({t: `🏚️ Ruine — ehemaliges Gebäude. Kann abgerissen werden (💰2). Verfällt mit der Zeit.`, c: 'bad'})
} else if (cur === 'dead_tree') {
eventLog.push({t: `🪵 Toter Baum — vertrocknet oder ertrunken. Verrottet langsam.`, c: 'bad'})
} else {
let msg = `🌱 Wiese — freie Fläche.`
if (d < 2.5) msg += ` Nah am Fluss: fruchtbar, aber hochwassergefährdet.`
else if (d < 4) msg += ` Gute Lage für Feld oder Siedlung.`
else msg += ` Weit vom Fluss. Trocken.`
eventLog.push({t: msg, c: ''})
}
renderUI(); return
}
if (t === 'straighten') {
if (!onRiver) return
if (budget < 60) return
// Begradigung: Fluss wird zur Bildmitte (Spalte 10) gezogen
riverEng[gr] = Math.min(3, riverEng[gr] + 1)
riverCol[gr] = COLS / 2 // Mitte
budget -= 60
eventLog.push({t:`📏 Fluss begradigt zur Mitte (Zeile ${gr+1}) — 💰60`,c:''})
} else if (t === 'levee') {
if (onRiver) return
const dRiv = distToRiver(gr, gc)
if (dRiv > 4) { eventLog.push({t:'🧱 Zu weit vom Fluss! Deich muss in Flussnähe stehen.',c:'bad'}); renderUI(); return }
if (cur !== 'meadow' && cur !== 'forest') return
if (budget < 6) return
land[gr][gc] = 'levee'; budget -= 6
} else if (t === 'field') {
if (onRiver) return
if (cur !== 'meadow') return
if (budget < 4) return
land[gr][gc] = 'field'; budget -= 4
// Kontextuelle Rueckmeldung
const d = distToRiver(gr, gc)
if (d < 2.5) eventLog.push({t:'🌾 Feld angelegt — schön nah am Wasser! Guter Ertrag, aber bei Hochwasser gefährdet.',c:'good'})
else if (d < 4) eventLog.push({t:'🌾 Feld angelegt — gute Lage. Genug Feuchtigkeit, sicher vor dem Fluss.',c:'good'})
else eventLog.push({t:'🌾 Feld angelegt — weit vom Fluss! Hier wird es trocken. Ertrag wird gering sein.',c:'bad'})
// Arbeiter-Hinweis
let hasWorkers = false
for(let dr=-2;dr<=2;dr++)for(let dc=-2;dc<=2;dc++){const nr=gr+dr,nc=gc+dc;if(nr>=0&&nr<ROWS&&nc>=0&&nc<COLS&&land[nr][nc]==='settlement'&&tilePop[nr][nc]>0)hasWorkers=true}
if(!hasWorkers) eventLog.push({t:'⚠️ Keine Siedlung in der Nähe! Ohne Arbeiter nur 30% Ertrag.',c:'bad'})
} else if (t === 'settlement') {
if (onRiver) return
if (cur !== 'meadow' && cur !== 'field') return
if (budget < 10) return
land[gr][gc] = 'settlement'; tilePop[gr][gc] = 0; tileCap[gr][gc] = 8; budget -= 10
const d = distToRiver(gr, gc)
if (d < 2) eventLog.push({t:'🏘️ Siedlung gebaut — sehr nah am Fluss! Hochwassergefahr! Deich empfohlen.',c:'bad'})
else if (d < 4) eventLog.push({t:'🏘️ Siedlung gebaut — gute Lage. Bewohner ziehen bald ein, wenn Nahrung da ist.',c:'good'})
else eventLog.push({t:'🏘️ Siedlung gebaut — sicher, aber weit weg. Felder in der Nähe anlegen!',c:''})
} else if (t === 'forest') {
if (onRiver) return
if (cur !== 'meadow') return
if (budget < 5) return
land[gr][gc] = 'forest'; budget -= 5
eventLog.push({t:'🌳 Wald gepflanzt — schützt vor Erosion und speichert Grundwasser.',c:'good'})
} else if (t === 'demolish') {
// Begradigung abreissen: auf Fluss klicken
if (onRiver && riverEng[gr] > 0) {
if (budget < 2) return
riverEng[gr] = 0 // Befestigung entfernt → Fluss wandert dort wieder frei
budget -= 2
eventLog.push({t:'🗑️ Begradigung entfernt (Zeile '+( gr+1)+')',c:''})
renderUI(); return
}
if (!['field','settlement','levee','ruin','dead_tree','forest'].includes(cur)) return
if (budget < 2) return
land[gr][gc] = 'meadow'; budget -= 2
}
renderUI()
}
// === CANVAS ===
const $cv = document.getElementById('cv'), ctx = $cv.getContext('2d')
let W=0, H=0, tw=0, th=0, animT=0
function resize() {
const r=$cv.parentElement.getBoundingClientRect(), dpr=devicePixelRatio||1
$cv.width=r.width*dpr;$cv.height=r.height*dpr;ctx.setTransform(dpr,0,0,dpr,0,0)
W=r.width;H=r.height;tw=W/COLS;th=H/ROWS
}
function draw() {
animT += 0.012
const s = Math.min(tw, th) * 0.3
// Background
ctx.fillStyle='#c8d8b0'; ctx.fillRect(0,0,W,H)
// === TILES ===
for (let r=0;r<ROWS;r++) for (let c=0;c<COLS;c++) {
const x=c*tw, y=r*th, tile=land[r][c]
const dist=distToRiver(r,c)
const fi=floodIntensity[r]?.[c]||0
const moisture=Math.max(0,1-dist/5) * 0.2 // permanente Feuchtigkeit nahe Fluss
// Grundfarbe
let bR=155,bG=185,bB=120 // meadow
if(tile==='forest'){bR=55;bG=115;bB=45}
if(tile==='field'){bR=185;bG=170;bB=75}
if(tile==='settlement'){bR=175;bG=150;bB=115}
if(tile==='levee'){bR=135;bG=110;bB=80}
if(tile==='ruin'){bR=125;bG=110;bB=95}
if(tile==='dead_tree'){bR=105;bG=125;bB=75}
// Feuchtigkeit + Hochwasser → Blau-Shift
const blue = Math.min(1, moisture + fi * 0.7)
const fR=bR*(1-blue)+65*blue|0
const fG=bG*(1-blue*0.4)+135*blue*0.4|0
const fB=bB*(1-blue)+185*blue|0
ctx.fillStyle=`rgb(${fR},${fG},${fB})`
ctx.fillRect(x,y,tw+0.5,th+0.5)
// Tile-Objekte
const cx=x+tw/2, cy=y+th/2
if(tile==='forest'){
ctx.fillStyle='#4a3020';ctx.fillRect(cx-s*0.1,cy-s*0.2,s*0.2,s*0.5)
ctx.fillStyle=r<ROWS*0.3?'#1a5a2a':'#2a7a2a'
ctx.beginPath();ctx.arc(cx,cy-s*0.4,s*0.45,0,Math.PI*2);ctx.fill()
}
if(tile==='settlement'){
// 3x3 Raster: Personen aussen, Haus-Emoji in der Mitte
// Personen-Emojis je nach Gesundheit (Feuchtigkeit des Hauses)
const pop=tilePop[r]?.[c]||0
const efs=Math.max(6,Math.min(12,tw*0.28))
ctx.font=`${efs}px serif`;ctx.textAlign='center';ctx.textBaseline='middle'
const gx=tw*0.32, gy=th*0.32
const healthy=['👩','👨','👧','👦','👩‍🦱','👨‍🦰','👱‍♀️','🧑']
const sick=['😰','🤒','🤢','😷','🤒','😰','🤢','😷']
for(let gr_=0;gr_<3;gr_++)for(let gc_=0;gc_<3;gc_++){
const idx=gr_*3+gc_
if(idx===4){ctx.fillText('🏘️',cx,cy)}
else{
const slotIdx=idx>4?idx-1:idx
if(slotIdx<pop){
// Gesund, leicht krank, schwer krank je nach Feuchtigkeit
let emoji
if(fi>0.35) emoji=sick[slotIdx%8] // schwer krank
else if(fi>0.15) emoji=(slotIdx%2===0)?sick[slotIdx%8]:healthy[slotIdx%8] // manche krank
else emoji=healthy[slotIdx%8] // alle gesund
ctx.fillText(emoji,cx+(gc_-1)*gx,cy+(gr_-1)*gy)
}
}
}
ctx.textBaseline='alphabetic'
}
if(tile==='field'){
// 3x3 Emoji-Raster: Weizen aussen, Status-Emoji in der Mitte
const yi=tileYield[r]?.[c]||0
const dist_=distToRiver(r,c)
let dryPen=0; for(let ii=Math.max(0,r-1);ii<=Math.min(ROWS-1,r+1);ii++){if(riverEng[ii]>0)dryPen=Math.max(dryPen,riverEng[ii]*0.25)}
const moist=Math.max(0,(1-Math.abs(dist_-2.5)/3)-dryPen)
// Status: 💧=nass, 👍=gut, 🥵=trocken
let status; if(fi>0.25) status='💧'; else if(moist>0.4) status='👍'; else status='🥵'
// 8 Positionen um die Mitte: Weizen oder Wasser
// Bei Ueberschwemmung ersetzen Wasser-Emojis den Weizen von unten
const waterSlots = fi > 0.15 ? Math.min(8, Math.round(fi * 12)) : 0
const wheatSlots = Math.min(8, yi) - waterSlots
const grid3 = [] // 9 Positionen: 0-8, Mitte=4
for(let p=0;p<9;p++){
if(p===4){grid3[p]=status; continue}
const slotIdx=p>4?p-1:p // 0-7 fuer die 8 Aussenpositionen
if(slotIdx<Math.max(0,wheatSlots)) grid3[p]='🌾'
else if(slotIdx<Math.max(0,wheatSlots)+waterSlots) grid3[p]='💧'
else grid3[p]=''
}
const efs=Math.max(6,Math.min(12,tw*0.28))
ctx.font=`${efs}px serif`;ctx.textAlign='center';ctx.textBaseline='middle'
const gx=tw*0.32, gy=th*0.32
for(let gr_=0;gr_<3;gr_++)for(let gc_=0;gc_<3;gc_++){
const idx=gr_*3+gc_
if(grid3[idx]){ctx.fillText(grid3[idx],cx+(gc_-1)*gx,cy+(gr_-1)*gy)}
}
ctx.textBaseline='alphabetic'
}
if(tile==='levee'){
// Deich — wird schmaler bei Erosion (hohe flood intensity)
const w=s*0.8*(1-fi*0.5)
ctx.fillStyle=fi>0.3?'rgba(140,115,85,0.5)':'rgba(140,115,85,0.9)'
ctx.fillRect(cx-w,cy-s*0.1,w*2,s*0.25)
}
if(tile==='ruin'){
ctx.fillStyle='rgba(100,85,70,0.5)';ctx.fillRect(cx-s*0.3,cy-s*0.15,s*0.6,s*0.3)
}
if(tile==='dead_tree'){
ctx.strokeStyle='#2a2a2a';ctx.lineWidth=Math.max(0.5,s*0.1)
ctx.beginPath();ctx.moveTo(cx,cy+s*0.2);ctx.lineTo(cx+s*0.05,cy-s*0.3)
ctx.moveTo(cx+s*0.03,cy-s*0.15);ctx.lineTo(cx-s*0.2,cy-s*0.4)
ctx.stroke()
}
}
// Gitter (dezent)
ctx.strokeStyle='rgba(0,0,0,0.04)';ctx.lineWidth=0.3
for(let r=0;r<=ROWS;r++){ctx.beginPath();ctx.moveTo(0,r*th);ctx.lineTo(W,r*th);ctx.stroke()}
for(let c=0;c<=COLS;c++){ctx.beginPath();ctx.moveTo(c*tw,0);ctx.lineTo(c*tw,H);ctx.stroke()}
// === FLUSS — fliessender Verlauf aus interpolierten Kreisen ===
// Smooth-Animation: riverColSmooth wandert langsam zu riverCol
for (let i = 0; i < ROWS; i++) {
riverColSmooth[i] += (riverCol[i] - riverColSmooth[i]) * 0.06
}
// Virtuelle Punkte oben und unten (damit der Fluss nicht im Nichts beginnt)
const pts = [] // {x (pixel), y (pixel), rad (pixel), eng}
// Virtueller Punkt oberhalb
pts.push({
x: riverColSmooth[0] * tw + tw/2,
y: -th,
rad: riverRadius[0] * tw * 0.45,
eng: riverEng[0]
})
// Alle echten Punkte
for (let i = 0; i < ROWS; i++) {
pts.push({
x: riverColSmooth[i] * tw + tw/2,
y: i * th + th/2,
rad: riverRadius[i] * tw * 0.45,
eng: riverEng[i]
})
}
// Virtueller Punkt unterhalb
pts.push({
x: riverColSmooth[ROWS-1] * tw + tw/2,
y: H + th,
rad: riverRadius[ROWS-1] * tw * 0.45,
eng: riverEng[ROWS-1]
})
// Fluss zeichnen: Kreise entlang der Verbindungen, Radius interpoliert
const totalPts = pts.length
for (let i = 0; i < totalPts - 1; i++) {
const p0 = pts[i], p1 = pts[i+1]
const steps = 12
for (let s = 0; s <= steps; s++) {
const t = s / steps
const px = p0.x + (p1.x - p0.x) * t
const py = p0.y + (p1.y - p0.y) * t
const pr = p0.rad + (p1.rad - p0.rad) * t // Radius interpoliert
const rr = pr + Math.sin(animT * 1.8 + py * 0.01) * pr * 0.06 // leichtes Pulsieren
// Wasser
ctx.fillStyle = 'rgba(50,118,172,0.88)'
ctx.beginPath(); ctx.arc(px, py, rr, 0, Math.PI * 2); ctx.fill()
}
}
// Schimmer/Stroemung ueber den ganzen Fluss
ctx.strokeStyle = `rgba(255,255,255,${0.06 + Math.sin(animT * 2) * 0.03})`; ctx.lineWidth = 0.8
for (let k = 0; k < 8; k++) {
const t = (k / 8 + animT * 0.02) % 1
const idx = t * (totalPts - 1)
const i = Math.floor(idx), frac = idx - i
if (i >= totalPts - 1) continue
const px = pts[i].x + (pts[i+1].x - pts[i].x) * frac
const py = pts[i].y + (pts[i+1].y - pts[i].y) * frac
const pr = pts[i].rad + (pts[i+1].rad - pts[i].rad) * frac
ctx.beginPath(); ctx.moveTo(px - pr * 0.4, py); ctx.lineTo(px + pr * 0.4, py + 1); ctx.stroke()
}
// Uferbefestigung bei begradigten Segmenten
for (let i = 1; i < totalPts - 1; i++) {
const p = pts[i]
const eng = p.eng
if (eng > 0) {
const engColors = ['','rgba(130,100,50,0.5)','rgba(110,110,110,0.6)','rgba(160,160,160,0.7)']
ctx.strokeStyle = engColors[Math.min(3, eng)]
ctx.lineWidth = 1.5 + eng * 1.2
const segH = th * 0.5
ctx.beginPath(); ctx.moveTo(p.x - p.rad - 2, p.y - segH); ctx.lineTo(p.x - p.rad - 2, p.y + segH); ctx.stroke()
ctx.beginPath(); ctx.moveTo(p.x + p.rad + 2, p.y - segH); ctx.lineTo(p.x + p.rad + 2, p.y + segH); ctx.stroke()
}
}
// === WOLKEN ===
for(let i=0;i<3;i++){
const cx=((animT*(10+i*4)+i*250)%(W+120))-60
const cy=10+i*18+Math.sin(animT*0.3+i)*4
const isRain=weather.f>=1.5&&i<2
ctx.fillStyle=isRain?'rgba(90,90,110,0.3)':'rgba(255,255,255,0.35)'
ctx.beginPath();ctx.ellipse(cx,cy,22,8,0,0,Math.PI*2);ctx.fill()
ctx.beginPath();ctx.ellipse(cx-10,cy+2,16,7,0,0,Math.PI*2);ctx.fill()
ctx.beginPath();ctx.ellipse(cx+12,cy+1,18,7,0,0,Math.PI*2);ctx.fill()
if(isRain){ctx.strokeStyle='rgba(80,130,180,0.2)';ctx.lineWidth=0.5
for(let j=0;j<4;j++){const dx=cx-8+j*5;ctx.beginPath();ctx.moveTo(dx,cy+8);ctx.lineTo(dx-1,cy+14+Math.random()*3);ctx.stroke()}}
}
// Floating emojis
const now=performance.now()
for(let i=floats.length-1;i>=0;i--){
const f=floats[i],age=(now-f.t)/1000
if(age>3){floats.splice(i,1);continue}
f.y-=0.0004
ctx.globalAlpha=Math.max(0,1-age/3)
ctx.font=`${12+age*3}px serif`;ctx.textAlign='center'
ctx.fillText(f.e,f.x*W,f.y*H)
ctx.globalAlpha=1
}
}
// === RENDER UI ===
function renderUI(){
let inc=0,pop=0,food=0,sett=0,fld=0,for_=0,lev=0,ruin=0
for(let r=0;r<ROWS;r++)for(let c=0;c<COLS;c++){
const t=land[r][c];if(t==='settlement'){sett++;pop+=tilePop[r][c]}
if(t==='field'){fld++;food+=tileYield[r][c];inc+=tileYield[r][c]*0.5}
if(t==='forest')for_++;if(t==='levee')lev++;if(t==='ruin')ruin++
if(t==='settlement')inc+=tilePop[r][c]*0.8
}
document.getElementById('h-bud').textContent=budget
document.getElementById('h-inc').textContent=`+${Math.round(inc)}/J`
document.getElementById('h-yr').textContent=`${year}/${MAX_YEARS}`
document.getElementById('h-wx').textContent=`${weather.e} ${weather.n}`
const avgEng=riverEng.reduce((s,v)=>s+v,0)/ROWS
const floodTotal=floodIntensity.flat().reduce((s,v)=>s+v,0)/(ROWS*COLS)
const clamp=v=>Math.min(100,Math.max(0,Math.round(v)))
const ps=[
{n:`Bevölkerung (${pop})`,i:'👥',v:pop,abs:true}, // absolut, nicht 0-100
{n:'Nahrung',i:'🌾',v:clamp(food*1.5-(weather.f<0.3?20:0))},
{n:'Biodiversität',i:'🦎',v:clamp(for_*4-avgEng*30-(sett+fld)*1.5)},
{n:'Hochwasserschutz',i:'🛡️',v:clamp(15+lev*6+avgEng*8-floodTotal*80)},
{n:'Wirtschaft',i:'💰',v:clamp(pop*3+fld*2-ruin*5)},
]
document.getElementById('params').innerHTML=ps.map(p=>{
const col=p.v<30?'var(--coral)':p.v>55?'var(--moss)':'var(--sand)'
return `<div class="param"><div class="param-i">${p.i}</div><div class="param-inf"><div class="param-n">${p.n}</div><div class="param-bar"><div class="param-bar-f" style="width:${p.v}%;background:${col}"></div></div></div><div class="param-v" style="color:${col}">${p.v}</div></div>`
}).join('')
document.getElementById('log').innerHTML=eventLog.slice(-10).reverse().map(e=>`<div class="log ${e.c}">${e.t}</div>`).join('')
// Wenn selektiertes Tool nicht mehr leistbar → abwaehlen
if(selectedTool){const st=TOOLS.find(t=>t.id===selectedTool);if(st&&budget<st.cost)selectedTool=null}
document.getElementById('tools').innerHTML=TOOLS.map(t=>{
const af=budget>=t.cost;return `<div class="tool ${selectedTool===t.id&&af?'on':''} ${!af?'off':''}" data-t="${t.id}"><span class="tool-e">${t.e}</span><div class="tool-inf"><div class="tool-nm">${t.nm}</div><div class="tool-ds">${t.ds}</div></div><span class="tool-c">💰${t.cost}</span></div>`
}).join('')
document.querySelectorAll('.tool').forEach(b=>b.addEventListener('click',()=>{selectedTool=selectedTool===b.dataset.t?null:b.dataset.t;renderUI()}))
// === GRAPH ===
drawGraph()
}
function drawGraph(){
const gc=document.getElementById('graph')
if(!gc||history.length<2)return
const gctx=gc.getContext('2d')
const rect=gc.parentElement.getBoundingClientRect()
const dpr=devicePixelRatio||1
gc.width=rect.width*dpr;gc.height=90*dpr;gctx.setTransform(dpr,0,0,dpr,0,0)
const gW=rect.width,gH=90
gctx.clearRect(0,0,gW,gH)
// Hintergrund-Zonen (Toleranzen)
gctx.fillStyle='rgba(180,60,50,0.06)';gctx.fillRect(0,gH*0.75,gW,gH*0.25) // Gefahrenzone unten
gctx.fillStyle='rgba(90,140,90,0.06)';gctx.fillRect(0,0,gW,gH*0.35) // Gute Zone oben
gctx.fillStyle='rgba(200,180,80,0.06)';gctx.fillRect(0,gH*0.35,gW,gH*0.4) // Mittelzone
// Trennlinien
gctx.strokeStyle='rgba(0,0,0,0.06)';gctx.lineWidth=0.5;gctx.setLineDash([3,3])
gctx.beginPath();gctx.moveTo(0,gH*0.35);gctx.lineTo(gW,gH*0.35);gctx.stroke()
gctx.beginPath();gctx.moveTo(0,gH*0.75);gctx.lineTo(gW,gH*0.75);gctx.stroke()
gctx.setLineDash([])
// Labels
gctx.font='7px Inter,sans-serif';gctx.fillStyle='rgba(0,0,0,0.2)';gctx.textAlign='right'
gctx.fillText('gut',gW-3,gH*0.2);gctx.fillText('kritisch',gW-3,gH*0.88)
const lines=[
{key:'pop',color:'#6a6aaa',label:'👥'},
{key:'food',color:'#8a9a30',label:'🌾'},
{key:'bio',color:'#3a8a3a',label:'🦎'},
{key:'safety',color:'#4a7c8a',label:'🛡️'},
{key:'economy',color:'#c4a35a',label:'💰'},
]
// Auto-scale: finde max ueber alle Werte
let maxVal=100
for(const h of history)for(const l of lines){const v=h[l.key]||0;if(v>maxVal)maxVal=v}
maxVal=Math.max(100,maxVal*1.1)
const pad=2,plotW=gW-pad*2,plotH=gH-pad*2
for(const line of lines){
gctx.strokeStyle=line.color;gctx.lineWidth=1.2;gctx.beginPath()
history.forEach((h,i)=>{
const x=pad+(i/(Math.max(1,MAX_YEARS-1)))*plotW
const y=pad+plotH-(((h[line.key]||0)/maxVal)*plotH)
i===0?gctx.moveTo(x,y):gctx.lineTo(x,y)
})
gctx.stroke()
// Label am Ende
if(history.length>0){
const last=history[history.length-1]
const lx=pad+((history.length-1)/(Math.max(1,MAX_YEARS-1)))*plotW
const ly=pad+plotH-(((last[line.key]||0)/maxVal)*plotH)
gctx.font='8px serif';gctx.textAlign='left';gctx.fillStyle=line.color
gctx.fillText(line.label,lx+3,ly+3)
}
}
}
// === EVENTS ===
const $cv2=$cv
$cv2.addEventListener('click',e=>{
const r=$cv2.getBoundingClientRect()
const gc=Math.floor((e.clientX-r.left)/tw), gr=Math.floor((e.clientY-r.top)/th)
applyAt(gr,gc)
})
$cv2.addEventListener('mousemove',e=>{
const r=$cv2.getBoundingClientRect()
const gc=Math.floor((e.clientX-r.left)/tw), gr=Math.floor((e.clientY-r.top)/th)
if(gr>=0&&gr<ROWS&&gc>=0&&gc<COLS){
const t=land[gr][gc],dist=distToRiver(gr,gc).toFixed(1)
const onR=isRiverTile(gr,gc)
let info=onR?`🌊 Fluss (Befestigung: ${riverEng[gr]})`:
{meadow:'🌱 Wiese',forest:'🌳 Wald',field:`🌾 Feld (Ertrag: ${tileYield[gr]?.[gc]||0})`,
settlement:`🏠 Siedlung (${tilePop[gr]?.[gc]||0}/8)`,levee:'🧱 Deich',ruin:'🏚️ Ruine',dead_tree:'🪵 Toter Baum'}[t]||t
info+=`<br>Flussabstand: ${dist}`
document.getElementById('info').innerHTML=info
}
})
document.querySelectorAll('.spd button').forEach(b=>b.addEventListener('click',()=>{
speed=parseInt(b.dataset.s);document.querySelectorAll('.spd button').forEach(x=>x.classList.remove('on'));b.classList.add('on')
}))
function showEnd(){
document.getElementById('end-t').textContent='100 Jahre sind um!'
let p=0;for(let r=0;r<ROWS;r++)for(let c=0;c<COLS;c++)p+=tilePop[r][c]
document.getElementById('end-p').textContent=`💰${budget} | 👥${p} Bewohner | 🏠${land.flat().filter(t=>t==='settlement').length} Siedlungen`
document.getElementById('ov-end').classList.remove('hidden')
}
document.getElementById('btn-go').addEventListener('click',()=>{document.getElementById('ov-intro').classList.add('hidden');renderUI()})
document.getElementById('btn-again').addEventListener('click',()=>{document.getElementById('ov-end').classList.add('hidden');initGame();renderUI()})
document.getElementById('btn-reset').addEventListener('click',()=>{if(confirm('Neu?')){initGame();renderUI()}})
// === GAME LOOP ===
let lastT=0
function loop(ts){if(!lastT)lastT=ts;const dt=ts-lastT;lastT=ts
if(speed>0&&!gameOver){tickAccum+=dt*speed;while(tickAccum>=MS_PER_TICK){tickAccum-=MS_PER_TICK;tick()}}
draw()
requestAnimationFrame(loop)}
initGame();resize();window.addEventListener('resize',resize);renderUI();draw();requestAnimationFrame(loop)
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,522 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Klimawächter</title>
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="stylesheet" href="assets/fonts/inter.css">
<style>
*, *::before, *::after { margin:0; padding:0; box-sizing:border-box; }
:root {
--bg: #f7f6f3; --bg2: #efeee9; --white: #fff;
--text: #1a1a1a; --text2: #4a4a4a; --text3: #8a8a8a;
--fjord: #4a7c8a; --fjord-l: #dae8ec; --fjord-d: #3a6470;
--moss: #5a8a5e; --moss-l: #dceadd;
--sand: #c4a35a; --sand-l: #f2eacc;
--coral: #c07a6b; --coral-l: #f2ddd8;
--danger: #b04a3a;
}
body { font-family:'Inter',system-ui,sans-serif; background:var(--bg); color:var(--text); line-height:1.5; }
.nav { padding:.6rem 1.2rem; display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid rgba(0,0,0,.05); background:var(--white); }
.nav-brand { display:flex; align-items:center; gap:.5rem; font-weight:800; font-size:1rem; color:var(--fjord); text-decoration:none; }
.nav-brand img { width:28px; height:28px; border-radius:6px; }
.nav-actions { display:flex; gap:.4rem; }
.nav-btn { padding:.4rem .8rem; border:1px solid rgba(0,0,0,.08); border-radius:8px; background:var(--white); color:var(--text2); font-size:.78rem; font-weight:600; cursor:pointer; text-decoration:none; }
.nav-btn:hover { background:var(--fjord-l); border-color:var(--fjord); color:var(--fjord); }
/* === MAIN LAYOUT === */
.game-layout { display:grid; grid-template-columns:280px 1fr 280px; gap:.8rem; padding:.8rem; max-width:1400px; margin:0 auto; align-items:start; }
.panel { background:var(--white); border-radius:12px; border:1px solid rgba(0,0,0,.05); padding:.9rem; box-shadow:0 2px 8px rgba(0,0,0,.03); }
.panel-h { font-size:.66rem; font-weight:700; color:var(--fjord); text-transform:uppercase; letter-spacing:.06em; margin-bottom:.5rem; }
/* === LEFT: Resources & Goals === */
.left { display:flex; flex-direction:column; gap:.8rem; }
.resource { display:flex; align-items:center; gap:.6rem; padding:.4rem .2rem; border-bottom:1px solid rgba(0,0,0,.04); }
.resource:last-child { border-bottom:none; }
.resource-icon { font-size:1.4rem; width:28px; text-align:center; }
.resource-info { flex:1; min-width:0; }
.resource-name { font-size:.7rem; color:var(--text3); font-weight:500; }
.resource-val { font-size:.95rem; font-weight:700; color:var(--text); }
.resource-trend { font-size:.65rem; padding:1px 5px; border-radius:4px; font-weight:600; }
.trend-up { background:var(--moss-l); color:var(--moss); }
.trend-down { background:var(--coral-l); color:var(--coral); }
.goals .goal { padding:.4rem 0; border-bottom:1px solid rgba(0,0,0,.04); }
.goals .goal:last-child { border:0; }
.goal-row { display:flex; align-items:center; gap:.4rem; font-size:.78rem; }
.goal-check { width:14px; height:14px; border:1.5px solid var(--text3); border-radius:3px; display:flex; align-items:center; justify-content:center; font-size:.6rem; flex-shrink:0; }
.goal-check.done { background:var(--moss); border-color:var(--moss); color:#fff; }
.goal-bar { height:3px; background:#e8e5dc; border-radius:2px; margin-top:.3rem; overflow:hidden; }
.goal-bar-fill { height:100%; background:var(--moss); transition:width .3s; }
/* === CENTER: Game Canvas + Controls === */
.center { display:flex; flex-direction:column; gap:.6rem; }
.canvas-wrap { background:var(--white); border-radius:14px; border:1px solid rgba(0,0,0,.05); overflow:visible; box-shadow:0 4px 16px rgba(0,0,0,.05); position:relative; min-height:460px; }
/* Tutorial Overlay */
.tutorial-overlay { position:absolute; inset:0; background:rgba(15,30,40,.85); backdrop-filter:blur(8px); display:flex; align-items:center; justify-content:center; z-index:10; padding:1.5rem; }
.tutorial-card { background:var(--white); border-radius:16px; padding:1.5rem 1.8rem; max-width:480px; box-shadow:0 20px 60px rgba(0,0,0,.3); }
.tutorial-card h3 { font-size:1.2rem; font-weight:800; margin-bottom:.6rem; color:var(--fjord); }
.tutorial-card p { font-size:.88rem; color:var(--text2); white-space:pre-line; margin-bottom:1rem; }
.tutorial-card .progress-dots { display:flex; gap:.3rem; justify-content:center; margin:.8rem 0; }
.dot { width:8px; height:8px; border-radius:50%; background:#dadad0; }
.dot.active { background:var(--fjord); }
.tut-btn { width:100%; padding:.7rem; background:var(--fjord); color:#fff; border:none; border-radius:10px; font-weight:700; font-size:.9rem; cursor:pointer; }
.tut-btn:hover { background:var(--fjord-d); }
/* Speed controls */
.speed-bar { display:flex; gap:.3rem; padding:.5rem; background:var(--white); border-radius:10px; border:1px solid rgba(0,0,0,.05); }
.speed-btn { flex:1; padding:.5rem; border:none; background:transparent; color:var(--text2); font-size:.8rem; font-weight:600; cursor:pointer; border-radius:6px; }
.speed-btn:hover { background:#f0eeea; }
.speed-btn.active { background:var(--fjord); color:#fff; }
/* Graphs */
.graphs { display:grid; grid-template-columns:1fr 1fr; gap:.5rem; margin-top:.4rem; }
.graph-card { background:var(--white); border-radius:10px; border:1px solid rgba(0,0,0,.05); padding:.5rem .6rem .3rem; }
.graph-title { font-size:.65rem; color:var(--text3); font-weight:600; text-transform:uppercase; letter-spacing:.06em; margin-bottom:.2rem; }
.graph-svg { width:100%; height:55px; display:block; }
/* === RIGHT: Measures Shop & Events === */
.right { display:flex; flex-direction:column; gap:.8rem; max-height:50vh; overflow-y:auto; }
.measure-card { display:flex; gap:.6rem; padding:.6rem; border-radius:10px; border:1px solid rgba(0,0,0,.06); margin-bottom:.4rem; cursor:pointer; transition:all .2s; background:var(--white); }
.measure-card:hover { border-color:var(--fjord); background:var(--fjord-l); }
.measure-card.disabled { opacity:.5; cursor:not-allowed; }
.measure-card.disabled:hover { border-color:rgba(0,0,0,.06); background:var(--white); }
.measure-icon { font-size:1.5rem; width:32px; text-align:center; }
.measure-info { flex:1; min-width:0; }
.measure-name { font-size:.8rem; font-weight:700; }
.measure-desc { font-size:.66rem; color:var(--text3); line-height:1.3; }
.measure-stats { display:flex; gap:.3rem; flex-wrap:wrap; margin-top:.2rem; }
.measure-stat { font-size:.6rem; padding:1px 5px; border-radius:4px; background:#f0eeea; color:var(--text3); font-weight:600; }
.measure-cost { background:var(--sand-l); color:var(--sand); }
.measure-co2 { background:var(--moss-l); color:var(--moss); }
.measure-protect { background:var(--fjord-l); color:var(--fjord); }
.measure-count { font-size:.62rem; padding:1px 5px; border-radius:4px; background:var(--fjord); color:#fff; font-weight:700; }
.events-list { max-height:160px; overflow-y:auto; }
.event { padding:.4rem .5rem; border-radius:8px; margin-bottom:.3rem; font-size:.74rem; line-height:1.3; }
.event-info { background:#f0eeea; color:var(--text2); }
.event-success { background:var(--moss-l); color:var(--moss); }
.event-warning { background:var(--sand-l); color:var(--sand); }
.event-danger { background:var(--coral-l); color:var(--danger); }
/* === END SCREEN === */
.end-screen { position:fixed; inset:0; background:rgba(15,30,40,.9); backdrop-filter:blur(10px); display:flex; align-items:center; justify-content:center; z-index:100; padding:2rem; }
.end-card { background:var(--white); border-radius:20px; padding:2rem 2.5rem; max-width:500px; text-align:center; box-shadow:0 30px 80px rgba(0,0,0,.4); }
.end-icon { font-size:4rem; margin-bottom:.5rem; }
.end-card h2 { font-size:1.8rem; font-weight:900; letter-spacing:-.02em; margin-bottom:.3rem; }
.end-card .end-summary { color:var(--text2); margin-bottom:1rem; }
.end-stats { display:grid; grid-template-columns:1fr 1fr; gap:.8rem; margin:1rem 0; }
.end-stat { background:#f7f6f3; border-radius:10px; padding:.8rem; }
.end-stat-val { font-size:1.4rem; font-weight:800; color:var(--fjord); }
.end-stat-lbl { font-size:.7rem; color:var(--text3); text-transform:uppercase; }
/* RESPONSIVE */
@media (max-width: 1100px) {
.game-layout { grid-template-columns:1fr; }
.left, .right { order:2; }
}
</style>
</head>
<body>
<nav class="nav">
<a href="index.html" class="nav-brand"><img src="assets/img/logo.png" alt="">GeoGraSim · Klimawächter</a>
<div class="nav-actions">
<button class="nav-btn" id="btn-save">💾 Speichern</button>
<button class="nav-btn" id="btn-load">📂 Laden</button>
<button class="nav-btn" id="btn-finish">⏹ Beenden</button>
<button class="nav-btn" id="btn-reset" title="Stand löschen und neu starten">🔄 Reset</button>
<a href="index.html" class="nav-btn">← Zurück</a>
</div>
</nav>
<div class="game-layout">
<!-- LEFT: Resources, Goals -->
<aside class="left">
<div class="panel">
<div class="panel-h">📊 Status</div>
<div id="resources"></div>
</div>
<div class="panel goals">
<div class="panel-h">🎯 Ziele</div>
<div id="goals"></div>
</div>
</aside>
<!-- CENTER: Canvas + Controls + Graphs -->
<main class="center">
<div class="canvas-wrap" id="canvas-wrap">
<div class="tutorial-overlay" id="tutorial">
<div class="tutorial-card">
<h3 id="tut-title"></h3>
<p id="tut-text"></p>
<div class="progress-dots" id="tut-dots"></div>
<button class="tut-btn" id="tut-next">Weiter →</button>
</div>
</div>
</div>
<div class="speed-bar">
<button class="speed-btn" data-speed="0"></button>
<button class="speed-btn active" data-speed="1">▶ Normal</button>
<button class="speed-btn" data-speed="2">⏩ Schnell</button>
<button class="speed-btn" data-speed="4">⏭ Sehr schnell</button>
</div>
<div class="graphs">
<div class="graph-card">
<div class="graph-title">🌫 CO₂ über Zeit</div>
<svg class="graph-svg" id="graph-co2"></svg>
</div>
<div class="graph-card">
<div class="graph-title">🌡 Temperatur über Zeit</div>
<svg class="graph-svg" id="graph-temp"></svg>
</div>
<div class="graph-card">
<div class="graph-title">💰 Budget über Zeit</div>
<svg class="graph-svg" id="graph-budget"></svg>
</div>
<div class="graph-card">
<div class="graph-title">🌊 Meeresspiegel über Zeit</div>
<svg class="graph-svg" id="graph-sea"></svg>
</div>
</div>
</main>
<!-- RIGHT: Measures Shop, Events -->
<aside class="right">
<div class="panel">
<div class="panel-h">🏗 Maßnahmen kaufen</div>
<div id="measures"></div>
</div>
<div class="panel">
<div class="panel-h">📰 Ereignisse</div>
<div class="events-list" id="events"></div>
</div>
</aside>
</div>
<!-- End screen template -->
<div id="end-screen-host"></div>
<script type="module">
import { KlimawaechterGame, MEASURES } from './src/sims/sim-05-treibhaus/game.ts'
import { KlimawaechterRenderer } from './src/sims/sim-05-treibhaus/game-renderer.ts'
const game = new KlimawaechterGame()
let renderer = null
let endShown = false
// === ELEMENTS ===
const tutorialEl = document.getElementById('tutorial')
const tutTitle = document.getElementById('tut-title')
const tutText = document.getElementById('tut-text')
const tutDots = document.getElementById('tut-dots')
const tutNext = document.getElementById('tut-next')
const resourcesEl = document.getElementById('resources')
const goalsEl = document.getElementById('goals')
const measuresEl = document.getElementById('measures')
const eventsEl = document.getElementById('events')
const canvasWrap = document.getElementById('canvas-wrap')
// === RENDER MEASURES SHOP ===
function renderMeasures() {
const budget = game.getResource('budget')
measuresEl.innerHTML = MEASURES.map(m => {
const count = game.getMeasureCount(m.id)
const canAfford = budget >= m.cost
return `
<div class="measure-card ${canAfford ? '' : 'disabled'}" data-id="${m.id}">
<div class="measure-icon">${m.emoji}</div>
<div class="measure-info">
<div style="display:flex;justify-content:space-between;align-items:center">
<div class="measure-name">${m.name}</div>
${count > 0 ? `<span class="measure-count">×${count}</span>` : ''}
</div>
<div class="measure-desc">${m.description}</div>
<div class="measure-stats">
<span class="measure-stat measure-cost">💰 ${m.cost} €</span>
${m.co2Reduction > 0 ? `<span class="measure-stat measure-co2">${m.co2Reduction} CO₂/J</span>` : ''}
${m.protection > 0 ? `<span class="measure-stat measure-protect">🛡 +${m.protection} cm</span>` : ''}
<span class="measure-stat">⚙ ${m.upkeep} €/J</span>
</div>
</div>
</div>
`
}).join('')
measuresEl.querySelectorAll('.measure-card').forEach(el => {
el.addEventListener('click', () => {
if (el.classList.contains('disabled')) return
const id = el.dataset.id
game.buyMeasure(id)
})
})
}
// === RENDER RESOURCES ===
function renderResources() {
const resources = game.getResourcesArray()
resourcesEl.innerHTML = resources.map(r => `
<div class="resource">
<div class="resource-icon">${r.icon}</div>
<div class="resource-info">
<div class="resource-name">${r.name}</div>
<div class="resource-val">${r.format ? r.format(r.current) : r.current}</div>
</div>
</div>
`).join('')
}
// === RENDER GOALS ===
function renderGoals() {
const snap = game.getSnapshot()
goalsEl.innerHTML = snap.goals.map((g, i) => {
const goal = game.goals?.[i] || { title: g.id, description: '' }
return `
<div class="goal">
<div class="goal-row">
<div class="goal-check ${g.achieved ? 'done' : ''}">${g.achieved ? '✓' : ''}</div>
<div>${goal.title || g.id}</div>
</div>
<div class="goal-bar"><div class="goal-bar-fill" style="width:${g.progress}%"></div></div>
</div>
`
}).join('')
}
// === RENDER EVENTS ===
function renderEvents() {
const events = game.getEvents(8)
if (events.length === 0) {
eventsEl.innerHTML = '<div style="font-size:.72rem;color:var(--text3);text-align:center;padding:.5rem">Noch keine Ereignisse</div>'
return
}
eventsEl.innerHTML = events.map(e => `
<div class="event event-${e.severity}">
<strong>Jahr ${e.tick}:</strong> ${e.text}
</div>
`).join('')
}
// === RENDER GRAPHS (volle Version mit Zonen, Referenzlinien, Animation) ===
const GRAPH_CFG = [
{id:'graph-co2', key:'co2', color:'#8a7868', unit:'ppm', label:'CO₂',
zones:[{min:280,max:350,color:'rgba(90,138,94,.12)',label:'Vorindustriell'},{min:350,max:450,color:'rgba(196,163,90,.1)'},{min:450,max:800,color:'rgba(192,122,107,.08)'}],
lines:[{y:420,label:'Heute',color:'#c4a35a',dash:true}]},
{id:'graph-temp', key:'temperature', color:'#c07a6b', unit:'°C', label:'Temperatur',
zones:[{min:14,max:16,color:'rgba(90,138,94,.12)',label:'Sicher'},{min:16,max:17,color:'rgba(196,163,90,.1)'},{min:17,max:22,color:'rgba(192,122,107,.08)'}],
lines:[{y:17,label:'Klimaziel',color:'#c07a6b',dash:true}]},
{id:'graph-budget', key:'budget', color:'#5a8a5e', unit:'Mrd', label:'Budget',
zones:[], lines:[{y:0,label:'',color:'#ccc',dash:true}]},
{id:'graph-sea', key:'sealevel', color:'#4a7c8a', unit:'cm', label:'Meeresspiegel',
zones:[{min:0,max:30,color:'rgba(90,138,94,.08)'},{min:30,max:100,color:'rgba(196,163,90,.08)'},{min:100,max:300,color:'rgba(192,122,107,.08)'}],
lines:[{y:50,label:'Kritisch',color:'#c07a6b',dash:true}]}
]
function renderGraphs() {
const snap = game.getSnapshot()
const tl = snap.timeline
if (tl.length === 0) return
GRAPH_CFG.forEach(cfg => {
const pts = tl.filter(e => e.values).map(e => ({x: e.tick, y: e.values[cfg.key] || 0}))
// Append live value
const live = snap.values?.[cfg.key]
if (live !== undefined) pts.push({x: snap.tick, y: live})
drawGraph(cfg, pts)
})
}
function niceFloor(v, step) { return Math.floor(v / step) * step }
function niceCeil(v, step) { return Math.ceil(v / step) * step }
function fmtTick(v) { return Math.abs(v) >= 1000 ? (v/1000).toFixed(1)+'k' : v >= 10 ? v.toFixed(0) : v.toFixed(1) }
function drawGraph(cfg, points) {
const svg = document.getElementById(cfg.id)
if (!svg) return
const W = svg.clientWidth || 200, H = 55
const pad = {l:28, r:6, t:4, b:12}
const gw = W - pad.l - pad.r, gh = H - pad.t - pad.b
svg.setAttribute('viewBox', `0 0 ${W} ${H}`)
if (points.length < 2) { svg.innerHTML = ''; return }
// Auto-scale Y
let allY = points.map(p => p.y)
let yMin = Math.min(...allY), yMax = Math.max(...allY)
const margin = (yMax - yMin) * 0.1 || 1
const step = yMax - yMin > 100 ? 50 : yMax - yMin > 10 ? 5 : 1
yMin = niceFloor(yMin - margin, step)
yMax = niceCeil(yMax + margin, step)
if (yMin === yMax) { yMin -= 1; yMax += 1 }
const maxX = Math.max(30, points[points.length - 1].x)
const px = p => pad.l + (p.x / maxX) * gw
const py = p => pad.t + gh - ((p.y - yMin) / (yMax - yMin)) * gh
let html = ''
// Zones
;(cfg.zones || []).forEach(z => {
const zy1 = pad.t + gh - ((Math.min(z.max, yMax) - yMin) / (yMax - yMin)) * gh
const zy2 = pad.t + gh - ((Math.max(z.min, yMin) - yMin) / (yMax - yMin)) * gh
html += `<rect x="${pad.l}" y="${zy1}" width="${gw}" height="${zy2-zy1}" fill="${z.color}" />`
})
// Reference lines
;(cfg.lines || []).forEach(ln => {
if (ln.y >= yMin && ln.y <= yMax) {
const ly = pad.t + gh - ((ln.y - yMin) / (yMax - yMin)) * gh
html += `<line x1="${pad.l}" y1="${ly}" x2="${W-pad.r}" y2="${ly}" stroke="${ln.color}" stroke-width=".7" ${ln.dash ? 'stroke-dasharray="3,2"' : ''} />`
if (ln.label) html += `<text x="${W-pad.r-1}" y="${ly-2}" text-anchor="end" font-size="5.5" fill="${ln.color}" opacity=".7">${ln.label}</text>`
}
})
// Y-axis ticks
for (let i = 0; i <= 3; i++) {
const val = yMin + (yMax - yMin) * (i / 3)
const ty = pad.t + gh - (i / 3) * gh
html += `<text x="${pad.l-2}" y="${ty+2.5}" text-anchor="end" font-size="5.5" fill="#aaa">${fmtTick(val)}</text>`
html += `<line x1="${pad.l}" y1="${ty}" x2="${W-pad.r}" y2="${ty}" stroke="#eee" stroke-width=".3" />`
}
// Data line
const path = points.map((p, i) => `${i===0?'M':'L'} ${px(p).toFixed(1)} ${Math.max(pad.t+1, Math.min(H-pad.b-1, py(p))).toFixed(1)}`).join(' ')
html += `<path d="${path}" fill="none" stroke="${cfg.color}" stroke-width="1.8" stroke-linejoin="round" stroke-linecap="round" />`
// Animated dot at latest point
const last = points[points.length - 1]
const lx = px(last), ly = Math.max(pad.t+1, Math.min(H-pad.b-1, py(last)))
html += `<circle cx="${lx}" cy="${ly}" r="2.5" fill="${cfg.color}"><animate attributeName="r" values="2.5;4;2.5" dur="1.5s" repeatCount="indefinite"/><animate attributeName="opacity" values="1;.5;1" dur="1.5s" repeatCount="indefinite"/></circle>`
// Value badge
html += `<text x="${W-pad.r}" y="${pad.t+7}" text-anchor="end" font-size="7" font-weight="700" fill="${cfg.color}">${last.y.toFixed(last.y > 50 ? 0 : 1)} ${cfg.unit}</text>`
svg.innerHTML = html
}
// === TUTORIAL ===
function renderTutorial() {
const step = game.getCurrentTutorialStep()
if (!step || game.getSnapshot().state !== 'tutorial') {
tutorialEl.style.display = 'none'
if (!renderer) {
renderer = new KlimawaechterRenderer(canvasWrap, game)
renderer.start()
}
return
}
tutorialEl.style.display = 'flex'
tutTitle.textContent = step.title
tutText.textContent = step.text
const totalSteps = 4
tutDots.innerHTML = ''
for (let i = 0; i < totalSteps; i++) {
const d = document.createElement('div')
d.className = 'dot' + (i === game.getSnapshot().tutorialStep ? ' active' : '')
tutDots.appendChild(d)
}
tutNext.textContent = game.getSnapshot().tutorialStep === totalSteps - 1 ? 'Simulation starten 🚀' : 'Weiter →'
}
tutNext.addEventListener('click', () => {
game.nextTutorialStep()
renderAll()
})
// === SPEED CONTROL ===
document.querySelectorAll('.speed-btn').forEach(btn => {
btn.addEventListener('click', () => {
const speed = parseInt(btn.dataset.speed)
game.setSpeed(speed)
document.querySelectorAll('.speed-btn').forEach(b => b.classList.remove('active'))
btn.classList.add('active')
})
})
// === SAVE / LOAD / FINISH ===
document.getElementById('btn-save').addEventListener('click', () => {
localStorage.setItem('klimawaechter-save', game.serialize())
alert('💾 Stand gespeichert')
})
document.getElementById('btn-load').addEventListener('click', () => {
const save = localStorage.getItem('klimawaechter-save')
if (!save) { alert('Kein Stand vorhanden'); return }
if (game.deserialize(save)) {
renderAll()
alert('📂 Stand geladen')
}
})
document.getElementById('btn-finish').addEventListener('click', () => {
game.finish()
showEndScreen()
})
// === END SCREEN ===
function showEndScreen() {
if (endShown) return
endShown = true
const snap = game.getSnapshot()
const won = snap.state === 'won'
const lost = snap.state === 'lost'
const icon = won ? '🏆' : lost ? '💔' : '🏁'
const title = won ? 'Du hast gewonnen!' : lost ? 'Simulation gescheitert' : 'Simulation beendet'
const summary = won
? 'Glückwunsch! Du hast deine Stadt bis zum Jahr 2100 erfolgreich durch die Klimakrise geführt.'
: lost
? snap.resources.budget < 0 ? 'Deine Stadt ist pleite gegangen.' : 'Der steigende Meeresspiegel hat zu viele Häuser zerstört.'
: `Du hast im Jahr ${2025 + snap.tick} beendet.`
const html = `
<div class="end-screen">
<div class="end-card">
<div class="end-icon">${icon}</div>
<h2>${title}</h2>
<p class="end-summary">${summary}</p>
<div class="end-stats">
<div class="end-stat"><div class="end-stat-val">${snap.tick}</div><div class="end-stat-lbl">Jahre (bis ${2025 + snap.tick})</div></div>
<div class="end-stat"><div class="end-stat-val">${Math.round(snap.resources.budget)} €</div><div class="end-stat-lbl">Budget übrig</div></div>
<div class="end-stat"><div class="end-stat-val">${snap.resources.temperature.toFixed(1)}°C</div><div class="end-stat-lbl">Endtemperatur</div></div>
<div class="end-stat"><div class="end-stat-val">${Math.round(snap.resources.flooded)}%</div><div class="end-stat-lbl">Überflutet</div></div>
</div>
<button class="tut-btn" onclick="location.reload()">🔄 Nochmal spielen</button>
</div>
</div>
`
document.getElementById('end-screen-host').innerHTML = html
}
// === MAIN RENDER ===
function renderAll() {
renderResources()
renderGoals()
renderMeasures()
renderEvents()
renderGraphs()
renderTutorial()
const snap = game.getSnapshot()
if ((snap.state === 'won' || snap.state === 'lost') && !endShown) {
setTimeout(showEndScreen, 800)
}
}
// Subscribe to game updates
game.subscribe(renderAll)
// Initial render
renderAll()
</script>
</body>
</html>
@@ -0,0 +1,454 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Helikopter-Navigation</title>
<link rel="icon" type="image/png" href="favicon-96x96.png">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;overflow:hidden;height:100vh;background:#e8e4d8}
#map{width:100%;height:100vh}
/* Aktive Basen: groß, farbig */
.heli-active{font-size:32px;text-align:center;line-height:1;filter:drop-shadow(0 2px 4px rgba(0,0,0,.4))}
.heli-active.oeamtc{filter:drop-shadow(0 2px 4px rgba(0,0,0,.3)) brightness(1.3) saturate(3) hue-rotate(-30deg) sepia(.3)}
.heli-active.ara{filter:drop-shadow(0 2px 4px rgba(0,0,0,.3)) hue-rotate(320deg) saturate(2)}
.heli-active.police{filter:drop-shadow(0 2px 4px rgba(0,0,0,.3)) hue-rotate(180deg) saturate(1.5) brightness(1.1)}
.heli-active.bh{filter:drop-shadow(0 2px 4px rgba(0,0,0,.3)) hue-rotate(90deg) saturate(2)}
/* Inaktive Basen: klein, deutlich sichtbar aber entsättigt */
.heli-inactive{font-size:18px;text-align:center;line-height:1;filter:drop-shadow(0 1px 3px rgba(0,0,0,.3)) grayscale(.7) brightness(.7) contrast(1.2)}
/* Labels/Pills */
.heli-pill{font-family:'Inter',system-ui,sans-serif;font-weight:600;font-size:11px;white-space:nowrap;padding:3px 8px;border-radius:5px;background:rgba(255,255,255,.92);box-shadow:0 1px 4px rgba(0,0,0,.15);line-height:1.3;max-width:180px}
.heli-pill .pill-name{font-weight:800;display:block}
.heli-pill .pill-org{font-size:8px;opacity:.7;display:block}
.heli-pill.oeamtc{border-left:4px solid #d4a017;color:#7a5f00}
.heli-pill.ara{border-left:4px solid #cc3333;color:#882222}
.heli-pill.police{border-left:4px solid #2255aa;color:#1a3a77}
.heli-pill.bh{border-left:4px solid #228833;color:#145522}
.heli-pill-sm{font-family:'Inter',system-ui,sans-serif;font-weight:700;font-size:10px;white-space:nowrap;padding:2px 6px;border-radius:3px;background:rgba(255,255,255,.88);box-shadow:0 1px 3px rgba(0,0,0,.15);color:#555;border-left:3px solid #888}
/* Marker: weiße Kreise mit Rahmen */
.marker-circle{width:26px;height:26px;background:#fff;border:2.5px solid #1f4e5a;border-radius:50%;box-shadow:0 2px 6px rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;font-size:13px;line-height:1}
.marker-circle.capital{border-color:#e8833a;width:30px;height:30px;font-size:15px}
.marker-circle.peak{border-color:#5a8a5e;width:24px;height:24px;font-size:12px}
.marker-circle.heli-m{border-color:#d4a017;width:30px;height:30px;font-size:16px}
.marker-circle.heli-m.inactive{width:22px;height:22px;font-size:11px;border-color:#888;opacity:.7}
/* Stadt-Pills */
.city-pill{font-family:'Inter',system-ui,sans-serif;font-weight:700;font-size:11px;white-space:nowrap;padding:4px 10px;border-radius:5px;background:rgba(218,232,236,.94);box-shadow:0 1px 4px rgba(0,0,0,.15);color:#1f4e5a;border-bottom:2.5px solid #4a7c8a;display:flex;align-items:center;height:22px}
.city-pill.capital{color:#e8833a;background:rgba(232,200,170,.92);border-bottom-color:#e8833a}
.city-pill .city-pop{font-size:8px;font-weight:500;color:#8a8a8a;margin-left:4px}
/* Berg-Pills */
.peak-pill{font-family:'Inter',system-ui,sans-serif;font-weight:700;font-size:10px;white-space:nowrap;padding:4px 9px;border-radius:5px;background:rgba(220,234,221,.94);box-shadow:0 1px 4px rgba(0,0,0,.12);color:#3a6b3e;border-bottom:2.5px solid #5a8a5e;display:flex;align-items:center;height:20px}
.peak-pill .peak-ele{font-size:8px;font-weight:500;color:#5a8a5e;margin-left:4px}
/* Quartett-Popup */
.heli-popup .leaflet-popup-content{margin:0;padding:0}
.heli-popup .leaflet-popup-content-wrapper{border-radius:12px;padding:0;overflow:hidden;box-shadow:0 4px 20px rgba(0,0,0,.15)}
.quartett{width:260px;font-family:'Inter',system-ui,sans-serif}
.quartett-img{height:140px;overflow:hidden;background:#f7f6f3;display:flex;align-items:center;justify-content:center}
.quartett-img img{width:100%;height:100%;object-fit:cover}
.quartett-body{padding:10px 12px 12px}
.quartett-name{font-size:13px;font-weight:800;color:#1f4e5a;margin-bottom:2px}
.quartett-org{font-size:10px;color:#8a8a8a;margin-bottom:8px}
.quartett-stats{display:grid;grid-template-columns:1fr 1fr;gap:4px}
.quartett-stat{background:#f7f6f3;border-radius:5px;padding:4px 6px}
.quartett-stat .qs-label{font-size:8px;color:#8a8a8a;font-weight:500}
.quartett-stat .qs-val{font-size:12px;font-weight:800;color:#1f4e5a}
.quartett-fact{margin-top:8px;font-size:10px;color:#4a4a4a;font-style:italic;line-height:1.4;border-top:1px solid #eee;padding-top:6px}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
var bounds = L.latLngBounds(L.latLng(46.3, 9.4), L.latLng(49.1, 17.2));
var map = L.map('map', {
zoomControl: false,
maxBounds: bounds.pad(0.05),
maxBoundsViscosity: 1.0,
minZoom: 7,
maxZoom: 15
}).setView([47.4, 13.2], 8);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
maxZoom: 18
}).addTo(map);
L.control.zoom({position: 'topright'}).addTo(map);
L.control.scale({position: 'bottomleft', metric: true, imperial: false, maxWidth: 200}).addTo(map);
// === HUBSCHRAUBER-TYPEN (Quartett-Daten) ===
var HELI_TYPES = {
'EC135': {name:'Eurocopter EC135', img:'sims/heli/assets/heli-ec135.png', engines:2, crew:'1 Pilot + 1 Notarzt + 1 Sanitäter', maxSpeed:254, range:635, ceiling:6096, weight:2910, seats:6, role:'Rettung & Notfall', fact:'Der EC135 ist der meistgenutzte Rettungshubschrauber in Europa.', wiki:'https://de.wikipedia.org/wiki/Eurocopter_EC_135'},
'H145': {name:'Airbus H145', img:'sims/heli/assets/heli-h145.png', engines:2, crew:'12 Piloten + Rettungsteam', maxSpeed:268, range:680, ceiling:5485, weight:3700, seats:8, role:'Rettung & Intensivtransport', fact:'Der H145 kann auch bei Nacht und schlechtem Wetter fliegen (IFR).', wiki:'https://de.wikipedia.org/wiki/Airbus_Helicopters_H145'},
'AW169': {name:'Leonardo AW169', img:'sims/heli/assets/heli-aw169.png', engines:2, crew:'12 Piloten + Besatzung', maxSpeed:280, range:820, ceiling:4572, weight:4600, seats:10, role:'Polizei & Überwachung', fact:'Die Flugpolizei nennt ihre Hubschrauber „Libelle".', wiki:'https://de.wikipedia.org/wiki/Leonardo_AW169'},
'AB212': {name:'Agusta-Bell AB212', img:'sims/heli/assets/heli-ab212.png', engines:2, crew:'2 Piloten + Besatzung', maxSpeed:240, range:450, ceiling:4630, weight:5070, seats:14, role:'Militär & Katastrophenhilfe', fact:'Das Bundesheer setzt den AB212 seit den 1980er-Jahren ein.', wiki:'https://de.wikipedia.org/wiki/Agusta-Bell_AB_212'},
};
// === ALLE HUBSCHRAUBER-BASEN ÖSTERREICH ===
var bases = [
// Notarzt-Heli
{name:'Innsbruck OE-XVB', code:'C1', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.2600, lon:11.3439, active:true, heli:'EC135', region:'Innsbruck / Nordtirol'},
{name:'Krems OE-XVI', code:'C2', org:'Notarzt-Heli', orgClass:'oeamtc', lat:48.4306, lon:15.6100, active:false, heli:'EC135', region:'Gneixendorf / Waldviertel'},
{name:'Wr. Neustadt OE-XEL', code:'C3', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.8419, lon:16.2331, active:false, heli:'EC135', region:'Wiener Neustadt'},
{name:'Kitzbühel OE-XEY', code:'C4', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.3801, lon:12.3580, active:true, heli:'H145', region:'Reith bei Kitzbühel'},
{name:'Zams OE-XEZ', code:'C5', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.1597, lon:10.5933, active:true, heli:'H145', region:'Zams / Oberinntal'},
{name:'Salzburg OE-XVL', code:'C6', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.7950, lon:13.0044, active:false, heli:'H145', region:'Salzburg'},
{name:'Lienz OE-XER', code:'C7', org:'Notarzt-Heli', orgClass:'oeamtc', lat:46.8297, lon:12.7647, active:true, heli:'EC135', region:'Lienz / Osttirol'},
{name:'Nenzing OE-XEN', code:'C8', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.1828, lon:9.7072, active:true, heli:'H145', region:'Nenzing / Vorarlberg'},
{name:'Wien-Aspern OE-XVO', code:'C9', org:'Notarzt-Heli', orgClass:'oeamtc', lat:48.1172, lon:16.5025, active:false, heli:'EC135', region:'Wien'},
{name:'Linz OE-XVP', code:'C10', org:'Notarzt-Heli', orgClass:'oeamtc', lat:48.2331, lon:14.1875, active:false, heli:'EC135', region:'Linz'},
{name:'Klagenfurt OE-XEK', code:'C11', org:'Notarzt-Heli', orgClass:'oeamtc', lat:46.6503, lon:14.3372, active:false, heli:'EC135', region:'Klagenfurt'},
{name:'Graz OE-XEM', code:'C12', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.0094, lon:15.4106, active:false, heli:'EC135', region:'Graz'},
{name:'Niederöblarn OE-XEW', code:'C14', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.4756, lon:14.0042, active:false, heli:'H145', region:'Niederöblarn'},
{name:'Ybbsitz OE-XSA', code:'C15', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.9483, lon:14.8894, active:false, heli:'EC135', region:'Ybbsitz'},
{name:'Pinkafeld OE-XEU', code:'C16', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.2886, lon:16.2069, active:false, heli:'EC135', region:'Oberwart'},
{name:'Vomp OE-XAB', code:'C17', org:'Notarzt-Heli', orgClass:'oeamtc', lat:47.3500, lon:11.7000, active:false, heli:'EC135', region:'Vomp / Inntal'},
// ARA Flugrettung
{name:'Alpin 1 Reutte', code:'RK1', org:'ARA Flugrettung', orgClass:'ara', lat:47.4858, lon:10.7181, active:true, heli:'H145', region:'Reutte / Außerfern'},
{name:'Alpin 2 Fresach', code:'RK2', org:'ARA Flugrettung', orgClass:'ara', lat:46.7250, lon:13.6825, active:false, heli:'H145', region:'Fresach / Kärnten'},
{name:'Alpin 3 Ybbsitz', code:'RK3', org:'ARA Flugrettung', orgClass:'ara', lat:47.9500, lon:14.8900, active:false, heli:'EC135', region:'Ybbsitz / Mostviertel'},
{name:'Alpin 5 Salzburg', code:'RK5', org:'ARA Flugrettung', orgClass:'ara', lat:47.7933, lon:13.0100, active:false, heli:'H145', region:'Salzburg'},
{name:'Alpin 6 Linz', code:'RK6', org:'ARA Flugrettung', orgClass:'ara', lat:48.2333, lon:14.2000, active:false, heli:'EC135', region:'Linz'},
// Polizei/BMI Flugpolizei
{name:'Libelle Innsbruck', code:'LIB-I', org:'Flugpolizei BMI', orgClass:'police', lat:47.2583, lon:11.3556, active:true, heli:'AW169', region:'Innsbruck'},
{name:'Hohenems (Flugplatz)', code:'LIB-H', org:'Flugpolizei BMI', orgClass:'police', lat:47.38154, lon:9.69851, active:true, heli:'AW169', region:'Hohenems / Rheintal'},
{name:'Libelle Wien', code:'LIB-W', org:'Flugpolizei BMI', orgClass:'police', lat:48.1103, lon:16.5697, active:false, heli:'AW169', region:'Wien'},
{name:'Libelle Salzburg', code:'LIB-S', org:'Flugpolizei BMI', orgClass:'police', lat:47.7933, lon:13.0033, active:false, heli:'AW169', region:'Salzburg'},
{name:'Libelle Klagenfurt', code:'LIB-K', org:'Flugpolizei BMI', orgClass:'police', lat:46.6428, lon:14.3375, active:false, heli:'AW169', region:'Klagenfurt'},
{name:'Libelle Linz', code:'LIB-L', org:'Flugpolizei BMI', orgClass:'police', lat:48.2400, lon:14.1867, active:false, heli:'AW169', region:'Linz'},
{name:'Libelle Graz', code:'LIB-G', org:'Flugpolizei BMI', orgClass:'police', lat:47.0094, lon:15.4400, active:false, heli:'AW169', region:'Graz'},
// Bundesheer
{name:'Aigen im Ennstal', code:'BH-A', org:'Bundesheer', orgClass:'bh', lat:47.5236, lon:14.1350, active:false, heli:'AB212', region:'Aigen / Ennstal'},
{name:'Höttinger Au', code:'BH-I', org:'Bundesheer', orgClass:'bh', lat:47.2722, lon:11.3753, active:true, heli:'AB212', region:'Innsbruck'},
{name:'Langenlebarn', code:'BH-L', org:'Bundesheer', orgClass:'bh', lat:48.3239, lon:15.9836, active:false, heli:'AB212', region:'Langenlebarn / Tullnerfeld'},
// Krankenhaus-Landeplätze
{name:'LKH Innsbruck', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.2636, lon:11.3833, active:false},
{name:'LKH Feldkirch', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.2400, lon:9.5956, active:false},
{name:'AKH Wien', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:48.2206, lon:16.3453, active:false},
{name:'LKH Salzburg', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.7989, lon:13.0333, active:false},
{name:'LKH Graz', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.0808, lon:15.4678, active:false},
{name:'KH Kufstein', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.5836, lon:12.1647, active:false},
{name:'KH St. Johann', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.3528, lon:12.4247, active:false},
{name:'KH Zams', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.1575, lon:10.5917, active:false},
{name:'BKH Lienz', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:46.8300, lon:12.7700, active:false},
{name:'LKH Bregenz', code:'KH', org:'Krankenhaus', orgClass:'oeamtc', lat:47.4997, lon:9.7431, active:false},
];
function makeHeliPopup(b) {
var t = HELI_TYPES[b.heli];
if (!t) return '<strong>' + b.name + '</strong><br>' + b.org;
var bp = location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1);
return '<div class="quartett">'
+ '<div class="quartett-img"><img src="' + bp + t.img + '" alt="' + t.name + '"></div>'
+ '<div class="quartett-body">'
+ '<div class="quartett-name">' + b.code + ' — ' + b.name + '</div>'
+ '<div class="quartett-org">' + b.org + ' · ' + (b.region||'') + '</div>'
+ '<div class="quartett-stats">'
+ '<div class="quartett-stat"><div class="qs-label">Typ</div><div class="qs-val">' + t.name + '</div></div>'
+ '<div class="quartett-stat"><div class="qs-label">Max. Speed</div><div class="qs-val">' + t.maxSpeed + ' km/h</div></div>'
+ '<div class="quartett-stat"><div class="qs-label">Reichweite</div><div class="qs-val">' + t.range + ' km</div></div>'
+ '<div class="quartett-stat"><div class="qs-label">Gipfelhöhe</div><div class="qs-val">' + t.ceiling + ' m</div></div>'
+ '<div class="quartett-stat"><div class="qs-label">Triebwerke</div><div class="qs-val">' + t.engines + '</div></div>'
+ '<div class="quartett-stat"><div class="qs-label">Besatzung</div><div class="qs-val">' + t.crew + '</div></div>'
+ '</div>'
+ '<div class="quartett-fact">💡 ' + t.fact + '</div>'
+ (t.wiki ? '<div style="text-align:center;margin-top:6px"><a href="'+t.wiki+'" target="_blank" style="font-size:10px;color:#4a7c8a;text-decoration:none;font-weight:600">📖 Wikipedia →</a></div>' : '')
+ '</div></div>';
}
bases.forEach(function(b) {
var size = b.active ? 30 : 22;
var circleClass = b.active ? 'marker-circle heli-m' : 'marker-circle heli-m inactive';
L.marker([b.lat, b.lon], {
icon: L.divIcon({
className: '',
html: '<div class="' + circleClass + '">🚁</div>',
iconSize: [size, size],
iconAnchor: [size/2, size/2]
}),
title: b.name + ' (' + b.org + ')',
zIndexOffset: b.active ? 1000 : 0
}).addTo(map).bindPopup(makeHeliPopup(b), {maxWidth:280, className:'heli-popup'});
// Pill-Label
if (b.active) {
L.marker([b.lat, b.lon], {
icon: L.divIcon({
className: 'heli-pill ' + b.orgClass,
html: '<span class="pill-name">' + b.code + ' ' + b.name + '</span><span class="pill-org">' + b.org + '</span>',
iconSize: [160, 28],
iconAnchor: [-20, 14]
}),
interactive: false,
zIndexOffset: 900
}).addTo(map);
} else {
L.marker([b.lat, b.lon], {
icon: L.divIcon({
className: 'heli-pill-sm',
html: b.code,
iconSize: [40, 14],
iconAnchor: [-10, 7]
}),
interactive: false
}).addTo(map);
}
});
// === STÄDTE >30.000 EW + Bregenz ===
var cities = [
{name:'Wien', lat:48.2082, lon:16.3738, pop:1982097, capital:true, land:'Wien', fun:'Hat mehr Einwohner*innen als alle anderen Landeshauptstädte zusammen!', fact:'Wurde 5× zur lebenswertesten Stadt der Welt gewählt.', wiki:'https://de.wikipedia.org/wiki/Wien', img:'sims/heli/assets/cities/wien.png'},
{name:'Graz', lat:47.0707, lon:15.4395, pop:295424, capital:true, land:'Steiermark', fun:'Die Altstadt ist UNESCO-Weltkulturerbe.', fact:'Zweitgrößte Stadt Österreichs, Universitätsstadt mit über 60.000 Studierenden.', wiki:'https://de.wikipedia.org/wiki/Graz', img:'sims/heli/assets/cities/graz.png'},
{name:'Linz', lat:48.3069, lon:14.2858, pop:210118, capital:true, land:'Oberösterreich', fun:'War 2009 Europäische Kulturhauptstadt.', fact:'Wichtigster Industriestandort Österreichs (Stahl, Chemie).', wiki:'https://de.wikipedia.org/wiki/Linz', img:'sims/heli/assets/cities/linz.png'},
{name:'Salzburg', lat:47.8095, lon:13.0550, pop:157745, capital:true, land:'Salzburg', fun:'Geburtsstadt von Wolfgang Amadeus Mozart (1756).', fact:'Die Altstadt gehört zum UNESCO-Weltkulturerbe.', wiki:'https://de.wikipedia.org/wiki/Salzburg', img:'sims/heli/assets/cities/salzburg.png'},
{name:'Innsbruck', lat:47.2654, lon:11.3928, pop:130585, capital:true, land:'Tirol', fun:'Hat schon 3× Olympische Winterspiele ausgerichtet!', fact:'Liegt auf 574 m im Inntal, umgeben von Bergen bis 2.300 m.', wiki:'https://de.wikipedia.org/wiki/Innsbruck', img:'sims/heli/assets/cities/innsbruck.png'},
{name:'Klagenfurt', lat:46.6247, lon:14.3050, pop:103671, capital:true, land:'Kärnten', fun:'Am Wörthersee — einem der wärmsten Badeseen der Alpen.', fact:'Südlichste Landeshauptstadt Österreichs.', wiki:'https://de.wikipedia.org/wiki/Klagenfurt_am_Wörthersee', img:'sims/heli/assets/cities/klagenfurt.png'},
{name:'Villach', lat:46.6111, lon:13.8558, pop:65127, capital:false, land:'Kärnten', fun:'Hat einen eigenen Fasching — den Villacher Fasching!', fact:'Zweitgrößte Stadt Kärntens, wichtiger Verkehrsknotenpunkt Richtung Italien/Slowenien.', wiki:'https://de.wikipedia.org/wiki/Villach', img:'sims/heli/assets/cities/villach.png'},
{name:'Wels', lat:48.1575, lon:14.0289, pop:63539, capital:false, land:'Oberösterreich', fun:'Hier findet die größte Agrarmesse Österreichs statt.', fact:'Zweitgrößte Stadt Oberösterreichs.', wiki:'https://de.wikipedia.org/wiki/Wels_(Stadt)', img:'sims/heli/assets/cities/wels.png'},
{name:'St. Pölten', lat:48.2047, lon:15.6256, pop:57169, capital:true, land:'Niederösterreich', fun:'Jüngste Landeshauptstadt — erst seit 1986!', fact:'Vorher war Wien auch Hauptstadt von Niederösterreich.', wiki:'https://de.wikipedia.org/wiki/St._Pölten', img:'sims/heli/assets/cities/stpoelten.png'},
{name:'Dornbirn', lat:47.4136, lon:9.7424, pop:51222, capital:false, land:'Vorarlberg', fun:'Größte Stadt Vorarlbergs — nicht Bregenz!', fact:'Wichtiger Messestandort und Wirtschaftszentrum im Rheintal.', wiki:'https://de.wikipedia.org/wiki/Dornbirn', img:'sims/heli/assets/cities/dornbirn.png'},
{name:'Wiener Neustadt', lat:47.8133, lon:16.2467, pop:48519, capital:false, land:'Niederösterreich', fun:'Hier wurde die erste Militärakademie der Welt gegründet (1752).', fact:'Wichtiger Standort des Bundesheers.', wiki:'https://de.wikipedia.org/wiki/Wiener_Neustadt', img:'sims/heli/assets/cities/wiener_neustadt.png'},
{name:'Steyr', lat:48.0386, lon:14.4214, pop:38572, capital:false, land:'Oberösterreich', fun:'Die Altstadt sieht aus wie ein Märchen — mit Flüssen und mittelalterlichen Häusern.', fact:'Berühmt für Stahl und Waffen seit dem Mittelalter.', wiki:'https://de.wikipedia.org/wiki/Steyr', img:'sims/heli/assets/cities/steyr.png'},
{name:'Feldkirch', lat:47.2376, lon:9.5982, pop:35793, capital:false, land:'Vorarlberg', fun:'Hat eine echte mittelalterliche Burg — die Schattenburg.', fact:'Grenzstadt zur Schweiz und Liechtenstein.', wiki:'https://de.wikipedia.org/wiki/Feldkirch', img:'sims/heli/assets/cities/feldkirch.png'},
{name:'Bregenz', lat:47.5026, lon:9.7473, pop:29620, capital:true, land:'Vorarlberg', fun:'Die Seebühne auf dem Bodensee ist die größte Freilichtbühne der Welt!', fact:'Kleinste Landeshauptstadt Österreichs.', wiki:'https://de.wikipedia.org/wiki/Bregenz', img:'sims/heli/assets/cities/bregenz.png'},
{name:'Leoben', lat:47.3765, lon:15.0910, pop:32768, capital:false, land:'Steiermark', fun:'Hier wird an der Montanuniversität Bergbau studiert.', fact:'War jahrhundertelang das Zentrum des steirischen Erzbergbaus.', wiki:'https://de.wikipedia.org/wiki/Leoben', img:'sims/heli/assets/cities/leoben.png'},
{name:'Eisenstadt', lat:47.8456, lon:16.5189, pop:15073, capital:true, land:'Burgenland', fun:'Hier lebte der Komponist Joseph Haydn über 30 Jahre.', fact:'Kleinste Landeshauptstadt nach Einwohnern. Schloss Esterházy ist das Wahrzeichen.', wiki:'https://de.wikipedia.org/wiki/Eisenstadt', img:'sims/heli/assets/cities/eisenstadt.png'},
];
cities.forEach(function(c) {
var dotSize = c.capital ? 30 : 26;
var cls = '';
// Punkt
var capitalBadge = c.capital ? '<div style="display:inline-block;background:#e8833a;color:#fff;font-size:9px;font-weight:700;padding:2px 8px;border-radius:99px;margin-bottom:6px">★ Landeshauptstadt' + (c.land ? ' von ' + c.land : '') + '</div>' : (c.land ? '<div style="font-size:10px;color:#8a8a8a;margin-bottom:4px">' + c.land + '</div>' : '');
L.marker([c.lat, c.lon], {
icon: L.divIcon({
className: cls,
html: '<div class="marker-circle' + (c.capital ? ' capital' : '') + '">🏙️</div>',
iconSize: [dotSize, dotSize],
iconAnchor: [dotSize/2, dotSize/2]
}),
zIndexOffset: c.capital ? 500 : 100
}).addTo(map).bindPopup(
'<div style="font-family:Inter,sans-serif;width:260px;padding:0">'
+ (c.img ? '<div style="height:130px;overflow:hidden;border-radius:8px 8px 0 0;margin:-12px -12px 8px"><img src="'+c.img+'" style="width:100%;height:100%;object-fit:cover"></div>' : '')
+ '<div style="text-align:center">'
+ capitalBadge
+ '<div style="font-size:16px;font-weight:900;color:#1f4e5a;margin-bottom:2px">' + c.name + '</div>'
+ '<div style="font-size:22px;font-weight:900;color:#4a7c8a">' + c.pop.toLocaleString() + '</div>'
+ '<div style="font-size:9px;color:#8a8a8a;margin-bottom:8px">Einwohner*innen</div>'
+ '</div>'
+ (c.fun ? '<div style="font-size:11px;color:#2d5434;background:rgba(90,138,94,.08);border-radius:6px;padding:6px 8px;margin-bottom:6px">🎯 ' + c.fun + '</div>' : '')
+ (c.fact ? '<div style="font-size:11px;color:#4a4a4a;line-height:1.4;margin-bottom:6px">📚 ' + c.fact + '</div>' : '')
+ (c.wiki ? '<div style="text-align:center"><a href="'+c.wiki+'" target="_blank" style="font-size:10px;color:#4a7c8a;text-decoration:none;font-weight:600">📖 Wikipedia →</a></div>' : '')
+ '</div>'
, {maxWidth:260});
// Pill
var popStr = c.pop >= 1000000 ? (c.pop/1000000).toFixed(1) + ' Mio' : Math.round(c.pop/1000) + 'k';
var star = c.capital ? ' ★' : '';
L.marker([c.lat, c.lon], {
icon: L.divIcon({
className: 'city-pill' + (c.capital ? ' capital' : ''),
html: c.name + star + '<span class="city-pop">' + popStr + '</span>',
iconSize: [140, 22],
iconAnchor: [-16, 11]
}),
interactive: false,
zIndexOffset: c.capital ? 400 : 50
}).addTo(map);
});
// === BERGGIPFEL ===
var peaks = [
{name:'Großglockner', lat:47.0742, lon:12.6947, ele:3798, fact:'Höchster Berg Österreichs! Die berühmte Großglockner Hochalpenstraße führt bis auf 2.504 m.', wiki:'https://de.wikipedia.org/wiki/Großglockner'},
{name:'Wildspitze', lat:46.8853, lon:10.8672, ele:3768, fact:'Höchster Berg Tirols und zweithöchster Österreichs. Hat den größten Gletscher der Ostalpen.', wiki:'https://de.wikipedia.org/wiki/Wildspitze'},
{name:'Weißkugel', lat:46.8439, lon:10.7833, ele:3738, fact:'Dritthöchster Berg Österreichs, direkt an der Grenze zu Italien.', wiki:'https://de.wikipedia.org/wiki/Weißkugel'},
{name:'Großvenediger', lat:47.1092, lon:12.3464, ele:3657, fact:'Wird „Weltalte Majestät" genannt. Einer der schönsten Gletscherberge der Alpen.', wiki:'https://de.wikipedia.org/wiki/Großvenediger'},
{name:'Similaun', lat:46.7578, lon:10.8853, ele:3599, fact:'Hier wurde 1991 die Gletschermumie „Ötzi" gefunden — 5.300 Jahre alt!', wiki:'https://de.wikipedia.org/wiki/Similaun'},
{name:'Zuckerhütl', lat:46.9833, lon:11.1167, ele:3505, fact:'Höchster Berg der Stubaier Alpen. Beliebtes Ziel für Skitouren.', wiki:'https://de.wikipedia.org/wiki/Zuckerhütl'},
{name:'Piz Buin', lat:46.8444, lon:10.1186, ele:3312, fact:'Höchster Berg Vorarlbergs. Die Sonnencreme-Marke wurde nach ihm benannt!', wiki:'https://de.wikipedia.org/wiki/Piz_Buin'},
{name:'Zugspitze', lat:47.4211, lon:10.9856, ele:2962, fact:'Höchster Berg Deutschlands, direkt an der Grenze zu Österreich. Seilbahn fährt bis zum Gipfel.', wiki:'https://de.wikipedia.org/wiki/Zugspitze'},
{name:'Dachstein', lat:47.4753, lon:13.6061, ele:2995, fact:'Höchster Berg der Steiermark. Hat eine Eishöhle und den berühmten Skywalk.', wiki:'https://de.wikipedia.org/wiki/Hoher_Dachstein'},
{name:'Valluga', lat:47.1567, lon:10.2117, ele:2809, fact:'Hausberg von St. Anton am Arlberg, eines der berühmtesten Skigebiete der Welt.', wiki:'https://de.wikipedia.org/wiki/Valluga'},
{name:'Parseierspitze', lat:47.0825, lon:10.5086, ele:3036, fact:'Höchster Berg der Lechtaler Alpen. Nur für erfahrene Bergsteiger*innen!', wiki:'https://de.wikipedia.org/wiki/Parseierspitze'},
{name:'Brenner', lat:47.0422, lon:11.5067, ele:1370, fact:'Der wichtigste Alpenpass zwischen Österreich und Italien. Schon die Römer nutzten ihn.', wiki:'https://de.wikipedia.org/wiki/Brennerpass'},
{name:'Patscherkofel', lat:47.2083, lon:11.4611, ele:2246, fact:'Hausberg von Innsbruck. Hier fanden Olympische Skirennen statt.', wiki:'https://de.wikipedia.org/wiki/Patscherkofel'},
{name:'Arlberg', lat:47.1297, lon:10.2119, ele:1793, fact:'Grenze zwischen Tirol und Vorarlberg. Wiege des alpinen Skilaufs.', wiki:'https://de.wikipedia.org/wiki/Arlberg'},
];
peaks.forEach(function(p) {
L.marker([p.lat, p.lon], {
icon: L.divIcon({
className: '',
html: '<div class="marker-circle peak">🏔️</div>',
iconSize: [24, 24],
iconAnchor: [12, 12]
})
}).addTo(map).bindPopup(
'<div style="font-family:Inter,sans-serif;width:220px;padding:2px">'
+ '<div style="text-align:center">'
+ '<div style="font-size:2.5rem">🏔️</div>'
+ '<div style="font-size:15px;font-weight:900;color:#3a6b3e">' + p.name + '</div>'
+ '<div style="font-size:24px;font-weight:900;color:#1f4e5a">' + p.ele.toLocaleString() + ' m</div>'
+ '</div>'
+ (p.fact ? '<div style="font-size:11px;color:#4a4a4a;line-height:1.4;margin:8px 0 4px;background:rgba(90,138,94,.06);border-radius:6px;padding:6px 8px">🌿 ' + p.fact + '</div>' : '')
+ (p.wiki ? '<div style="text-align:center"><a href="'+p.wiki+'" target="_blank" style="font-size:10px;color:#4a7c8a;text-decoration:none;font-weight:600">📖 Wikipedia →</a></div>' : '')
+ '</div>'
, {maxWidth:240});
L.marker([p.lat, p.lon], {
icon: L.divIcon({
className: 'peak-pill',
html: p.name + '<span class="peak-ele">' + p.ele.toLocaleString() + ' m</span>',
iconSize: [140, 20],
iconAnchor: [-14, 10]
}),
interactive: false
}).addTo(map);
});
// === DB-SYNC: Koordinaten aus der Datenbank laden und Marker aktualisieren ===
(async function() {
try {
var bp = location.pathname.replace(/\/[^\/]*$/, '');
var resp = await fetch(bp + '/api/waypoints');
var dbWaypoints = await resp.json();
if (!Array.isArray(dbWaypoints)) return;
// Index nach wp_key
var dbMap = {};
dbWaypoints.forEach(function(w) { dbMap[w.wp_key] = {lat: parseFloat(w.lat), lon: parseFloat(w.lon), name: w.name}; });
// Alle Leaflet-Marker durchgehen und Positionen updaten
map.eachLayer(function(layer) {
if (layer.getLatLng && layer.options && layer.options.title) {
// Versuche den Marker anhand des Titels zu identifizieren
// (Heli-Basen haben title gesetzt)
}
});
// Einfacher: Seite neu rendern wenn DB-Daten signifikant abweichen
// Für jetzt: Konsolenwarnung bei Abweichungen
var fixes = 0;
bases.forEach(function(b) {
var key = b.name.toLowerCase().replace(/[^a-z0-9]/g,'');
// Versuche verschiedene Key-Matches
for (var dbKey in dbMap) {
if (dbMap[dbKey].name === b.name || dbMap[dbKey].name.indexOf(b.code) !== -1) {
var dLat = Math.abs(b.lat - dbMap[dbKey].lat);
var dLon = Math.abs(b.lon - dbMap[dbKey].lon);
if (dLat > 0.001 || dLon > 0.001) {
console.warn('DB-Korrektur: ' + b.name + ' (' + b.lat + ',' + b.lon + ') → (' + dbMap[dbKey].lat + ',' + dbMap[dbKey].lon + ')');
fixes++;
}
break;
}
}
});
// Marker-Positionen korrigieren: alle Layer entfernen und neu setzen
if (Object.keys(dbMap).length > 0) {
// Alle bestehenden Marker entfernen
var layersToRemove = [];
map.eachLayer(function(layer) {
if (layer.getLatLng && !(layer instanceof L.TileLayer)) {
layersToRemove.push(layer);
}
});
layersToRemove.forEach(function(l) { map.removeLayer(l); });
// Basen neu setzen mit DB-Koordinaten
bases.forEach(function(b) {
// Finde passenden DB-Eintrag
for (var dbKey in dbMap) {
if (dbMap[dbKey].name.indexOf(b.code) !== -1 || dbMap[dbKey].name === b.name) {
b.lat = dbMap[dbKey].lat;
b.lon = dbMap[dbKey].lon;
break;
}
}
var size = b.active ? 30 : 22;
var circleClass = b.active ? 'marker-circle heli-m' : 'marker-circle heli-m inactive';
L.marker([b.lat, b.lon], {
icon: L.divIcon({className:'', html:'<div class="'+circleClass+'">🚁</div>', iconSize:[size,size], iconAnchor:[size/2,size/2]}),
title: b.name+' ('+b.org+')', zIndexOffset: b.active ? 1000 : 0
}).addTo(map).bindPopup(makeHeliPopup(b), {maxWidth:280, className:'heli-popup'});
if (b.active) {
L.marker([b.lat, b.lon], {
icon: L.divIcon({className:'heli-pill '+b.orgClass, html:'<span class="pill-name">'+b.code+' '+b.name+'</span><span class="pill-org">'+b.org+'</span>', iconSize:[160,28], iconAnchor:[-20,14]}),
interactive:false, zIndexOffset:900
}).addTo(map);
}
});
// Städte neu setzen
cities.forEach(function(c) {
var dbKey = c.name.toLowerCase().replace(/[^a-zäöüß]/g,'');
for (var k in dbMap) {
if (dbMap[k].name.toLowerCase().replace(/[^a-zäöüß]/g,'') === dbKey) {
c.lat = dbMap[k].lat; c.lon = dbMap[k].lon; break;
}
}
var dotSize = c.capital ? 30 : 26;
var capitalBadge = c.capital ? '<div style="display:inline-block;background:#e8833a;color:#fff;font-size:9px;font-weight:700;padding:2px 8px;border-radius:99px;margin-bottom:6px">★ Landeshauptstadt'+(c.land?' von '+c.land:'')+'</div>' : (c.land?'<div style="font-size:10px;color:#8a8a8a;margin-bottom:4px">'+c.land+'</div>':'');
L.marker([c.lat, c.lon], {
icon: L.divIcon({className:'', html:'<div class="marker-circle'+(c.capital?' capital':'')+'">🏙️</div>', iconSize:[dotSize,dotSize], iconAnchor:[dotSize/2,dotSize/2]}),
zIndexOffset: c.capital ? 500 : 100
}).addTo(map).bindPopup(
'<div style="font-family:Inter,sans-serif;width:260px;padding:0">'
+(c.img?'<div style="height:130px;overflow:hidden;border-radius:8px 8px 0 0;margin:-12px -12px 8px"><img src="'+c.img+'" style="width:100%;height:100%;object-fit:cover"></div>':'')
+'<div style="text-align:center">'+capitalBadge
+'<div style="font-size:16px;font-weight:900;color:#1f4e5a;margin-bottom:2px">'+c.name+'</div>'
+'<div style="font-size:22px;font-weight:900;color:#4a7c8a">'+c.pop.toLocaleString()+'</div>'
+'<div style="font-size:9px;color:#8a8a8a;margin-bottom:8px">Einwohner*innen</div></div>'
+(c.fun?'<div style="font-size:11px;color:#2d5434;background:rgba(90,138,94,.08);border-radius:6px;padding:6px 8px;margin-bottom:6px">🎯 '+c.fun+'</div>':'')
+(c.fact?'<div style="font-size:11px;color:#4a4a4a;line-height:1.4;margin-bottom:6px">📚 '+c.fact+'</div>':'')
+(c.wiki?'<div style="text-align:center"><a href="'+c.wiki+'" target="_blank" style="font-size:10px;color:#4a7c8a;text-decoration:none;font-weight:600">📖 Wikipedia →</a></div>':'')
+'</div>', {maxWidth:260});
var star = c.capital ? ' ★' : '';
var popStr = c.pop >= 1000000 ? (c.pop/1000000).toFixed(1)+' Mio' : Math.round(c.pop/1000)+'k';
L.marker([c.lat, c.lon], {
icon: L.divIcon({className:'city-pill'+(c.capital?' capital':''), html:c.name+star+'<span class="city-pop">'+popStr+'</span>', iconSize:[140,22], iconAnchor:[-16,11]}),
interactive:false, zIndexOffset: c.capital ? 400 : 50
}).addTo(map);
});
// Berge neu setzen
peaks.forEach(function(p) {
L.marker([p.lat, p.lon], {
icon: L.divIcon({className:'', html:'<div class="marker-circle peak">🏔️</div>', iconSize:[24,24], iconAnchor:[12,12]})
}).addTo(map).bindPopup(
'<div style="font-family:Inter,sans-serif;width:220px;padding:2px"><div style="text-align:center">'
+'<div style="font-size:2.5rem">🏔️</div><div style="font-size:15px;font-weight:900;color:#3a6b3e">'+p.name+'</div>'
+'<div style="font-size:24px;font-weight:900;color:#1f4e5a">'+p.ele.toLocaleString()+' m</div></div>'
+(p.fact?'<div style="font-size:11px;color:#4a4a4a;line-height:1.4;margin:8px 0 4px;background:rgba(90,138,94,.06);border-radius:6px;padding:6px 8px">🌿 '+p.fact+'</div>':'')
+(p.wiki?'<div style="text-align:center"><a href="'+p.wiki+'" target="_blank" style="font-size:10px;color:#4a7c8a;text-decoration:none;font-weight:600">📖 Wikipedia →</a></div>':'')
+'</div>', {maxWidth:240});
L.marker([p.lat, p.lon], {
icon: L.divIcon({className:'peak-pill', html:p.name+'<span class="peak-ele">'+p.ele.toLocaleString()+' m</span>', iconSize:[140,20], iconAnchor:[-14,10]}),
interactive:false
}).addTo(map);
});
console.log('Karte mit DB-Koordinaten aktualisiert (' + Object.keys(dbMap).length + ' Waypoints)');
}
} catch(e) { console.warn('DB-Sync fehlgeschlagen:', e); }
})();
</script>
</body>
</html>
@@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>toDelete/ · Entwurfswerkzeug</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;background:#f5f2eb;color:#2a2a2a;line-height:1.5;padding:24px;max-width:1100px;margin:0 auto}
h1{color:#1f4e5a;font-size:1.5rem;margin-bottom:4px}
.sub{font-size:.82rem;color:#8a8a8a;margin-bottom:14px}
h2{color:#1f4e5a;font-size:1.05rem;margin:20px 0 8px;padding-bottom:4px;border-bottom:2px solid #4a7c8a}
table{width:100%;border-collapse:collapse;font-size:.86rem;background:#fff;border-radius:6px;overflow:hidden}
th,td{text-align:left;padding:8px 12px;border-bottom:1px solid #e0ddd4;vertical-align:top}
th{background:#9c4d2e;color:#fff;font-weight:600;font-size:.74rem;text-transform:uppercase;letter-spacing:.04em}
tr:last-child td{border-bottom:none}
code{background:#f5f2eb;padding:2px 6px;border-radius:3px;font-family:'JetBrains Mono',Consolas,monospace;font-size:.85em}
a{color:#1f4e5a}
.danger-box{background:#fde0db;border-left:4px solid #c85c4a;padding:10px 14px;margin:12px 0;border-radius:0 6px 6px 0;font-size:.86rem}
/* einheitliches Entwurfswerkzeug-Menü */
.top-nav{background:#fff;border:1px solid #e0ddd4;border-radius:8px;padding:10px 14px;margin-bottom:18px;font-size:.86rem;display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.top-nav strong{color:#1f4e5a}
.top-nav a{text-decoration:none;padding:5px 11px;border-radius:6px;color:#1f4e5a}
.top-nav a:hover{background:#f5f2eb}
.top-nav a.active{background:#9c4d2e;color:#fff}
</style>
</head>
<body>
<nav class="top-nav">
<strong>🛠 Entwurfswerkzeug</strong>
<a href="../index.html">Übersicht</a>
<a href="../../entwurf.html">Entwurf</a>
<a href="../../review.html">Review</a>
<a href="../accounts.html">🔑 Accounts</a>
<a href="../standards.html">📐 Standards</a>
<a href="../legacy/" style="color:#6b6448">📂 legacy/</a>
<a href="index.html" class="active">📂 toDelete/</a>
<a href="../../../App/admin-sitemap.html" target="_blank">📊 Sitemap</a>
</nav>
<h1>📂 toDelete/</h1>
<p class="sub">10 Top-Level-Pages aus der Prod entfernt (Sweep 2026-06-08). Warten auf finale Lösch-Freigabe.</p>
<div class="danger-box">
<b>Wenn du eines davon brauchst:</b> einfach zurück mit
<code>git mv .dontDeploy/entwurfswerkzeug/toDelete/&lt;datei&gt; App/&lt;datei&gt;</code> — Historie bleibt erhalten.
</div>
<h2>📄 Lösch-Kandidaten</h2>
<table>
<thead><tr><th>Datei</th><th>Was es war</th><th>Status</th></tr></thead>
<tbody>
<tr><td><a href="dashboard.html"><code>dashboard.html</code></a></td><td>Demo-Mockup mit „alle Daten erfunden"-Banner</td><td>Ersetzt durch <code>App/teacher.html</code>. Endgültig löschbar.</td></tr>
<tr><td><a href="game.html"><code>game.html</code></a></td><td>Klima-2D Standalone vor Wrapper-System</td><td>Wrapper <code>pages/klima.php</code> ist Production. Löschbar.</td></tr>
<tr><td><a href="game-3d.html"><code>game-3d.html</code></a></td><td>Klima-3D Standalone</td><td>Wrapper <code>pages/klima-3d.php</code> ist Production. Löschbar.</td></tr>
<tr><td><a href="fluss.html"><code>fluss.html</code></a></td><td>Flussmanagement Standalone</td><td>Wrapper <code>pages/fluss.php</code> ist Production. Löschbar.</td></tr>
<tr><td><a href="heli.html"><code>heli.html</code></a></td><td>Heli-Navigation Standalone</td><td>Wrapper <code>pages/heli.php</code> ist Production. Löschbar.</td></tr>
<tr><td><a href="stadt.html"><code>stadt.html</code></a></td><td>Stadt-Simulation Standalone (nie fertig)</td><td>Stadt noch in Entwurf — beim Reaktivieren ggf. zurückholen.</td></tr>
<tr><td><a href="erdbeben.html"><code>erdbeben.html</code></a></td><td>Konzept-Stub Erdbeben-Sim</td><td>Konzept nie umgesetzt. Löschbar.</td></tr>
<tr><td><a href="energiemix.html"><code>energiemix.html</code></a></td><td>Konzept-Stub Energiemix</td><td>Ersetzt durch <code>energiemanager</code>. Löschbar.</td></tr>
<tr><td><a href="lieferketten.html"><code>lieferketten.html</code></a></td><td>Konzept-Stub Lieferketten</td><td>Konzept nie umgesetzt. Löschbar.</td></tr>
<tr><td><a href="regenwald.html"><code>regenwald.html</code></a></td><td>Konzept-Stub Regenwald</td><td>Konzept nie umgesetzt. Löschbar.</td></tr>
</tbody>
</table>
<footer style="margin:24px 0;text-align:center;font-size:.75rem;color:#8a8a8a">
Liegt unter <code>.dontDeploy/entwurfswerkzeug/toDelete/</code> · wird nicht deployed.
</footer>
</body>
</html>
@@ -0,0 +1,545 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Lieferketten-Planer*in</title>
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="stylesheet" href="assets/fonts/inter.css">
<style>
*, *::before, *::after { margin:0; padding:0; box-sizing:border-box; }
:root {
--bg: #f7f6f3;
--text: #1a1a1a; --text2: #4a4a4a; --text3: #8a8a8a;
--fjord: #4a7c8a; --fjord-l: #dae8ec; --fjord-d: #3a6470;
--moss: #5a8a5e; --moss-l: #dceadd;
--sand: #c4a35a; --sand-l: #f2eacc;
--coral: #c07a6b; --coral-l: #f2ddd8;
--danger: #b04a3a;
--glass-bg: rgba(255,255,255,.85);
--glass-bg2: rgba(255,255,255,.92);
--glass-bord: rgba(255,255,255,.55);
--glass-shadow: 0 8px 32px rgba(20,40,60,.18);
--topbar-h: 2.8rem;
--side-w: 220px;
--pad: .65rem;
}
html, body { height:100%; overflow:hidden; }
body { font-family:'Inter',system-ui,sans-serif; background:#dceaf0; color:var(--text); line-height:1.5; }
#canvas-wrap { position:fixed; inset:0; z-index:0; }
/* === FLOATING PANELS === */
.floating {
position:fixed; z-index:5;
backdrop-filter:blur(14px) saturate(140%); -webkit-backdrop-filter:blur(14px) saturate(140%);
background:var(--glass-bg); border:1px solid var(--glass-bord);
box-shadow:var(--glass-shadow); border-radius:14px;
}
/* === TOPBAR === */
.topbar {
top:0; left:0; right:0;
padding:.35rem .7rem;
display:flex; justify-content:space-between; align-items:center; gap:.8rem;
border-radius:0; border-bottom:1px solid rgba(0,0,0,.08);
}
.topbar .brand {
display:flex; align-items:center; gap:.5rem; font-weight:800; font-size:.92rem;
color:var(--fjord); text-decoration:none; white-space:nowrap;
}
.topbar .brand img { width:24px; height:24px; border-radius:6px; }
.topbar-controls { display:flex; gap:.5rem; align-items:center; }
.icon-btn {
width:32px; height:32px; border-radius:8px;
border:1px solid rgba(0,0,0,.08); background:rgba(255,255,255,.55);
color:var(--text2); font-size:.95rem; cursor:pointer;
display:flex; align-items:center; justify-content:center; padding:0;
text-decoration:none; transition:all .15s;
}
.icon-btn:hover { background:var(--fjord-l); border-color:var(--fjord); color:var(--fjord); }
.ctrl-group {
display:flex; gap:.2rem; padding:.2rem;
background:rgba(255,255,255,.45); border-radius:9px;
border:1px solid rgba(0,0,0,.06);
}
.ctrl-group .icon-btn {
border:none; background:transparent;
width:30px; height:30px; font-size:.8rem; font-weight:700;
}
.ctrl-group .icon-btn:hover { background:rgba(0,0,0,.06); }
.ctrl-group .icon-btn.active { background:var(--fjord); color:#fff; }
.topbar-sep { width:1px; height:24px; background:rgba(0,0,0,.08); }
/* === STATUS-PANEL (LINKS) === */
.panel-h {
font-size:.58rem; font-weight:800; color:var(--fjord);
text-transform:uppercase; letter-spacing:.07em; margin-bottom:.4rem;
}
.panel-h-sub { margin-top:.9rem; padding-top:.6rem; border-top:1px solid rgba(0,0,0,.08); }
.panel-status {
top: calc(var(--topbar-h) + var(--pad));
left: var(--pad);
width: var(--side-w);
padding: .6rem .75rem;
bottom: var(--pad);
overflow-y: auto;
}
.resource {
display:flex; align-items:flex-start; gap:.5rem; padding:.3rem .1rem;
border-bottom:1px solid rgba(0,0,0,.06);
}
.resource:last-child { border-bottom:none; }
.resource-icon { font-size:1.15rem; width:22px; text-align:center; padding-top:.1rem; }
.resource-info { flex:1; min-width:0; }
.resource-name { font-size:.58rem; color:var(--text3); font-weight:500; line-height:1.1; }
.resource-val { font-size:.85rem; font-weight:800; color:var(--text); line-height:1.2; }
/* === GOALS === */
.goal { padding:.3rem 0; border-bottom:1px solid rgba(0,0,0,.06); }
.goal:last-child { border:0; }
.goal-row { display:flex; align-items:center; gap:.35rem; font-size:.7rem; }
.goal-check {
width:12px; height:12px; border:1.5px solid var(--text3);
border-radius:3px; display:flex; align-items:center; justify-content:center;
font-size:.52rem; flex-shrink:0;
}
.goal-check.done { background:var(--moss); border-color:var(--moss); color:#fff; }
.goal-bar { height:3px; background:rgba(0,0,0,.08); border-radius:2px; margin-top:.2rem; overflow:hidden; }
.goal-bar-fill { height:100%; background:var(--moss); transition:width .3s; }
/* === BESTELL-FORMULAR (RECHTS) === */
.panel-order {
top: calc(var(--topbar-h) + var(--pad));
right: var(--pad);
bottom: var(--pad);
width: 300px;
padding: .85rem 1rem 1rem;
overflow-y: auto;
}
.order-section { margin-bottom: 1rem; }
.order-section h4 {
font-size: .65rem; font-weight: 800; color: var(--fjord);
text-transform: uppercase; letter-spacing: .07em;
margin-bottom: .35rem;
}
.order-options { display: flex; flex-direction: column; gap: .25rem; }
.order-option {
display: flex; align-items: center; gap: .4rem;
padding: .35rem .5rem; border-radius: 8px;
background: rgba(255,255,255,.55);
border: 1.5px solid rgba(0,0,0,.06);
cursor: pointer; transition: all .12s;
font-family: inherit; text-align: left;
}
.order-option:hover { border-color: var(--fjord); background: var(--fjord-l); }
.order-option.selected { border-color: var(--moss); background: var(--moss-l); }
.oo-flag { font-size: .9rem; flex-shrink: 0; }
.oo-info { flex: 1; min-width: 0; }
.oo-name { font-size: .68rem; font-weight: 700; line-height: 1.15; }
.oo-stats {
font-size: .55rem; color: var(--text3); line-height: 1.2;
font-variant-numeric: tabular-nums;
}
.oo-stats .oo-stat { display: inline-block; margin-right: .35rem; }
.oo-stat-good { color: var(--moss); }
.oo-stat-bad { color: var(--danger); }
.transport-row { display: flex; gap: .25rem; }
.transport-btn {
flex: 1; padding: .4rem .25rem; border-radius: 8px;
background: rgba(255,255,255,.55); border: 1.5px solid rgba(0,0,0,.06);
cursor: pointer; font-family: inherit;
display: flex; flex-direction: column; align-items: center; gap: .1rem;
transition: all .12s;
}
.transport-btn:hover { border-color: var(--fjord); background: var(--fjord-l); }
.transport-btn.selected { border-color: var(--moss); background: var(--moss-l); }
.transport-btn .tb-emoji { font-size: 1.1rem; }
.transport-btn .tb-name { font-size: .55rem; font-weight: 700; }
.order-summary {
margin-top: .8rem; padding: .65rem .75rem;
background: rgba(0,0,0,.04); border-radius: 9px;
font-size: .68rem; line-height: 1.5;
}
.order-summary .os-row {
display: flex; justify-content: space-between;
font-variant-numeric: tabular-nums;
}
.order-summary .os-row strong { font-weight: 800; }
.os-good { color: var(--moss); }
.os-bad { color: var(--danger); }
.order-btn {
margin-top: .7rem; width: 100%;
padding: .65rem; border: none; border-radius: 9px;
background: var(--fjord); color: #fff;
font-weight: 800; font-size: .85rem; cursor: pointer;
font-family: inherit; transition: all .15s;
}
.order-btn:hover { background: var(--fjord-d); }
.order-btn:disabled { background: #c0c0c0; cursor: not-allowed; }
/* === EVENTS-STRIP (unten links neben dem Status) === */
.panel-events-strip {
position: fixed;
left: calc(var(--side-w) + var(--pad) * 2);
bottom: var(--pad);
width: 280px;
max-height: 90px;
padding: .35rem .55rem .45rem;
overflow: hidden;
cursor: pointer;
transition: max-height .38s cubic-bezier(.22,1,.36,1);
z-index: 12;
display: flex; flex-direction: column; gap: .25rem;
}
.panel-events-strip.open {
max-height: calc(100vh - var(--topbar-h) - 4 * var(--pad));
box-shadow: 0 18px 50px rgba(20,40,60,.28);
cursor: zoom-out;
}
.events-strip-header {
display: flex; justify-content: space-between; align-items: center;
font-size: .58rem; font-weight: 800; color: var(--fjord);
text-transform: uppercase; letter-spacing: .07em;
flex-shrink: 0;
}
.esh-toggle { transition: transform .35s ease-out; font-size: .65rem; color: var(--text3); }
.panel-events-strip.open .esh-toggle { transform: rotate(180deg); }
.events-strip-list {
overflow-y: auto;
flex: 1; min-height: 0;
}
.panel-events-strip:not(.open) .events-strip-list .event:not(:first-child) {
display: none;
}
.panel-events-strip:not(.open) .events-strip-list .event {
margin-bottom: 0;
}
.event {
padding:.3rem .45rem; border-radius:7px; margin-bottom:.25rem;
font-size:.65rem; line-height:1.3;
display:flex; gap:.3rem; align-items:flex-start;
}
.event-text { flex:1; }
.event-info { background:rgba(0,0,0,.05); color:var(--text2); }
.event-success { background:var(--moss-l); color:var(--moss); }
.event-warning { background:var(--sand-l); color:var(--sand); }
.event-danger { background:var(--coral-l); color:var(--danger); }
/* === TUTORIAL === */
.tutorial-overlay {
position:fixed; inset:0; background:rgba(15,30,40,.85);
backdrop-filter:blur(8px); display:flex; align-items:center;
justify-content:center; z-index:50; padding:1.5rem;
}
.tutorial-card {
background:#fff; border-radius:18px; padding:1.6rem 1.9rem;
max-width:520px; box-shadow:0 30px 80px rgba(0,0,0,.4);
}
.tutorial-card h3 { font-size:1.25rem; font-weight:800; margin-bottom:.6rem; color:var(--fjord); }
.tutorial-card p { font-size:.9rem; color:var(--text2); white-space:pre-line; margin-bottom:1rem; }
.progress-dots { display:flex; gap:.3rem; justify-content:center; margin:.8rem 0; }
.dot { width:8px; height:8px; border-radius:50%; background:#dadad0; }
.dot.active { background:var(--fjord); }
.tut-btn {
width:100%; padding:.75rem; background:var(--fjord); color:#fff;
border:none; border-radius:10px; font-weight:700; font-size:.95rem; cursor:pointer;
}
.tut-btn:hover { background:var(--fjord-d); }
/* === END SCREEN === */
.end-screen {
position:fixed; inset:0; background:rgba(15,30,40,.9);
backdrop-filter:blur(10px); display:flex; align-items:center;
justify-content:center; z-index:100; padding:2rem;
}
.end-card { background:#fff; border-radius:20px; padding:2rem 2.5rem; max-width:500px; text-align:center; box-shadow:0 30px 80px rgba(0,0,0,.4); }
.end-icon { font-size:4rem; margin-bottom:.5rem; }
.end-card h2 { font-size:1.8rem; font-weight:900; letter-spacing:-.02em; margin-bottom:.3rem; }
.end-card .end-summary { color:var(--text2); margin-bottom:1rem; }
.end-stats { display:grid; grid-template-columns:1fr 1fr; gap:.8rem; margin:1rem 0; }
.end-stat { background:#f7f6f3; border-radius:10px; padding:.8rem; }
.end-stat-val { font-size:1.4rem; font-weight:800; color:var(--fjord); }
.end-stat-lbl { font-size:.7rem; color:var(--text3); text-transform:uppercase; }
.end-goals { display:flex; flex-direction:column; gap:.3rem; margin:.8rem 0 1rem; text-align:left; }
.end-goal { display:flex; align-items:center; gap:.55rem; font-size:.82rem; padding:.35rem .55rem; border-radius:7px; background:#f7f6f3; }
.end-goal.done { color:var(--moss); background:var(--moss-l); }
.end-goal.fail { color:var(--danger); background:var(--coral-l); }
.end-goal-icon { font-weight:900; font-size:.95rem; width:1rem; text-align:center; }
</style>
</head>
<body>
<div id="canvas-wrap"></div>
<!-- TOP BAR -->
<nav class="floating topbar">
<a href="index.html" class="brand">
<img src="assets/img/logo.png" alt="">
Lieferketten-Planer*in
</a>
<div class="topbar-controls">
<div class="ctrl-group" title="Simulationsgeschwindigkeit">
<button class="icon-btn" data-speed="0" title="Pause"></button>
<button class="icon-btn active" data-speed="1" title="Normal (×1)">×1</button>
<button class="icon-btn" data-speed="2" title="Schnell (×2)">×2</button>
<button class="icon-btn" data-speed="4" title="Sehr schnell (×4)">×4</button>
</div>
<div class="topbar-sep"></div>
<button class="icon-btn" id="btn-save" title="Stand speichern">💾</button>
<button class="icon-btn" id="btn-load" title="Stand laden">📂</button>
<button class="icon-btn" id="btn-finish" title="Simulation beenden"></button>
<button class="icon-btn" id="btn-reset" title="Zurücksetzen">🔄</button>
<a class="icon-btn" href="index.html" title="Zurück zur Übersicht">🏠</a>
</div>
</nav>
<!-- LINKS: Status + Ziele -->
<aside class="floating panel-status">
<div class="panel-h">📊 Status</div>
<div id="resources"></div>
<div class="panel-h panel-h-sub">🎯 Ziele</div>
<div id="status-goals"></div>
</aside>
<!-- LINKS DANEBEN: Ereignisse-Strip -->
<aside class="floating panel-events-strip" id="events-strip">
<div class="events-strip-header">
<span>📰 Ereignisse</span>
<span class="esh-toggle"></span>
</div>
<div id="events" class="events-strip-list"></div>
</aside>
<!-- RECHTS: Bestell-Formular -->
<aside class="floating panel-order">
<div class="order-section">
<h4>🌾 Baumwolle</h4>
<div class="order-options" id="cotton-options"></div>
</div>
<div class="order-section">
<h4>🚚 Transport zur Näherei</h4>
<div class="transport-row" id="transport1-row"></div>
</div>
<div class="order-section">
<h4>🏭 Näherei</h4>
<div class="order-options" id="factory-options"></div>
</div>
<div class="order-section">
<h4>🚚 Transport nach Wien</h4>
<div class="transport-row" id="transport2-row"></div>
</div>
<div class="order-summary" id="order-summary"></div>
<button class="order-btn" id="order-btn" disabled>Bestellen</button>
</aside>
<!-- TUTORIAL -->
<div class="tutorial-overlay" id="tutorial">
<div class="tutorial-card">
<h3 id="tut-title"></h3>
<p id="tut-text"></p>
<div class="progress-dots" id="tut-dots"></div>
<button class="tut-btn" id="tut-next">Weiter →</button>
</div>
</div>
<div id="end-screen-host"></div>
<div id="citizen-event-host"></div>
<div id="info-overlay-host"></div>
<script type="module">
import {
LieferkettenGame, COTTON_SOURCES, FACTORIES, TRANSPORT_MODES, DEFAULT_CONFIG,
} from './src/sims/sim-10-lieferketten/game.ts'
import { LieferkettenRenderer } from './src/sims/sim-10-lieferketten/renderer.ts'
import { GameUI } from './src/ui/game-ui.ts'
import { installTooltips } from './src/ui/info-overlay.ts'
installTooltips()
// === Lehrer-Konfiguration via URL-Parameter ===
// ?config=<json> erlaubt der Lehrperson, die drei Hauptparameter zu setzen.
function readConfig() {
try {
const raw = new URLSearchParams(location.search).get('config')
if (raw) return JSON.parse(decodeURIComponent(raw))
} catch {}
return {}
}
const userConfig = readConfig()
const game = new LieferkettenGame(userConfig)
// === GameUI ohne Shop (wir bauen unser eigenes Bestell-Formular) ===
// Wir geben einen leeren shopItems-Array, damit GameUI nichts rendert.
const ui = new GameUI({
game,
Renderer: LieferkettenRenderer,
shopItems: [],
onBuy: () => false,
graphs: [],
saveKey: 'lieferketten-save',
yearOffset: 2025,
})
// === Bestell-Formular bauen ===
const filterByConfig = (list, allowed) => list.filter(x => allowed.includes(x.id))
const cottonAvailable = filterByConfig(COTTON_SOURCES, game.getConfig().enabledOptions.cotton)
const factoryAvailable = filterByConfig(FACTORIES, game.getConfig().enabledOptions.factory)
const transportAvailable = TRANSPORT_MODES.filter(t => game.getConfig().enabledOptions.transport.includes(t.id))
const currentOrder = {
cottonId: null,
factoryId: null,
transport1: null,
transport2: null,
}
function ratingClass(value, goodAbove, badBelow) {
if (value >= goodAbove) return 'oo-stat-good'
if (value < badBelow) return 'oo-stat-bad'
return ''
}
function renderCottonOptions() {
const el = document.getElementById('cotton-options')
el.innerHTML = cottonAvailable.map(c => `
<button class="order-option ${currentOrder.cottonId === c.id ? 'selected' : ''}" data-id="${c.id}">
<span class="oo-flag">${c.flag}</span>
<div class="oo-info">
<div class="oo-name">${c.country}</div>
<div class="oo-stats">
<span class="oo-stat">€${c.pricePerShirt.toFixed(2)}</span>
<span class="oo-stat ${ratingClass(-c.co2PerShirt, -1.5, -2.4)}">${c.co2PerShirt.toFixed(1)} kg CO₂</span>
<span class="oo-stat ${ratingClass(c.ethicsScore, 7, 5)}">⚖️ ${c.ethicsScore}</span>
</div>
</div>
</button>
`).join('')
el.querySelectorAll('.order-option').forEach(btn => {
btn.addEventListener('click', () => {
currentOrder.cottonId = btn.dataset.id
renderCottonOptions(); updatePreview()
})
})
}
function renderFactoryOptions() {
const el = document.getElementById('factory-options')
el.innerHTML = factoryAvailable.map(f => `
<button class="order-option ${currentOrder.factoryId === f.id ? 'selected' : ''}" data-id="${f.id}">
<span class="oo-flag">${f.flag}</span>
<div class="oo-info">
<div class="oo-name">${f.country}</div>
<div class="oo-stats">
<span class="oo-stat">€${f.pricePerShirt.toFixed(2)}</span>
<span class="oo-stat ${ratingClass(-f.co2PerShirt, -1.0, -1.5)}">${f.co2PerShirt.toFixed(1)} kg CO₂</span>
<span class="oo-stat ${ratingClass(f.ethicsScore, 7, 5)}">⚖️ ${f.ethicsScore}</span>
</div>
</div>
</button>
`).join('')
el.querySelectorAll('.order-option').forEach(btn => {
btn.addEventListener('click', () => {
currentOrder.factoryId = btn.dataset.id
renderFactoryOptions(); updatePreview()
})
})
}
function renderTransportRow(rowId, key) {
const el = document.getElementById(rowId)
el.innerHTML = transportAvailable.map(t => `
<button class="transport-btn ${currentOrder[key] === t.id ? 'selected' : ''}" data-id="${t.id}">
<span class="tb-emoji">${t.emoji}</span>
<span class="tb-name">${t.name.split(' ')[0]}</span>
</button>
`).join('')
el.querySelectorAll('.transport-btn').forEach(btn => {
btn.addEventListener('click', () => {
currentOrder[key] = btn.dataset.id
renderTransportRow(rowId, key); updatePreview()
})
})
}
function updatePreview() {
// Renderer-Vorschau aktualisieren
const r = ui.getRenderer()
if (r && typeof r.setPreviewOrder === 'function') {
r.setPreviewOrder(currentOrder)
}
// Order-Summary berechnen
const summary = document.getElementById('order-summary')
const btn = document.getElementById('order-btn')
const complete = currentOrder.cottonId && currentOrder.factoryId && currentOrder.transport1 && currentOrder.transport2
if (!complete) {
summary.innerHTML = '<div style="color:var(--text3);text-align:center;font-size:.7rem">Wähle alle 4 Stationen aus.</div>'
btn.disabled = true
return
}
// Vorschau-Berechnung lokal (mirror der placeOrder-Logik)
const c = COTTON_SOURCES.find(x => x.id === currentOrder.cottonId)
const f = FACTORIES.find(x => x.id === currentOrder.factoryId)
const t1 = TRANSPORT_MODES.find(x => x.id === currentOrder.transport1)
const t2 = TRANSPORT_MODES.find(x => x.id === currentOrder.transport2)
// Sehr grobe Schätzung — wir nutzen die Distanz-Mittelwerte (nicht die Effekte)
const dist1 = 5 // ~5000 km Schätzwert für Vorschau
const dist2 = 3 // ~3000 km Schätzwert
const t1Cost = t1.costPer1000km * dist1
const t2Cost = t2.costPer1000km * dist2
const t1CO2 = t1.co2Per1000km * dist1
const t2CO2 = t2.co2Per1000km * dist2
const costPerShirt = c.pricePerShirt + f.pricePerShirt + t1Cost + t2Cost
const co2PerShirt = c.co2PerShirt + f.co2PerShirt + t1CO2 + t2CO2
const ethics = (c.ethicsScore + f.ethicsScore) / 2
const profitPerShirt = 12 - costPerShirt
const profitMio = (5000 * profitPerShirt) / 1_000_000
const profitClass = profitMio >= 0 ? 'os-good' : 'os-bad'
const co2Class = co2PerShirt < 6 ? 'os-good' : 'os-bad'
const ethicsClass = ethics >= 5 ? 'os-good' : 'os-bad'
summary.innerHTML = `
<div class="os-row"><span>Kosten/Stück</span><strong>€${costPerShirt.toFixed(2)}</strong></div>
<div class="os-row"><span>Gewinn/Charge</span><strong class="${profitClass}">${profitMio >= 0 ? '+' : ''}${profitMio.toFixed(2)} Mio €</strong></div>
<div class="os-row"><span>CO₂/Stück</span><strong class="${co2Class}">${co2PerShirt.toFixed(1)} kg</strong></div>
<div class="os-row"><span>Ø Ethik</span><strong class="${ethicsClass}">${ethics.toFixed(1)} / 10</strong></div>
`
btn.disabled = false
}
document.getElementById('order-btn').addEventListener('click', () => {
if (!currentOrder.cottonId || !currentOrder.factoryId || !currentOrder.transport1 || !currentOrder.transport2) return
game.placeOrder({ ...currentOrder })
// Sprite-Animation auf der Karte
const r = ui.getRenderer()
if (r && typeof r.animateOrder === 'function') r.animateOrder(currentOrder)
})
// Events-Strip toggle
const eventsStrip = document.getElementById('events-strip')
eventsStrip.addEventListener('click', (ev) => {
ev.stopPropagation()
eventsStrip.classList.toggle('open')
})
document.addEventListener('click', (ev) => {
if (!eventsStrip.classList.contains('open')) return
if (eventsStrip.contains(ev.target)) return
eventsStrip.classList.remove('open')
})
// Initial render
renderCottonOptions()
renderFactoryOptions()
renderTransportRow('transport1-row', 'transport1')
renderTransportRow('transport2-row', 'transport2')
updatePreview()
</script>
</body>
</html>
@@ -0,0 +1,520 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Forscher*in im Regenwald</title>
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="stylesheet" href="assets/fonts/inter.css">
<style>
*, *::before, *::after { margin:0; padding:0; box-sizing:border-box; }
:root {
--text: #1a1a1a; --text2: #4a4a4a; --text3: #8a8a8a;
--fjord: #4a7c8a; --fjord-l: #dae8ec; --fjord-d: #3a6470;
--moss: #5a8a5e; --moss-l: #dceadd;
--sand: #c4a35a; --sand-l: #f2eacc;
}
html, body { height:100%; overflow:hidden; }
body { font-family:'Inter',system-ui,sans-serif; color:var(--text); background: #2a5a3a; overflow: hidden; }
/* === FLUSS-KARTE (scrollender Hintergrund) === */
#river-scene {
position: fixed; inset: -200vh 0; z-index: 0;
overflow: visible;
/* Der Gradient — 3x viewport height to prevent white edges */
background: linear-gradient(0deg, #a8d8c8 0%, #5a9a6a 30%, #3a7a4a 60%, #2a5a3a 80%, #1a4a2a 100%);
background-size: 100% 300vh;
background-position: center bottom;
}
/* Flussbett — ein vertikaler Streifen in der Mitte */
#river-bed {
position: absolute;
left: 50%; transform: translateX(-50%);
width: 140px;
top: 0; bottom: 0;
background: linear-gradient(180deg, rgba(60,120,160,.5) 0%, rgba(40,90,130,.65) 100%);
border-left: 2px solid rgba(40,80,120,.3);
border-right: 2px solid rgba(40,80,120,.3);
border-radius: 10px;
}
/* Wellen-Effekt (CSS, keine Sprites) */
#river-bed::before {
content: '';
position: absolute; inset: 0;
background: repeating-linear-gradient(
0deg,
transparent 0px,
transparent 28px,
rgba(255,255,255,.12) 30px,
transparent 32px
);
animation: wave 3s linear infinite;
}
@keyframes wave { from { background-position-y: 0; } to { background-position-y: 60px; } }
/* === UFER-BÄUME (einfache Emoji-Reihen links und rechts) === */
.tree-row {
position: absolute; top: 0; bottom: 0;
width: 80px;
display: flex; flex-direction: column; gap: 35px;
padding-top: 20px;
font-size: 1.6rem; line-height: 1;
overflow: hidden;
pointer-events: none;
}
.tree-row.left { left: calc(50% - 140px); text-align: right; }
.tree-row.right { right: calc(50% - 140px); text-align: left; }
/* === BOOT === */
#boat {
position: fixed;
left: 50%; bottom: 38%;
transform: translateX(-50%);
font-size: 2.6rem;
z-index: 10;
transition: bottom .8s cubic-bezier(.22,1,.36,1);
filter: drop-shadow(0 4px 8px rgba(0,0,0,.25));
pointer-events: none;
}
/* === ENTDECKUNGS-MARKER === */
.discovery-marker {
position: absolute;
left: 50%;
transform: translateX(-50%);
z-index: 8;
cursor: pointer;
transition: all .25s cubic-bezier(.22,1,.36,1);
}
.dm-bubble {
display: flex; align-items: center; justify-content: center;
width: 52px; height: 52px;
border-radius: 50%;
background: rgba(255,255,255,.85);
box-shadow: 0 4px 16px rgba(0,40,30,.22);
border: 2.5px solid rgba(255,255,255,.8);
font-size: 1.7rem;
transition: all .2s;
}
.dm-bubble:hover { transform: scale(1.2); box-shadow: 0 6px 24px rgba(0,40,30,.35); }
.dm-bubble.found { border-color: var(--moss); background: var(--moss-l); }
.dm-bubble.mystery {
background: rgba(255,255,255,.5);
border: 2.5px dashed rgba(0,0,0,.2);
font-size: 1.4rem; color: var(--text3);
}
.dm-name {
text-align: center;
font-size: .62rem; font-weight: 700;
color: #fff;
text-shadow: 0 1px 4px rgba(0,0,0,.5);
margin-top: .2rem;
white-space: nowrap;
}
/* === INFO-KARTE (erscheint bei Klick auf ein Tier) === */
.info-overlay {
position: fixed; inset: 0; z-index: 50;
background: rgba(15,35,25,.75); backdrop-filter: blur(8px);
display: flex; align-items: center; justify-content: center;
padding: 1.5rem;
animation: fadein .25s ease-out;
}
@keyframes fadein { from { opacity:0; } to { opacity:1; } }
.info-card {
background: #fff; border-radius: 20px;
padding: 1.8rem 2rem; max-width: 480px; width: 100%;
box-shadow: 0 30px 80px rgba(0,0,0,.4);
animation: cardpop .3s cubic-bezier(.22,1,.36,1);
}
@keyframes cardpop {
from { transform: scale(.9) translateY(15px); opacity:0; }
to { transform: scale(1) translateY(0); opacity:1; }
}
.info-card .ic-emoji { font-size: 3.5rem; text-align: center; margin-bottom: .5rem; }
.info-card h3 { font-size: 1.3rem; font-weight: 800; color: var(--fjord); margin-bottom: .5rem; text-align: center; }
.info-card .ic-zone {
text-align: center; font-size: .62rem; font-weight: 700;
color: var(--moss); text-transform: uppercase; letter-spacing: .06em;
margin-bottom: .8rem;
}
.info-card p { font-size: .95rem; color: var(--text2); line-height: 1.6; margin-bottom: .7rem; }
.info-card .ic-wow {
background: var(--sand-l); border-left: 3px solid var(--sand);
padding: .7rem .9rem; border-radius: 0 8px 8px 0;
font-size: .85rem; color: var(--text2); margin-bottom: .9rem;
}
.info-card .ic-wow::before { content: '🤯 '; }
.info-card .ic-btn {
width: 100%; padding: .7rem; border: none; border-radius: 10px;
background: var(--moss); color: #fff;
font-weight: 700; font-size: .9rem; cursor: pointer;
font-family: inherit;
}
.info-card .ic-btn:hover { background: #4a7a4e; }
/* === TOPBAR (sehr schlank) === */
.topbar {
position: fixed; top: 0; left: 0; right: 0; z-index: 20;
padding: .3rem .8rem;
display: flex; justify-content: space-between; align-items: center;
background: rgba(255,255,255,.75);
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(0,0,0,.08);
}
.topbar .brand {
display: flex; align-items: center; gap: .45rem;
font-weight: 800; font-size: .85rem; color: var(--fjord);
text-decoration: none;
}
.topbar .brand img { width: 22px; height: 22px; border-radius: 5px; }
/* === SAMMELHEFT (rechts oben) === */
.sammelheft {
position: fixed; top: 3.2rem; right: .65rem; z-index: 15;
background: rgba(255,255,255,.88);
backdrop-filter: blur(12px);
border: 1px solid rgba(255,255,255,.6);
box-shadow: 0 8px 28px rgba(20,40,60,.18);
border-radius: 14px;
padding: .6rem .75rem;
width: 200px;
}
.sammelheft h4 {
font-size: .62rem; font-weight: 800; color: var(--fjord);
text-transform: uppercase; letter-spacing: .07em;
margin-bottom: .4rem;
}
.sh-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: .3rem;
}
.sh-slot {
width: 32px; height: 32px;
border-radius: 7px;
display: flex; align-items: center; justify-content: center;
font-size: 1.15rem;
background: rgba(0,0,0,.06);
border: 1.5px solid rgba(0,0,0,.06);
transition: all .2s;
}
.sh-slot.found { background: var(--moss-l); border-color: var(--moss); }
.sh-slot.empty { color: var(--text3); font-size: .85rem; }
.sh-progress {
margin-top: .45rem;
font-size: .62rem; font-weight: 700; color: var(--text2);
text-align: center;
}
.sh-bar { height: 4px; background: rgba(0,0,0,.08); border-radius: 2px; margin-top: .2rem; overflow: hidden; }
.sh-bar-fill { height: 100%; background: var(--moss); border-radius: 2px; transition: width .4s; }
/* === ZONEN-ANZEIGE (unten links, zeigt aktuelle Zone) === */
.zone-badge {
position: fixed; bottom: .8rem; left: .8rem; z-index: 15;
background: rgba(255,255,255,.82);
backdrop-filter: blur(10px);
border-radius: 12px; padding: .5rem .8rem;
box-shadow: 0 4px 16px rgba(0,0,0,.12);
font-size: .72rem; font-weight: 700; color: var(--text);
}
.zone-badge .zb-emoji { font-size: 1.1rem; margin-right: .3rem; }
.zone-badge .zb-name { color: var(--moss); }
/* === STEUERUNGS-HINT (unten mitte) === */
.controls-hint {
position: fixed; bottom: .8rem; left: 50%; transform: translateX(-50%);
z-index: 15;
background: rgba(255,255,255,.75);
backdrop-filter: blur(10px);
border-radius: 10px; padding: .4rem .8rem;
box-shadow: 0 4px 12px rgba(0,0,0,.1);
font-size: .65rem; color: var(--text2); font-weight: 600;
}
.controls-hint kbd {
display: inline-block; padding: 1px 5px;
background: rgba(0,0,0,.08); border: 1px solid rgba(0,0,0,.12);
border-radius: 3px; font-size: .62rem; font-weight: 800;
font-family: ui-monospace, monospace;
}
/* === GLÜCKWUNSCH-OVERLAY === */
.congrats-overlay {
position: fixed; inset: 0; z-index: 60;
background: rgba(15,35,25,.85); backdrop-filter: blur(10px);
display: flex; align-items: center; justify-content: center;
padding: 1.5rem;
}
.congrats-card {
background: #fff; border-radius: 20px; padding: 2rem 2.5rem;
max-width: 480px; text-align: center;
box-shadow: 0 30px 80px rgba(0,0,0,.4);
}
.congrats-card .cc-emoji { font-size: 4rem; margin-bottom: .5rem; }
.congrats-card h2 { font-size: 1.6rem; font-weight: 900; color: var(--fjord); margin-bottom: .5rem; }
.congrats-card p { font-size: .92rem; color: var(--text2); line-height: 1.6; margin-bottom: 1rem; }
.congrats-card .cc-stat { font-size: 1.1rem; font-weight: 800; color: var(--moss); margin-bottom: 1rem; }
.congrats-card .ic-btn { width: 100%; }
</style>
</head>
<body>
<!-- TOPBAR -->
<nav class="topbar">
<a href="index.html" class="brand">
<img src="assets/img/logo.png" alt="">
Forscher*in im Regenwald 🌴
</a>
<a href="index.html" class="brand" style="font-size:.7rem;opacity:.6;">← Zurück</a>
</nav>
<!-- SAMMELHEFT -->
<div class="sammelheft" id="sammelheft">
<h4>📔 Sammelheft</h4>
<div class="sh-grid" id="sh-grid"></div>
<div class="sh-progress" id="sh-progress">0 von 15 entdeckt</div>
<div class="sh-bar"><div class="sh-bar-fill" id="sh-bar-fill" style="width:0%"></div></div>
</div>
<!-- ZONEN-ANZEIGE -->
<div class="zone-badge" id="zone-badge">
<span class="zb-emoji">🏝</span>
<span class="zb-name">Flussmündung</span>
</div>
<!-- STEUERUNGS-HINT -->
<div class="controls-hint">
<kbd></kbd> flussaufwärts · <kbd></kbd> flussabwärts · Klick auf ein Tier zum Entdecken
</div>
<!-- FLUSS-SZENE -->
<div id="river-scene">
<div id="river-bed"></div>
<div class="tree-row left" id="trees-left"></div>
<div class="tree-row right" id="trees-right"></div>
<!-- Entdeckungs-Marker werden hier per JS eingefügt -->
</div>
<!-- BOOT -->
<div id="boat">🛶</div>
<!-- INFO-KARTE HOST -->
<div id="info-host"></div>
<!-- GLÜCKWUNSCH HOST -->
<div id="congrats-host"></div>
<script type="module">
import { DISCOVERIES, ZONES } from './src/sims/sim-11-regenwald/data.ts'
// ===================================================================
// STATE
// ===================================================================
const SAVE_KEY = 'regenwald-discovered'
let discovered = new Set()
try {
const saved = JSON.parse(localStorage.getItem(SAVE_KEY) || '[]')
if (Array.isArray(saved)) discovered = new Set(saved)
} catch {}
// Scroll-Position: 0 = Flussmündung (unten), 100 = Bergregenwald (oben)
let scrollPos = 0
const SCROLL_SPEED = 0.6
const SCENE_HEIGHT_VH = 350 // virtuelle Höhe der Szene in vh-Einheiten
function save() {
localStorage.setItem(SAVE_KEY, JSON.stringify([...discovered]))
}
// ===================================================================
// BÄUME AM UFER (deko, statisch)
// ===================================================================
function buildTrees() {
const treeEmojis = ['🌴', '🌳', '🌿', '🎋', '🌲']
const count = 35
for (const side of ['left', 'right']) {
const el = document.getElementById(`trees-${side}`)
let html = ''
for (let i = 0; i < count; i++) {
const emoji = treeEmojis[Math.floor(Math.random() * treeEmojis.length)]
const offset = side === 'left'
? `padding-right:${Math.random() * 30}px`
: `padding-left:${Math.random() * 30}px`
html += `<div style="${offset}">${emoji}</div>`
}
el.innerHTML = html
}
}
buildTrees()
// ===================================================================
// ENTDECKUNGS-MARKER
// ===================================================================
const scene = document.getElementById('river-scene')
const markerEls = []
for (const d of DISCOVERIES) {
const marker = document.createElement('div')
marker.className = 'discovery-marker'
marker.dataset.id = d.id
// Position: 0 = unten (100vh), 100 = oben (0vh)
// Wir setzen die Position relativ zur Szene-Höhe (SCENE_HEIGHT_VH)
const topPct = (1 - d.position / 100) * SCENE_HEIGHT_VH
marker.style.top = `${topPct}vh`
// Leicht links/rechts versetzen (abwechselnd)
const xOff = (DISCOVERIES.indexOf(d) % 2 === 0 ? -45 : 45)
marker.style.marginLeft = `${xOff}px`
const isFound = discovered.has(d.id)
marker.innerHTML = `
<div class="dm-bubble ${isFound ? 'found' : 'mystery'}">
${isFound ? d.emoji : '❓'}
</div>
<div class="dm-name">${isFound ? d.name : '???'}</div>
`
marker.addEventListener('click', () => showInfo(d))
scene.appendChild(marker)
markerEls.push({ marker, data: d })
}
// ===================================================================
// SAMMELHEFT
// ===================================================================
function renderSammelheft() {
const grid = document.getElementById('sh-grid')
grid.innerHTML = DISCOVERIES.map(d => {
const found = discovered.has(d.id)
return `<div class="sh-slot ${found ? 'found' : 'empty'}" title="${found ? d.name : '???'}">
${found ? d.emoji : '?'}
</div>`
}).join('')
const count = discovered.size
document.getElementById('sh-progress').textContent = `${count} von ${DISCOVERIES.length} entdeckt`
document.getElementById('sh-bar-fill').style.width = `${(count / DISCOVERIES.length) * 100}%`
}
renderSammelheft()
// ===================================================================
// ZONEN-BADGE
// ===================================================================
function updateZoneBadge() {
let zone = ZONES[0]
if (scrollPos > 33) zone = ZONES[1]
if (scrollPos > 66) zone = ZONES[2]
const badge = document.getElementById('zone-badge')
badge.innerHTML = `<span class="zb-emoji">${zone.emoji}</span> <span class="zb-name">${zone.name}</span> — ${zone.description}`
}
updateZoneBadge()
// ===================================================================
// SCROLL-MECHANIK (Pfeiltasten)
// ===================================================================
const keysDown = new Set()
window.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault()
keysDown.add(e.key)
}
})
window.addEventListener('keyup', (e) => keysDown.delete(e.key))
// Mausrad scrollt auch
window.addEventListener('wheel', (e) => {
e.preventDefault()
scrollPos = Math.max(0, Math.min(100, scrollPos + (e.deltaY > 0 ? -SCROLL_SPEED * 2 : SCROLL_SPEED * 2)))
applyScroll()
}, { passive: false })
function applyScroll() {
// Szene verschieben: 0 = Boden, 100 = Spitze
const translateY = scrollPos / 100 * (SCENE_HEIGHT_VH - 100)
scene.style.transform = `translateY(${translateY}vh)`
// Hintergrund-Gradient anpassen
const zone = scrollPos < 33 ? 0 : scrollPos < 66 ? 1 : 2
const z = ZONES[zone]
scene.style.background = `linear-gradient(0deg, ${z.bgFrom} 0%, ${z.bgTo} 100%)`
updateZoneBadge()
}
// rAF-Loop für Pfeiltasten-Scroll
function scrollLoop() {
if (keysDown.has('ArrowUp')) scrollPos = Math.min(100, scrollPos + SCROLL_SPEED)
if (keysDown.has('ArrowDown')) scrollPos = Math.max(0, scrollPos - SCROLL_SPEED)
if (keysDown.size > 0) applyScroll()
requestAnimationFrame(scrollLoop)
}
requestAnimationFrame(scrollLoop)
// ===================================================================
// INFO-KARTE
// ===================================================================
function showInfo(d) {
// Entdeckung registrieren
const wasNew = !discovered.has(d.id)
discovered.add(d.id)
save()
// Marker aktualisieren
const mel = markerEls.find(m => m.data.id === d.id)
if (mel) {
const bubble = mel.marker.querySelector('.dm-bubble')
bubble.className = 'dm-bubble found'
bubble.textContent = d.emoji
mel.marker.querySelector('.dm-name').textContent = d.name
}
renderSammelheft()
// Info-Overlay
const host = document.getElementById('info-host')
const zone = ZONES.find(z => z.id === d.zone)
host.innerHTML = `
<div class="info-overlay" id="info-bg">
<div class="info-card">
<div class="ic-emoji">${d.emoji}</div>
<h3>${d.name}</h3>
<div class="ic-zone">${zone?.emoji || ''} ${zone?.name || d.zone}</div>
<p>${d.fact}</p>
${d.wow ? `<div class="ic-wow">${d.wow}</div>` : ''}
<button class="ic-btn" id="info-close">
${wasNew ? '✅ Entdeckt! Weiter' : 'Verstanden ✓'}
</button>
</div>
</div>
`
document.getElementById('info-close')?.addEventListener('click', closeInfo)
document.getElementById('info-bg')?.addEventListener('click', (e) => {
if (e.target.id === 'info-bg') closeInfo()
})
// Prüfen ob alle gefunden
if (discovered.size === DISCOVERIES.length) {
setTimeout(showCongrats, 800)
}
}
function closeInfo() {
document.getElementById('info-host').innerHTML = ''
}
// ===================================================================
// GLÜCKWUNSCH
// ===================================================================
function showCongrats() {
const host = document.getElementById('congrats-host')
host.innerHTML = `
<div class="congrats-overlay">
<div class="congrats-card">
<div class="cc-emoji">🏆🌴</div>
<h2>Alle Tiere entdeckt!</h2>
<div class="cc-stat">${DISCOVERIES.length} von ${DISCOVERIES.length} gefunden</div>
<p>Du hast den ganzen Regenwald erforscht — von der Flussmündung bis zum Bergregenwald.</p>
<p>Aber vergiss nicht: Jedes Jahr verschwindet eine Regenwald-Fläche so groß wie die Schweiz. Was wir kennen, können wir schützen. 🌍</p>
<button class="ic-btn" onclick="this.closest('.congrats-overlay').remove()">Nochmal erkunden</button>
</div>
</div>
`
}
</script>
</body>
</html>
@@ -0,0 +1,300 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Stadt & Raumplanung</title>
<link rel="icon" type="image/png" href="favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="stylesheet" href="assets/fonts/inter.css">
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',sans-serif;background:#f0efe8;color:#1a1a1a;display:flex;height:100vh;overflow:hidden}
.sidebar{width:155px;background:#fff;border-right:1px solid rgba(0,0,0,.06);display:flex;flex-direction:column;overflow-y:auto}
.sidebar-section{padding:6px}
.sidebar h3{font-size:8px;color:#4a7c8a;text-transform:uppercase;letter-spacing:.05em;margin-bottom:3px;padding:0 4px}
.tb{display:flex;align-items:center;gap:3px;padding:3px 4px;border:1.5px solid transparent;border-radius:4px;cursor:pointer;font-size:8px;font-weight:600;margin-bottom:1px;transition:all .1s}
.tb:hover{border-color:#4a7c8a;background:#f0f6f8}
.tb.on{border-color:#4a7c8a;background:#4a7c8a;color:#fff}
.tb.off{opacity:.2;pointer-events:none}
.tb .te{font-size:13px}
.tb .tc{margin-left:auto;font-size:7px;color:#c4a35a}
.center{flex:1;display:flex;flex-direction:column;min-width:0}
.topbar{display:flex;align-items:center;gap:6px;padding:4px 8px;background:#fff;border-bottom:1px solid rgba(0,0,0,.05);flex-shrink:0}
.topbar .stat{font-size:10px;font-weight:700}
.topbar .gold{color:#c4a35a}
.topbar .rnd{font-size:9px;color:#4a7c8a;background:#dae8ec;padding:2px 6px;border-radius:4px;font-weight:700;margin-left:auto}
.topbar .btn{padding:2px 8px;border:none;border-radius:4px;background:#4a7c8a;color:#fff;font-size:9px;font-weight:700;cursor:pointer}
.canvas-area{flex:1;display:flex;align-items:center;justify-content:center;background:#c8d0b8;overflow:hidden}
.canvas-area canvas{max-width:100%;max-height:100%}
.right{width:155px;background:#fff;border-left:1px solid rgba(0,0,0,.06);display:flex;flex-direction:column;overflow-y:auto;padding:6px}
.right h3{font-size:8px;color:#4a7c8a;text-transform:uppercase;letter-spacing:.05em;margin-bottom:3px}
.param{display:flex;align-items:center;gap:3px;padding:2px 0;border-bottom:1px solid rgba(0,0,0,.02)}
.param:last-child{border:none}
.param-i{font-size:10px;width:14px;text-align:center}
.param-inf{flex:1}
.param-n{font-size:7px;color:#8a8a8a}
.param-bar{height:3px;background:#e8e5dc;border-radius:1.5px;margin-top:1px;overflow:hidden}
.param-bar-f{height:100%;border-radius:1.5px;transition:width .4s}
.param-v{font-size:8px;font-weight:700;min-width:14px;text-align:right}
.msg{font-size:7px;padding:2px 4px;border-radius:3px;margin-bottom:1px;background:#dae8ec;color:#3a6470;line-height:1.2}
.msg.warn{background:#f2ddd8;color:#c07a6b}
.msg.good{background:#dceadd;color:#5a8a5e}
.ov{position:fixed;inset:0;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center;z-index:100}
.ov-c{background:#fff;border-radius:12px;padding:1.2rem;max-width:360px;width:90%;text-align:center}
.ov-c h2{font-size:.9rem;font-weight:800;margin-bottom:.15rem}
.ov-c p{font-size:.7rem;color:#4a4a4a;margin-bottom:.4rem}
.ov-btn{padding:.3rem .8rem;border:none;border-radius:5px;background:#4a7c8a;color:#fff;font-size:.7rem;font-weight:700;cursor:pointer}
.hidden{display:none}
@media(max-width:700px){.sidebar,.right{display:none}.center{width:100%}}
</style>
</head>
<body>
<div class="sidebar">
<div class="sidebar-section">
<h3>Straßen</h3>
<div id="road-tools"></div>
</div>
<div class="sidebar-section">
<h3>Gebäude</h3>
<div id="building-tools"></div>
</div>
<div class="sidebar-section">
<h3>Natur</h3>
<div id="nature-tools"></div>
</div>
</div>
<div class="center">
<div class="topbar">
<span class="stat gold">💰 <span id="h-bud">50</span></span>
<span class="stat" style="font-size:8px;color:#5a8a5e" id="h-inc"></span>
<span class="rnd" id="h-rnd">1/10</span>
<button class="btn" id="btn-next">▶ Runde</button>
</div>
<div class="canvas-area"><canvas id="cv"></canvas></div>
</div>
<div class="right">
<h3>Kennwerte</h3>
<div id="params"></div>
<h3 style="margin-top:6px">Nachrichten</h3>
<div id="msgs"></div>
</div>
<div class="ov" id="ov-intro">
<div class="ov-c">
<h2>🏘️ Stadt & Raumplanung</h2>
<p>Baue eine Siedlung! Wähle links Kacheln und klicke ins Raster. Klicke oben-links/rechts auf platzierte Kacheln zum Drehen. Jede Runde bekommst du neues Budget.</p>
<button class="ov-btn" id="btn-go">Los geht's!</button>
</div>
</div>
<div class="ov hidden" id="ov-end">
<div class="ov-c"><h2 id="end-t"></h2><p id="end-p"></p><button class="ov-btn" id="btn-again">Nochmal</button></div>
</div>
<script>
var COLS=10, ROWS=10, TS=60
var GREEN='#b8ccaa', ROAD='#2e2e2e', WHITE='#fff'
// === ALLE KACHEL-TYPEN ===
// cat: road/building/nature, conn: [N,E,S,W], emoji: fuer Gebaeude
var TILES = [
// Strassen (cat=0)
{n:'Gerade │', cat:0, conn:[1,0,1,0], cost:2, emoji:'', eff:{}},
{n:'Gerade ─', cat:0, conn:[0,1,0,1], cost:2, emoji:'', eff:{}},
{n:'Kurve ╯', cat:0, conn:[1,1,0,0], cost:2, emoji:'', eff:{}},
{n:'Kurve ╮', cat:0, conn:[0,1,1,0], cost:2, emoji:'', eff:{}},
{n:'Kurve ╭', cat:0, conn:[0,0,1,1], cost:2, emoji:'', eff:{}},
{n:'Kurve ╰', cat:0, conn:[1,0,0,1], cost:2, emoji:'', eff:{}},
{n:'T ┴', cat:0, conn:[1,1,0,1], cost:3, emoji:'', eff:{}},
{n:'T ├', cat:0, conn:[1,1,1,0], cost:3, emoji:'', eff:{}},
{n:'T ┬', cat:0, conn:[0,1,1,1], cost:3, emoji:'', eff:{}},
{n:'T ┤', cat:0, conn:[1,0,1,1], cost:3, emoji:'', eff:{}},
{n:'Kreuzung', cat:0, conn:[1,1,1,1], cost:4, emoji:'', eff:{}},
// Gebaeude (cat=1) — mit Strassen + Emojis
{n:'Wohngebiet', cat:1, conn:[1,0,1,0], cost:6, emoji:'🏠🏡🏠', emojis:[{e:'🏠',x:-0.32,y:-0.2,s:0.15},{e:'🏡',x:0.32,y:0,s:0.15},{e:'🏠',x:-0.3,y:0.2,s:0.13},{e:'🏠',x:0.3,y:0.25,s:0.12}], eff:{eco:2,sat:2,env:-1,traf:1}},
{n:'Laden', cat:1, conn:[1,0,1,0], cost:7, emoji:'🏪', emojis:[{e:'🏪',x:0.3,y:0,s:0.22}], eff:{eco:4,sat:2,env:-1,traf:2,sup:5}},
{n:'Schule', cat:1, conn:[0,1,0,1], cost:9, emoji:'🏫', emojis:[{e:'🏫',x:0,y:-0.25,s:0.22}], eff:{eco:1,sat:5,traf:1,sup:4}},
{n:'Kirche', cat:1, conn:[0,0,1,0], cost:8, emoji:'⛪', emojis:[{e:'⛪',x:0,y:-0.05,s:0.4}], eff:{eco:1,sat:4,env:1,sup:2}},
{n:'Fabrik', cat:1, conn:[0,0,1,0], cost:8, emoji:'🏭', emojis:[{e:'🏭',x:0,y:-0.05,s:0.35}], eff:{eco:6,sat:-3,env:-6,traf:4}},
{n:'Spital', cat:1, conn:[1,0,0,0], cost:11,emoji:'🏥', emojis:[{e:'🏥',x:0,y:0.1,s:0.28},{e:'🚑',x:0,y:0.35,s:0.12}], eff:{eco:2,sat:5,traf:2,sup:5}},
{n:'Feuerwehr', cat:1, conn:[0,0,1,0], cost:8, emoji:'🚒', emojis:[{e:'🏠',x:0,y:-0.15,s:0.25},{e:'🚒',x:-0.15,y:0.2,s:0.18},{e:'🚒',x:0.15,y:0.2,s:0.18}], eff:{eco:1,sat:3,sup:3}},
{n:'Polizei', cat:1, conn:[1,0,0,0], cost:7, emoji:'🚔', emojis:[{e:'🏢',x:0,y:0.1,s:0.25},{e:'🚔',x:0,y:0.32,s:0.15}], eff:{eco:1,sat:3,sup:2}},
{n:'Supermarkt', cat:1, conn:[1,0,1,0], cost:9, emoji:'🏪', emojis:[{e:'🏪',x:0.3,y:-0.1,s:0.22},{e:'🚗',x:0.28,y:0.25,s:0.1},{e:'🚙',x:0.36,y:0.15,s:0.1}], eff:{eco:5,sat:3,env:-2,traf:3,sup:8}},
{n:'Hochhäuser', cat:1, conn:[1,0,1,0], cost:10,emoji:'🏢', emojis:[{e:'🏢',x:0.25,y:-0.15,s:0.16},{e:'🏢',x:0.33,y:-0.15,s:0.16},{e:'🏢',x:0.25,y:0.12,s:0.16},{e:'🏢',x:0.33,y:0.12,s:0.16}], eff:{eco:4,env:-2,traf:3}},
{n:'Rathaus', cat:1, conn:[0,0,1,0], cost:12,emoji:'🏛️', emojis:[{e:'🏛️',x:0,y:-0.05,s:0.35}], eff:{eco:3,sat:4,sup:3}},
{n:'Tankstelle', cat:1, conn:[1,0,1,0], cost:5, emoji:'⛽', emojis:[{e:'⛽',x:0.3,y:-0.15,s:0.14},{e:'⛽',x:0.3,y:0,s:0.14},{e:'⛽',x:0.3,y:0.15,s:0.14},{e:'🚗',x:0.38,y:0.05,s:0.1}], eff:{eco:2,env:-2,traf:1}},
{n:'Bauernhof', cat:1, conn:[0,1,0,0], cost:5, emoji:'🌾', emojis:[{e:'🏠',x:0,y:0,s:0.22},{e:'🌾',x:-0.3,y:-0.25,s:0.1},{e:'🌾',x:-0.15,y:-0.25,s:0.1},{e:'🌾',x:0,y:-0.25,s:0.1},{e:'🌾',x:0.15,y:-0.25,s:0.1},{e:'🐄',x:-0.2,y:0.25,s:0.13}], eff:{eco:3,sat:1,env:2,sup:3}},
// Natur (cat=2)
{n:'Park', cat:2, conn:[1,0,0,0], cost:4, emoji:'🌳', emojis:[{e:'🌳',x:-0.25,y:0.05,s:0.14},{e:'🌳',x:-0.05,y:0.05,s:0.14},{e:'🌳',x:0.15,y:0.05,s:0.14},{e:'🌳',x:0.3,y:0.05,s:0.13},{e:'🌷',x:-0.2,y:0.3,s:0.07},{e:'🌷',x:0,y:0.3,s:0.07},{e:'🌷',x:0.2,y:0.3,s:0.07}], eff:{sat:4,env:6,sup:1}},
{n:'Spielplatz', cat:2, conn:[1,0,0,0], cost:4, emoji:'🛝', emojis:[{e:'🛝',x:-0.15,y:0.08,s:0.18},{e:'🎠',x:0.18,y:0.08,s:0.15},{e:'⚽',x:0,y:0.3,s:0.08},{e:'🌳',x:-0.35,y:0.3,s:0.1},{e:'🌳',x:0.35,y:0.3,s:0.1}], eff:{sat:5,env:3}},
]
var grid = []
for(var r=0;r<ROWS;r++){grid[r]=[];for(var c=0;c<COLS;c++)grid[r][c]=null}
// Start: ein paar Strassen + 2 Haeuser
grid[4][4]={t:1,r:0};grid[4][5]={t:1,r:0};grid[4][6]={t:1,r:0} // Gerade E-W
grid[3][5]={t:11,r:0} // Wohngebiet
var sel=-1, budget=50, round=1, maxRounds=10, income=6, gameOver=false
var scores={eco:40,sat:40,env:55,traf:15,sup:15,reach:25}
var messages=[{text:'Willkommen! Baue Straßen und Gebäude.',type:''}]
function rotConn(c,n){var r=c.slice();for(var i=0;i<n;i++)r=[r[3],r[0],r[1],r[2]];return r}
// === DRAW TILE ===
var cv=document.getElementById('cv'), ctx=cv.getContext('2d')
cv.width=COLS*TS;cv.height=ROWS*TS
function drawTile(px,py,conn,alpha){
var S=TS,RW=S*0.4,M=S/2,HR=RW/2,lw=Math.max(1,S/50)
var N=conn[0],E=conn[1],So=conn[2],W=conn[3],sum=N+E+So+W
if(alpha<1)ctx.globalAlpha=alpha
ctx.fillStyle=GREEN;ctx.fillRect(px,py,S,S)
if(sum===1){ctx.fillStyle=ROAD;var st=Math.max(2,S*0.02)
if(N){ctx.fillRect(px+M-HR,py,RW,st);ctx.beginPath();ctx.arc(px+M,py+st,HR,0,Math.PI);ctx.fill()}
if(So){ctx.fillRect(px+M-HR,py+S-st,RW,st);ctx.beginPath();ctx.arc(px+M,py+S-st,HR,Math.PI,0);ctx.fill()}
if(E){ctx.fillRect(px+S-st,py+M-HR,st,RW);ctx.beginPath();ctx.arc(px+S-st,py+M,HR,Math.PI*1.5,Math.PI*0.5);ctx.fill()}
if(W){ctx.fillRect(px,py+M-HR,st,RW);ctx.beginPath();ctx.arc(px+st,py+M,HR,Math.PI*0.5,Math.PI*1.5);ctx.fill()}
ctx.strokeStyle=WHITE;ctx.lineWidth=lw
if(N){ctx.beginPath();ctx.moveTo(px+M-HR,py);ctx.lineTo(px+M-HR,py+st);ctx.stroke();ctx.beginPath();ctx.moveTo(px+M+HR,py);ctx.lineTo(px+M+HR,py+st);ctx.stroke();ctx.beginPath();ctx.arc(px+M,py+st,HR,0,Math.PI);ctx.stroke()}
if(So){ctx.beginPath();ctx.moveTo(px+M-HR,py+S);ctx.lineTo(px+M-HR,py+S-st);ctx.stroke();ctx.beginPath();ctx.moveTo(px+M+HR,py+S);ctx.lineTo(px+M+HR,py+S-st);ctx.stroke();ctx.beginPath();ctx.arc(px+M,py+S-st,HR,Math.PI,0);ctx.stroke()}
if(E){ctx.beginPath();ctx.moveTo(px+S,py+M-HR);ctx.lineTo(px+S-st,py+M-HR);ctx.stroke();ctx.beginPath();ctx.moveTo(px+S,py+M+HR);ctx.lineTo(px+S-st,py+M+HR);ctx.stroke();ctx.beginPath();ctx.arc(px+S-st,py+M,HR,Math.PI*1.5,Math.PI*0.5);ctx.stroke()}
if(W){ctx.beginPath();ctx.moveTo(px,py+M-HR);ctx.lineTo(px+st,py+M-HR);ctx.stroke();ctx.beginPath();ctx.moveTo(px,py+M+HR);ctx.lineTo(px+st,py+M+HR);ctx.stroke();ctx.beginPath();ctx.arc(px+st,py+M,HR,Math.PI*0.5,Math.PI*1.5);ctx.stroke()}
ctx.globalAlpha=1;return}
var isCurve=sum===2&&((N&&E)||(E&&So)||(So&&W)||(W&&N))
if(isCurve){var cx_,cy_,sa,ea
if(N&&E){cx_=px+M+HR;cy_=py+M-HR;sa=Math.PI;ea=Math.PI*0.5}
if(E&&So){cx_=px+M+HR;cy_=py+M+HR;sa=Math.PI*1.5;ea=Math.PI}
if(So&&W){cx_=px+M-HR;cy_=py+M+HR;sa=0;ea=Math.PI*1.5}
if(W&&N){cx_=px+M-HR;cy_=py+M-HR;sa=Math.PI*0.5;ea=0}
ctx.fillStyle=ROAD
if(N)ctx.fillRect(px+M-HR,py,RW,M-HR);if(So)ctx.fillRect(px+M-HR,py+M+HR,RW,M-HR)
if(E)ctx.fillRect(px+M+HR,py+M-HR,M-HR,RW);if(W)ctx.fillRect(px,py+M-HR,M-HR,RW)
ctx.beginPath();ctx.moveTo(cx_,cy_);ctx.arc(cx_,cy_,RW,sa,ea,true);ctx.closePath();ctx.fill()
ctx.strokeStyle=WHITE;ctx.lineWidth=lw;ctx.beginPath();ctx.arc(cx_,cy_,RW,sa,ea,true);ctx.stroke()
if(N){ctx.beginPath();ctx.moveTo(px+M-HR,py);ctx.lineTo(px+M-HR,cy_);ctx.stroke();ctx.beginPath();ctx.moveTo(px+M+HR,py);ctx.lineTo(px+M+HR,cy_);ctx.stroke()}
if(So){ctx.beginPath();ctx.moveTo(px+M-HR,cy_);ctx.lineTo(px+M-HR,py+S);ctx.stroke();ctx.beginPath();ctx.moveTo(px+M+HR,cy_);ctx.lineTo(px+M+HR,py+S);ctx.stroke()}
if(E){ctx.beginPath();ctx.moveTo(cx_,py+M-HR);ctx.lineTo(px+S,py+M-HR);ctx.stroke();ctx.beginPath();ctx.moveTo(cx_,py+M+HR);ctx.lineTo(px+S,py+M+HR);ctx.stroke()}
if(W){ctx.beginPath();ctx.moveTo(px,py+M-HR);ctx.lineTo(cx_,py+M-HR);ctx.stroke();ctx.beginPath();ctx.moveTo(px,py+M+HR);ctx.lineTo(cx_,py+M+HR);ctx.stroke()}
ctx.setLineDash([S*0.07,S*0.05]);ctx.lineDashOffset=-S*0.035;ctx.beginPath()
if(N&&E){ctx.moveTo(px+M,py);ctx.lineTo(px+M,cy_);ctx.arc(cx_,cy_,HR,Math.PI,Math.PI*0.5,true);ctx.lineTo(px+S,py+M)}
if(E&&So){ctx.moveTo(px+S,py+M);ctx.lineTo(cx_,py+M);ctx.arc(cx_,cy_,HR,Math.PI*1.5,Math.PI,true);ctx.lineTo(px+M,py+S)}
if(So&&W){ctx.moveTo(px+M,py+S);ctx.lineTo(px+M,cy_);ctx.arc(cx_,cy_,HR,0,Math.PI*1.5,true);ctx.lineTo(px,py+M)}
if(W&&N){ctx.moveTo(px,py+M);ctx.lineTo(cx_,py+M);ctx.arc(cx_,cy_,HR,Math.PI*0.5,0,true);ctx.lineTo(px+M,py)}
ctx.stroke();ctx.setLineDash([]);ctx.lineDashOffset=0;ctx.globalAlpha=1;return}
ctx.fillStyle=ROAD
if(N)ctx.fillRect(px+M-HR,py,RW,M+HR);if(So)ctx.fillRect(px+M-HR,py+M-HR,RW,M+HR)
if(E)ctx.fillRect(px+M-HR,py+M-HR,M+HR,RW);if(W)ctx.fillRect(px,py+M-HR,M+HR,RW)
ctx.strokeStyle=WHITE;ctx.lineWidth=lw
if(N||So){ctx.beginPath();ctx.moveTo(px+M-HR,N?py:py+M-HR);ctx.lineTo(px+M-HR,So?py+S:py+M+HR);ctx.stroke();ctx.beginPath();ctx.moveTo(px+M+HR,N?py:py+M-HR);ctx.lineTo(px+M+HR,So?py+S:py+M+HR);ctx.stroke()}
if(E||W){ctx.beginPath();ctx.moveTo(W?px:px+M-HR,py+M-HR);ctx.lineTo(E?px+S:px+M+HR,py+M-HR);ctx.stroke();ctx.beginPath();ctx.moveTo(W?px:px+M-HR,py+M+HR);ctx.lineTo(E?px+S:px+M+HR,py+M+HR);ctx.stroke()}
if((N||So)&&(E||W)){ctx.fillStyle=ROAD;ctx.fillRect(px+M-HR+1,py+M-HR-1,RW-2,lw+2);ctx.fillRect(px+M-HR+1,py+M+HR-lw,RW-2,lw+2);ctx.fillRect(px+M-HR-1,py+M-HR+1,lw+2,RW-2);ctx.fillRect(px+M+HR-lw,py+M-HR+1,lw+2,RW-2)}
ctx.strokeStyle=WHITE;ctx.lineWidth=lw;ctx.setLineDash([S*0.07,S*0.05]);ctx.lineDashOffset=-S*0.035
if(sum===2){if(N||So){ctx.beginPath();ctx.moveTo(px+M,py);ctx.lineTo(px+M,py+S);ctx.stroke()};if(E||W){ctx.beginPath();ctx.moveTo(px,py+M);ctx.lineTo(px+S,py+M);ctx.stroke()}}
else if(sum===3){if(!N){ctx.beginPath();ctx.moveTo(px,py+M);ctx.lineTo(px+S,py+M);ctx.stroke();ctx.beginPath();ctx.moveTo(px+M,py+M+HR);ctx.lineTo(px+M,py+S);ctx.stroke();ctx.setLineDash([]);ctx.lineWidth=lw*2;ctx.beginPath();ctx.moveTo(px+M-HR+3,py+M+HR+6);ctx.lineTo(px+M,py+M+HR+6);ctx.stroke();ctx.lineWidth=lw}
if(!So){ctx.beginPath();ctx.moveTo(px,py+M);ctx.lineTo(px+S,py+M);ctx.stroke();ctx.beginPath();ctx.moveTo(px+M,py);ctx.lineTo(px+M,py+M-HR);ctx.stroke();ctx.setLineDash([]);ctx.lineWidth=lw*2;ctx.beginPath();ctx.moveTo(px+M,py+M-HR-6);ctx.lineTo(px+M+HR-3,py+M-HR-6);ctx.stroke();ctx.lineWidth=lw}
if(!W){ctx.beginPath();ctx.moveTo(px+M,py);ctx.lineTo(px+M,py+S);ctx.stroke();ctx.beginPath();ctx.moveTo(px+M+HR,py+M);ctx.lineTo(px+S,py+M);ctx.stroke();ctx.setLineDash([]);ctx.lineWidth=lw*2;ctx.beginPath();ctx.moveTo(px+M+HR+6,py+M);ctx.lineTo(px+M+HR+6,py+M+HR-3);ctx.stroke();ctx.lineWidth=lw}
if(!E){ctx.beginPath();ctx.moveTo(px+M,py);ctx.lineTo(px+M,py+S);ctx.stroke();ctx.beginPath();ctx.moveTo(px,py+M);ctx.lineTo(px+M-HR,py+M);ctx.stroke();ctx.setLineDash([]);ctx.lineWidth=lw*2;ctx.beginPath();ctx.moveTo(px+M-HR-6,py+M-HR+3);ctx.lineTo(px+M-HR-6,py+M);ctx.stroke();ctx.lineWidth=lw}}
else if(sum===4){ctx.beginPath();ctx.moveTo(px+M,py);ctx.lineTo(px+M,py+S);ctx.stroke();ctx.beginPath();ctx.moveTo(px,py+M);ctx.lineTo(px+M-HR,py+M);ctx.stroke();ctx.beginPath();ctx.moveTo(px+M+HR,py+M);ctx.lineTo(px+S,py+M);ctx.stroke();ctx.setLineDash([]);ctx.lineWidth=lw*2;ctx.beginPath();ctx.moveTo(px+M-HR+6,py+M);ctx.lineTo(px+M-HR+6,py+M+HR-3);ctx.stroke();ctx.beginPath();ctx.moveTo(px+M+HR-6,py+M-HR+3);ctx.lineTo(px+M+HR-6,py+M);ctx.stroke();ctx.lineWidth=lw}
ctx.setLineDash([]);ctx.lineDashOffset=0;ctx.globalAlpha=1
}
function drawEmojis(px,py,tile){
if(!tile.emojis)return
ctx.textAlign='center';ctx.textBaseline='middle'
tile.emojis.forEach(function(em){
ctx.font=Math.round(TS*em.s)+'px serif'
ctx.fillText(em.e, px+TS/2+em.x*TS, py+TS/2+em.y*TS)
})
}
// === SIMULATION ===
function recalc(){
var eco=30,sat=35,env=55,traf=10,sup=10,reach=20
for(var r=0;r<ROWS;r++)for(var c=0;c<COLS;c++){
if(!grid[r][c])continue
var tile=TILES[grid[r][c].t],ef=tile.eff||{}
eco+=ef.eco||0;sat+=ef.sat||0;env+=ef.env||0;traf+=ef.traf||0;sup+=ef.sup||0
// Connection check
var conn=rotConn(tile.conn,grid[r][c].r)
var dirs=[[-1,0,0,2],[0,1,1,3],[1,0,2,0],[0,-1,3,1]],any=false
for(var d=0;d<dirs.length;d++){if(!conn[dirs[d][2]])continue;var nr=r+dirs[d][0],nc=c+dirs[d][1]
if(nr>=0&&nr<ROWS&&nc>=0&&nc<COLS&&grid[nr][nc]){var nb=rotConn(TILES[grid[nr][nc].t].conn,grid[nr][nc].r);if(nb[dirs[d][3]])any=true;else traf++}else traf++}
if(tile.cat===1&&!any){reach-=4}else if(tile.cat===1)reach+=3
// Neighbors
if(tile.n==='Wohngebiet'||tile.n==='Hochhäuser'){var nb2=[];if(r>0&&grid[r-1][c])nb2.push(TILES[grid[r-1][c].t]);if(c<COLS-1&&grid[r][c+1])nb2.push(TILES[grid[r][c+1].t]);if(r<ROWS-1&&grid[r+1][c])nb2.push(TILES[grid[r+1][c].t]);if(c>0&&grid[r][c-1])nb2.push(TILES[grid[r][c-1].t])
nb2.forEach(function(n){if(n.n==='Park'||n.n==='Spielplatz'){sat+=2;env+=1};if(n.n==='Fabrik'){sat-=4;env-=3};if(n.n.indexOf('Laden')>=0||n.n.indexOf('Supermarkt')>=0){sup+=2;sat+=1}})}
}
var cl=function(v){return Math.min(100,Math.max(0,Math.round(v)))}
scores={eco:cl(eco),sat:cl(sat),env:cl(env),traf:cl(traf),sup:cl(sup),reach:cl(reach)}
}
function nextRound(){
if(gameOver)return;round++;budget+=income;recalc()
if(round>maxRounds){gameOver=true
var total=Math.round(0.22*scores.eco+0.24*scores.sat+0.18*scores.env+0.16*scores.sup+0.12*scores.reach+0.08*(100-scores.traf))
document.getElementById('end-t').textContent=total>=50?'🎉 Geschafft!':'😔'
document.getElementById('end-p').textContent='Score: '+total
document.getElementById('ov-end').classList.remove('hidden')
return}
renderUI()
}
// === RENDER ===
var hoverR=-1,hoverC=-1
function render(){
ctx.fillStyle='#c8d0b8';ctx.fillRect(0,0,cv.width,cv.height)
for(var r=0;r<ROWS;r++)for(var c=0;c<COLS;c++){
var px=c*TS,py=r*TS,cell=grid[r][c]
if(cell){var conn=rotConn(TILES[cell.t].conn,cell.r);drawTile(px,py,conn,1);drawEmojis(px,py,TILES[cell.t])}
else{ctx.fillStyle=GREEN;ctx.fillRect(px,py,TS,TS)}
ctx.strokeStyle='rgba(0,0,0,0.06)';ctx.lineWidth=0.4;ctx.strokeRect(px,py,TS,TS)
if(r===hoverR&&c===hoverC&&sel>=0&&!cell){drawTile(px,py,TILES[sel].conn,0.3)}
}
requestAnimationFrame(render)
}
function renderUI(){
document.getElementById('h-bud').textContent=budget
document.getElementById('h-rnd').textContent=round+'/'+maxRounds
document.getElementById('h-inc').textContent='+'+income+'/Runde'
if(sel>=0&&budget<TILES[sel].cost)sel=-1
// Params
var ps=[{n:'Wirtschaft',i:'💰',v:scores.eco},{n:'Zufriedenheit',i:'😊',v:scores.sat},{n:'Umwelt',i:'🌿',v:scores.env},{n:'Verkehr',i:'🚗',v:scores.traf,inv:1},{n:'Versorgung',i:'🛒',v:scores.sup},{n:'Erreichbarkeit',i:'🔗',v:scores.reach}]
document.getElementById('params').innerHTML=ps.map(function(p){var g=p.inv?p.v<40:p.v>55,b=p.inv?p.v>65:p.v<30,col=b?'#c07a6b':g?'#5a8a5e':'#c4a35a';return'<div class="param"><div class="param-i">'+p.i+'</div><div class="param-inf"><div class="param-n">'+p.n+'</div><div class="param-bar"><div class="param-bar-f" style="width:'+p.v+'%;background:'+col+'"></div></div></div><div class="param-v" style="color:'+col+'">'+p.v+'</div></div>'}).join('')
document.getElementById('msgs').innerHTML=messages.slice(-5).reverse().map(function(m){return'<div class="msg '+m.type+'">'+m.text+'</div>'}).join('')
// Tools
;[['road-tools',0],['building-tools',1],['nature-tools',2]].forEach(function(pair){
var html='';TILES.forEach(function(t,i){if(t.cat!==pair[1])return;var af=budget>=t.cost
html+='<div class="tb'+(sel===i?' on':'')+(af?'':' off')+'" data-t="'+i+'"><span class="te">'+(t.emoji||'🛣️')+'</span>'+t.n+'<span class="tc">💰'+t.cost+'</span></div>'})
document.getElementById(pair[0]).innerHTML=html})
document.querySelectorAll('.tb').forEach(function(b){b.addEventListener('click',function(){var i=parseInt(this.dataset.t);sel=sel===i?-1:i;renderUI()})})
}
// === INPUT ===
cv.addEventListener('click',function(e){
if(gameOver)return;var rect=cv.getBoundingClientRect(),mx=(e.clientX-rect.left)*(cv.width/rect.width),my=(e.clientY-rect.top)*(cv.height/rect.height)
var gc=Math.floor(mx/TS),gr=Math.floor(my/TS);if(gr<0||gr>=ROWS||gc<0||gc>=COLS)return
var cell=grid[gr][gc]
if(cell){var rx=mx-gc*TS,ry=my-gr*TS;if(ry<TS*0.35){if(rx<TS*0.4){cell.r=(cell.r+3)%4;return};if(rx>TS*0.6){cell.r=(cell.r+1)%4;return}};return}
if(sel>=0&&budget>=TILES[sel].cost){grid[gr][gc]={t:sel,r:0};budget-=TILES[sel].cost;recalc();renderUI()}
})
cv.addEventListener('mousemove',function(e){var rect=cv.getBoundingClientRect();hoverC=Math.floor((e.clientX-rect.left)*(cv.width/rect.width)/TS);hoverR=Math.floor((e.clientY-rect.top)*(cv.height/rect.height)/TS);if(hoverR<0||hoverR>=ROWS||hoverC<0||hoverC>=COLS){hoverR=-1;hoverC=-1}})
cv.addEventListener('mouseleave',function(){hoverR=-1;hoverC=-1})
// Touch support
cv.addEventListener('touchstart',function(e){e.preventDefault();var t=e.touches[0],rect=cv.getBoundingClientRect(),mx=(t.clientX-rect.left)*(cv.width/rect.width),my=(t.clientY-rect.top)*(cv.height/rect.height);var gc=Math.floor(mx/TS),gr=Math.floor(my/TS);if(gr<0||gr>=ROWS||gc<0||gc>=COLS)return;var cell=grid[gr][gc];if(cell){var rx=mx-gc*TS,ry=my-gr*TS;if(ry<TS*0.35){if(rx<TS*0.4){cell.r=(cell.r+3)%4;return};if(rx>TS*0.6){cell.r=(cell.r+1)%4;return}};return};if(sel>=0&&budget>=TILES[sel].cost){grid[gr][gc]={t:sel,r:0};budget-=TILES[sel].cost;recalc();renderUI()}},{passive:false})
cv.addEventListener('touchmove',function(e){e.preventDefault();var t=e.touches[0],rect=cv.getBoundingClientRect();hoverC=Math.floor((t.clientX-rect.left)*(cv.width/rect.width)/TS);hoverR=Math.floor((t.clientY-rect.top)*(cv.height/rect.height)/TS);if(hoverR<0||hoverR>=ROWS||hoverC<0||hoverC>=COLS){hoverR=-1;hoverC=-1}},{passive:false})
document.addEventListener('keydown',function(e){if(e.key==='Escape'){sel=-1;renderUI()}})
document.getElementById('btn-next').addEventListener('click',nextRound)
document.getElementById('btn-go').addEventListener('click',function(){document.getElementById('ov-intro').classList.add('hidden');renderUI()})
document.getElementById('btn-again').addEventListener('click',function(){document.getElementById('ov-end').classList.add('hidden');location.reload()})
recalc();renderUI();render()
</script>
</body>
</html>
@@ -0,0 +1,126 @@
# Kofferdetektiv · Folge-Aufträge für Sim-Entwickler
**Datum:** 2026-06-15
**Kontext:** Nach Vollausbau-Iteration sind drei Punkte aus dem 17-Punkte-Brief offen, weil sie externe Werkzeuge oder größere Sim-Code-Architektur benötigen. Atlas liefert hier Briefing-Material für die nächste Welle.
## Was die Plattform schon liefert
- 46 Glossar-Begriffe (DB), 41 Lehrplan-Anker (AT/CH/DE/LI), Modul-Detailseite mit Bildergalerie
- Sim-Engine: 35 Glossar-Tooltips in Fundsachen, Joystick-Vollanschlag-Sprint (Touch), Audio-Lazy-Load (preload='metadata' statt 'auto'), prominenter Reflexionsfrage-Block mit 40-Zeichen-Minimum
- Datenschutz: `case_country` nur nach Lösung im GGS_LIVE_STATE, vorher nur `case_iso2` für die Lehrer-Sicht
- Reflexionen sichtbar bei Lehrer (Cockpit) + Schüler (Profil)
- 2D-Variante (`/kofferdetektiv-2d`) als WIP-Slot vorbereitet
## Punkt 9 — Englisch-Variante
**Status:** Vorbereitet (alle Texte in `data.js` und UI-Konstanten gebündelt laut INTEGRATION.md).
**Aufgabe:**
- Pro Text-Block zweite Sprach-Variante: `KD_HINTS_EN`, `KD_TUTORIAL_EN`, `KD_FUNDSACHEN_EN`, `KD_GLOSSARY_EN`
- Sprach-Toggle im Header (🌐 DE/EN, persistent in localStorage)
- Plattform-Glossar `glossar.php` müsste eine `?lang=en`-Variante unterstützen — dafür DB-Spalten `short_en`, `text_en` ergänzen
- Lehrplan-Anker bleiben sprachneutral (Quote ist Original-Lehrplan-Text)
**Umfang:**
- ~3.000 Strings zu übersetzen
- DB-Erweiterung + glossar-API-Anpassung
- Sim-Code Sprach-Switch-Logik
- Erweiterung der Plattform: `STUDENT_LANG` parallel zu `STUDENT_EASY`
**Empfehlung:** Erst dann angehen, wenn eine nicht-deutschsprachige Klasse das Modul testet — sonst Investition ohne Validierung.
## Punkt 12 — 3D-Globe-Endscreen
**Status:** Konzept skizziert im 17-Punkte-Brief.
**Aufgabe:** Im Endscreen statt klassischer Score-Liste eine Three.js-Globus-Komponente:
- Erde rotiert (Geschwindigkeit ~10°/s)
- Gelöste Länder leuchten goldgelb (`#e8c547`)
- Ungelöste Länder dunkel
- Klick auf Land → Visitenkarte
- Hover-Hint: „Du hast 4 von 30 Ländern erforscht"
**Technik:**
- Eigene Three.js-Komponente, nicht im Halle-Scene-Graph
- Erd-Modell: Sphere mit Texture (Natural Earth oder NASA Blue Marble, ~2 MB)
- Länder-Polygone: GeoJSON aus Natural Earth 50m (schon in `assets/data/outlines.json` vorhanden)
- Polygon-Extrusion in Three.js für Hover-Effekt
- Performance: Geometrie pre-computed, einmal pro Modul-Start
**Vorbild:** Sonnensystem-Sim hat ähnliche Three.js-Globus-Mechanik.
**Empfehlung:** Größerer Edit, etwa 600-800 LoC. Würde von Sim-Entwickler-Welle (Three.js-erfahren) gut hingebracht werden.
## Punkt 13 — Aussprache-Audio für Wahrzeichen
**Status:** Konzept skizziert.
**Aufgabe:** Schwer auszusprechende Ländernamen und Wahrzeichen bekommen einen 🔊-Knopf in der Visitenkarte, der die Aussprache als kurze MP3 abspielt.
**Liste der Audio-Files (Beispiele):**
- Sigiriya (Sri Lanka)
- Mtatsminda (Georgien)
- Petra tou Romiou (Zypern)
- São Tomé und Príncipe
- Côte d'Ivoire
- Bhutan (Druk Yul)
- ~30 Wahrzeichen + ~30 Hauptstädte = ~60 Audio-Dateien
**Generierung:**
- ElevenLabs (gleicher Workflow wie die 5 PA-Durchsagen im Modul)
- Stimme: ruhig, klar, mit korrekter Phonetik
- Format: MP3, 64 kbps mono, ~3-5 Sek pro Datei, Gesamtgröße ~3 MB
- Pfad: `App/sims/kofferdetektiv/assets/audio/pronounce/<iso2>-<slug>.mp3`
**Sim-Code-Edit:**
- In `fillCountryCard()` einen 🔊-Button neben dem Wahrzeichen-Namen
- Click → neues Audio-Element mit lazy load
- Persistente Audio-Cache wie bei den SFX
**Empfehlung:** Aussprache-Audio ist ein Inklusions-Feature — hilfreich besonders für Lernende mit Lese-Schwierigkeiten oder Easy-Sprache-Flag. Sehr empfehlenswert, mittlerer Aufwand.
## Offene Punkte (kleiner — Atlas oder Sim-Entwickler)
### Punkt 10 — Lehrer-Fall-Verlauf-Tab (Heatmap)
Der View `view=module&student_id=X` liefert schon `runs` mit `results.countries[]`. Atlas könnte im Modul-Detail-Modal (`teacher.html` `openModuleDetail`) für Kofferdetektiv einen Spezialfall einbauen:
- Heatmap-Tabelle: Schüler × Land × Status (gelöst/teilweise/nicht-gelöst)
- Histogramm Hinweis-Effizienz pro Schüler:in
- Trend „Zeit pro Fall" über Durchgänge
**Aufwand:** mittel (200-300 LoC), reines Frontend.
### Punkt 6 — Easy-Sprache durchgängig in Sim-Tutorial
Glossar-Begriffe nutzen schon die Plattform-Mechanik (DB `easy_language`-Auto-Switch). Aber die in-Sim-Texte (Tutorial, Hinweise, Fundsachen-Infos) sind nicht Easy-versioniert. Lösung: pro Text-Block zwei Varianten in `data.js`:
```js
const KD_HINTS_EASY = [
{ key:'population', text: c => `Auf einem Bild steht: In dem Land wohnen etwa ${c.population} Menschen.` },
// …
];
```
Sim zieht Easy-Variante wenn `window.STUDENT_EASY === true`.
**Aufwand:** mittel (~500 Strings zu schreiben).
### Punkt 7 — Bonus-Material Bottom-Sheet
Aktuell zeigt `fillCountryCard()` nach Lösung schon Wahrzeichen-Bild + Trivia + Daten. Ein zusätzlicher Bottom-Sheet wäre Polish, kein Muss. Verschiebbar bis Lehrperson-Feedback.
## Was Atlas selbst noch nachziehen kann
- Punkt 10 Heatmap (mittel, ohne Sim-Entwickler)
- KD_GLOSSARY DB-Spiegelung (erledigt am 2026-06-15)
- Easy-Sprache-Test mit echtem Schüler-Account (STUDENT_EASY=true)
## Briefing-Sicht
Die 3 großen Punkte sind alle „Polish-Plus" — keine blockiert die produktive Nutzung. Empfohlene Reihenfolge:
1. **Punkt 13 Aussprache-Audio** — höchster Inklusions-Wert, mittlerer Aufwand, kann direkt nach Klassen-Test in Auftrag
2. **Punkt 6/10 Easy-Sprache Sim-Tutorial + Lehrer-Heatmap** — Atlas-Inhouse-Arbeit, kann beim nächsten Atlas-Sprint
3. **Punkt 12 3D-Globe-Endscreen** — Sim-Entwickler-Welle, wenn Wal oder andere Three.js-Sim ohnehin in Arbeit
4. **Punkt 9 Englisch** — erst wenn Bedarf konkret vorhanden ist
---
**Kontakt:** Atlas (Plattform-Instanz) — bei Plattform-Fragen direkt fragen, dies hier ist nur ein Auftrags-Briefing.
@@ -0,0 +1,124 @@
# Kofferdetektiv · Integrations-ToDo
**Datum:** 2026-06-14
**Stand:** 3D-Version komplett fertig geliefert (siehe `.humanInput/Kofferdetektiv/app_to_deploy/INTEGRATION.md`). 2D-Version folgt später.
## Was geliefert ist
| Datei | Größe | Inhalt |
|---|---|---|
| `sims/kofferdetektiv/game.html` | 769 LoC | UI, Header, Overlays |
| `sims/kofferdetektiv/engine.js` | 4274 LoC | 3D-Welt + Fall-Logik |
| `sims/kofferdetektiv/data.js` | 1867 LoC | 30 Länder, Glossar, Hinweise |
| `sims/kofferdetektiv/welt.html` | 147 LoC | Welt-Übersicht aller Länder |
| `sims/kofferdetektiv/assets/` | 133 MB | 3D-Modelle, Audio, Bilder, Texturen |
| `pages/kofferdetektiv.php` | Wrapper-Vorschlag |
| `INTEGRATION.md` | Detailbriefing |
## Plattform-Anbindung von der Sim aus
Laut INTEGRATION.md bereits sauber drin:
- Header §4.2 mit `GGS_TUTORIAL.replay()`, 📚, 🔊, 🔄, 🏠
- `GGS_LIVE_STATE()` mit 12 Feldern
- `GGS_TUTORIAL` mit eigenem 5-Schritte-Tutorial
- Submit über `/api/progress.php` mit `__GGS_LIVE__.activeMs`
- localStorage-Autosave `kofferdetektiv.v1`
- Lernarbeit-Sprache
- iPad-tauglich (Touch-Joystick + Blick-Drag, ≥36 px, 16px-Inputs)
- 30 Fall-Länder in Klassen A/B/C, 3 Stufen mit Hinweisverteilung
- 20 Hinweis-Kategorien (Einwohnerzahl, Fläche, BIP, Flagge, Wappen …)
- 12 Flughäfen weltweit als Einsatzort
- 16 MP3 Hintergrundmusik + 8 SFX + 5 PA-Durchsagen
- Länderatlas im Header (gelb/grün-Markierung persistent)
## Atlas-ToDo
### A · Code-Integration (sofort, lokal)
- [x] ToDo-Liste schreiben (diese Datei)
- [ ] Sim-Files nach `App/sims/kofferdetektiv/` kopieren (133 MB)
- [ ] `welt.html` prüfen — was ist das, soll's deployed werden?
- [ ] Wrapper an Plattform-Standard angleichen (mode-locked/teacher_started/free, student_modules-Override, easy_language, locked-Page, WIP-Page wenn game.html fehlt)
- [ ] Sim-File umbenennen auf `game-3d.html` (Vorbereitung 2D-Slot), Wrapper-Pfad anpassen
- [ ] Wrapper `pages/kofferdetektiv-3d.php` aus aktuellem `pages/kofferdetektiv.php` ableiten
- [ ] Platzhalter `pages/kofferdetektiv-2d.php` mit WIP-Page anlegen
- [ ] Platzhalter `sims/kofferdetektiv/game-2d.html` (leer / WIP) anlegen — wartet auf Sim-Lieferung
### B · Datenbank
- [ ] `module_info`-Eintrag (Title „Kofferdetektiv", Icon 🕵️, Status `geplant`, Dauer ~25 min, Alter 11 16)
- SQL: `App/Don_t_Deploy/2026-06-14-kofferdetektiv-module-info.sql`
- [ ] SQL apply auf Prod (WIP-Page wird damit live)
- [ ] Status auf `beta` ziehen, sobald Sim deployed und einmal durchgetestet
- [ ] modul-detail-Partial: Sonderfall für Kofferdetektiv (zwei Buttons: 3D / 2D — analog Klima)
### C · Glossar (Atlas reicht an Glossar-Instanz weiter)
- [ ] 20 Begriffe in `glossar`-Tabelle anlegen mit `module_id='kofferdetektiv'`:
Einwohnerzahl · Fläche · Bevölkerungsdichte · BIP pro Kopf · Lebenserwartung · UN-Mitglied · Staatsform · Zeitzone · Kontinent · Region · Amtssprache · Währung · Nachbarstaat · Klima · Exportprodukt · Hauptstadt · Flagge · Länderumriss · Internet-Domain · Wahrzeichen
- [ ] Easy-Sprache-Variante für jeden Begriff
- [ ] **Kurztexte liegen bereits in `data.js` `KD_GLOSSARY`** — Glossar-Instanz kann sie übernehmen statt neu schreiben
### D · Lehrplan-Anker (Atlas reicht an Lehrplan-Instanz)
- [ ] 8 12 Anker pro Land (AT/CH/DE/LI)
- Mögliche Fachbezüge:
- **AT GW Sek I:** Kompetenz 1.x (Räume und Klimazonen weltweit), 3.x (Vielfalt Länder/Kulturen), 4.x (Migration und Mobilität)
- **CH RZG Sek I:** RZG.4.x (globale Vernetzung), RZG.5.x (Reisen + Verkehr)
- **DE Sek I:** Länder der Welt, globale Mobilität, Kulturkunde
- **Politische Bildung:** Internationale Mobilität, Zoll, Datenschutz im Reisekontext
- [ ] Verknüpfung in DB `module_kompetenz_map` setzen
### E · Lehrer-Backend
- [ ] **admin-modules.html:** Kofferdetektiv als wählbares Modul für Klassen-Freigabe (sollte automatisch funktionieren sobald `module_info` da ist)
- [ ] **Live-Cockpit `live.php`:** `case_country` nur für Lehrpersonen-Sicht durchreichen (im Schüler-Heartbeat NICHT zeigen — Sim trennt das schon, Atlas verifiziert)
- [ ] **LIVE_PRIMARY_FIELDS['kofferdetektiv']** in `teacher.html` einbauen (Vorlage in INTEGRATION.md §Vorschlag)
- [ ] **Ergebnis-Tab `results.php`:** Kofferdetektiv-Submission-Schema (Fall-Liste pro Schüler, Lösungs-Quote pro Land, Hinweis-Effizienz) sichtbar machen
- [ ] **Benchmark::scoreKofferdetektiv()** in `App/php/lib/Benchmark.php` (Formel-Vorlage in INTEGRATION.md §Vorschlag)
- Base 60 % Lösungsquote + 25 % Erstversuch + 15 % Hinweis-Effizienz bis 20 % Fehlversuch-Malus
- [ ] **Reflexionsfrage** automatisch vor Submit zeigen — Sim macht das schon laut INTEGRATION.md, Atlas verifiziert via Playwright
### F · Mode-Steuerung (Plattform-Standard)
- [ ] Wrapper-Hardening: `mode='teacher_started'` mit `forcedLevel` durchreichen (sperrt Stufenwahl in Sim)
- [ ] Wrapper-Hardening: `mode='locked'` mit Locked-Page (Plattform-Standard)
- [ ] Wrapper-Hardening: `student_modules`-Override pro Schüler:in
- [ ] Wrapper-Hardening: `STUDENT_EASY=true` aus `students.easy_language` ableiten
### G · Tests
- [ ] `sims/kofferdetektiv/game.html` `.dontDeploy/playwright/tests/stufe1-smoke/routes-static.spec.js` Slug ergänzen
- [ ] `sim-platform-context.spec.js`: Slug `kofferdetektiv` für `__GGS__ + live-client + GGS_LIVE_STATE`
- [ ] Stufe-2-Flow-Test: Boot → Onboarding → Erster Fall starten → GGS_LIVE_STATE liefert `case_index/cases_total`
- [ ] Performance-Check: Boot-Zeit auf iPad-equivalentem Viewport unter ~5s (3D-Modelle sind 1,8 MB + 0,3 MB)
### H · Card-Image + Modul-Detailseite
- [ ] `App/assets/img/card-kofferdetektiv.png` generieren (Flat Scandinavian, Atlas)
- [ ] Modul-Detailseite `modul-kofferdetektiv` (Plattform-Standard, kommt automatisch durch `modul-<id>.php`-Partial wenn `module_info` da ist)
- [ ] DALL-E-Prompt: „Flat scandinavian illustration, airport luggage counter at night, detective with magnifying glass over open suitcase, soft warm palette …"
### I · Deploy-Sequenz
- [ ] **Schritt 1 jetzt:** module_info-SQL apply → WIP-Page live unter `geograsim.at/kofferdetektiv-3d`
- [ ] **Schritt 2:** Wrapper + Sim-Files lokal vorbereiten + integrieren (Atlas heute)
- [ ] **Schritt 3:** Thomas-Test lokal → Feedback
- [ ] **Schritt 4:** Deploy Sim-Files + Wrapper auf Prod
- [ ] **Schritt 5:** Status `geplant``beta`
- [ ] **Schritt 6:** Playwright-Suite ergänzen + grün ziehen
- [ ] **Schritt 7:** Glossar + Lehrplan-Anker an Schwester-Instanzen
### J · 2D-Version (offen, später)
- [ ] Konzept für 2D-Variante mit Thomas klären
- Gleiche Mechanik als 2D-Top-Down-Variante?
- Vereinfachte iPad-First-Variante ohne 3D-Performance-Anforderungen?
- Mini-Spiel-Variante (z. B. einzelner Koffer als Drag-Drop)?
- [ ] Sim-Lieferung abwarten
- [ ] `sims/kofferdetektiv/game-2d.html` einsetzen
- [ ] Wrapper `pages/kofferdetektiv-2d.php` aktivieren
- [ ] modul-detail: zweiten Button freischalten
### K · Optional / Folgearbeit
- [ ] Glossar-Tooltip-Plattform-Lib mit `KD_GLOSSARY` aus data.js synchronisieren (DB-Single-Source-of-Truth)
- [ ] Reise-Plakate in der 3D-Halle: Lehrplan-Verweis pro Land
- [ ] Welt-Übersicht (`welt.html`) — eigene Sub-Route oder per Atlas-Header zugänglich machen?
- [ ] Audio-Lizenz-Doku: ElevenLabs-Sprachausgaben + Sketchfab-Lizenzen abklären
---
**Sofort-Plan:** A (Code), B (DB+SQL+WIP-Deploy) — beides geht heute.
**Mittelfrist:** C, D, E, F (Glossar, Lehrplan, Lehrer-Backend, Mode-Hardening) — über die nächsten Sessions.
**Nach Thomas-Test:** G (Tests), H (Card-Image), I (Deploy-Sequenz Sim-Files).
**Wartend:** J (2D-Version).
@@ -0,0 +1,161 @@
# Kofferdetektiv · Verbesserungsvorschläge
**Datum:** 2026-06-15
**Sicht:** Atlas (Plattform-Instanz), nach Vollausbau Glossar + Lehrplan
**Vorgehen:** sortiert nach Effekt vs. Aufwand
---
## Sicht von außen — die Sim ist didaktisch eine Schatzkiste
30 Fall-Länder, 20 Hinweis-Kategorien, 30 Fundsachen mit didaktisch starken Info-Texten, 12 Flughäfen weltweit, 16 Hintergrund-Musikstücke, ~190 Länder zum Auswählen. Das macht den Kofferdetektiv zum **inhaltsstärksten** Modul der Plattform. Die meisten Verbesserungen sind keine Lücken-Schließer, sondern Hebel um das Material noch besser sichtbar zu machen.
---
## Hoher Effekt, kleiner Aufwand
### 1. Glossar-Tooltips IN den Fundsachen-Texten verlinken
Die Fundsachen-Toasts erklären jetzt großartige Dinge („100-ml-Regel", „Roaming", „Powerbank-Sicherheit"). Die 26 neuen Glossar-Begriffe sind alle in der DB — aber die Sim-Toasts verlinken sie noch nicht.
**Aufwand:** Sim-Engine markiert in den Texten Schlagwörter mit `<span class="ggs-g" data-glossar="…">…</span>` (Plattform-Standard, glossar-tooltip.js ist schon eingebunden). Vorlage:
```
"Powerbanks müssen ins <span class='ggs-g' data-glossar='akku-handgepaeck'>Handgepäck</span>,
nie in den aufgegebenen Koffer..."
```
**Effekt:** Lernende klicken den Begriff → sehen Easy-Sprache-Variante. Lehrperson kann darauf bauen, dass das Vokabular klickbar ist.
### 2. Reflexionsfrage prominenter
INTEGRATION.md sagt: Reflexionsfrage „Welcher Hinweis hat dir am meisten geholfen — und welcher Fall war am schwierigsten? Begründe kurz" vor dem Submit. Falls die nur als kleines Textfeld kommt: stärker akzentuieren. Vorschlag: Vollbild-Modal mit dem Bild aus `intro/fall-abgeben.webp`, klares Layout, Submit-Button erst nach Mindest-Text-Länge aktiv (40+ Zeichen).
**Effekt:** echte didaktische Reflexion statt Klick-durch.
### 3. Lehrer-Sicht: case_country prüfen auf Datenschutz
Im Cockpit zeigt `LIVE_PRIMARY_FIELDS['kofferdetektiv']` das Zielland mit Kommentar „nur Lehrpersonen-Sicht". Aber: wenn der Sim den `case_country` in `GGS_LIVE_STATE` einbettet, ist er für Schüler:innen über DevTools im JavaScript sichtbar — der „Lehrer-only" Hinweis ist nur in der teacher.html-Render-Logik. Empfehlung:
- **Variante A:** Sim soll `case_country` **nicht** in GGS_LIVE_STATE liefern. Atlas liest in `/api/live.php` aus der Sim-internen DB (z. B. aus `kofferdetektiv_cases`-Log oder aus dem letzten `assessments.results.countries[]`-Eintrag), nur an Lehrpersonen-Endpoint.
- **Variante B:** Sim verschlüsselt `case_country` mit einem pro-Klasse-Schlüssel, den nur der Lehrer-Cockpit-Backend hat.
Für Pilot-Phase reicht Variante A; das Schüler:innen-Frontend bekommt keinen Anlass, in DevTools zu schauen.
### 4. Welt-Übersicht `welt.html` aus dem Header zugänglich machen
Die existierende `welt.html` (147 LoC, Welt-Atlas-Übersicht aller Länder) ist live unter `/sims/kofferdetektiv/welt.html`, aber nicht verlinkt. Vorschlag: kleiner Header-Button 🌍 im Sim-Header, der die welt.html im neuen Tab öffnet — als Begleit-Referenz „Welt-Übersicht aller 190 Länder".
**Effekt:** Lernende haben ein „großes Bild" zur Hand, didaktisch parallel zur Detektivarbeit.
### 5. Card-Image-Variation für die anderen Cockpit-Stellen
Aktuell: `card-kofferdetektiv.webp` ist die Standard-Karte. Für die Modul-Detailseite + Hero könnte ein zweites Bild („Detektiv mit Lupe über Koffer-Inhalt", aus `assets/img/intro/intro-koffer.webp` oder `intro-terminal.webp`) als Hero-Banner dienen.
---
## Mittlerer Effekt, mittlerer Aufwand
### 6. Easy-Sprache-Modus durch die ganze Sim
Wenn `window.STUDENT_EASY === true`, sollte die Sim:
- statt `KD_GLOSSARY` die Easy-Variante aus DB ziehen (oder lokal eigene Easy-Variante im data.js)
- Hinweis-Texte vereinfachen („Eine Infografik nennt etwa 110 Menschen pro km²" → „Auf einem Bild steht: ungefähr 110 Menschen leben auf 1 Quadrat-Kilometer")
- Wahrzeichen-Bilder ggf. mit Bild-Beschreibung als Alt-Text
- Fundsachen-Infos auf Easy-Sprache-Version umstellen
**Aufwand:** mittel; pro Sektion ein Easy-Pendant. Plattform liefert STUDENT_EASY schon, Sim muss es nur auswerten.
### 7. „Bonus-Material" als Bottom-Sheet zu jedem gelösten Land
Nach Lösung eines Falls könnte ein Bottom-Sheet zusätzliche Info zeigen:
- 1 Wahrzeichen-Foto (gibt's schon in `assets/img/landmarks/`)
- 1 Faktbox aus dem Sim-internen Wissen
- Link zum vollen Glossar-Eintrag der nationalen Symbole (Flagge / Wappen / Staatsform)
**Aufwand:** mittel; UI-Komponente + 30 kurze Texte.
**Effekt:** vertieft das Lernen statt Klick-Bestätigung.
### 8. Performance-Profil dokumentieren
133 MB Sim-Assets, davon 1,8 MB Three.js-Modell (Airport-Diorama) + ~80 MB Hintergrundmusik (16 MP3). Auf Prod ist das fein, weil HTTP/2 und Cache, aber:
- iPad-Erstaufruf könnte langsam wirken
- Schul-Internet mit niedriger Bandbreite testen
- Audio kann später nachgeladen werden (lazy-load nach Boot)
**Empfehlung:** Audio-Verzeichnis `assets/audio/music/` mit `<audio preload="none">` Pattern, lädt erst beim Play.
### 9. Mehrsprachigkeit vorbereitet → tatsächlich Englisch ergänzen
INTEGRATION.md sagt: „Mehrsprachigkeit vorbereitet, alle Texte in data.js/UI-Konstanten gebündelt". Für DACH-Klassen mit nicht-deutschsprachigen Lernenden wäre Englisch-Variante nützlich. Vorschlag: `KD_TEXTS_DE`/`KD_TEXTS_EN`, Schalter im Header.
**Aufwand:** groß (alle Texte übersetzen). **Effekt:** Klassen-Inklusion.
---
## Hoher Effekt, größerer Aufwand
### 10. Lehrer-Auswertung „Fall-Verlauf pro Schüler:in"
Im Lehrer-Cockpit (`results.php`) könnte für Kofferdetektiv ein eigener Tab kommen:
- **Welche Länder** hat Schüler:in X gelöst (Heatmap)
- **Welche Hinweise** wurden am häufigsten gebraucht (Histogramm)
- **Zeit pro Fall** als Trend
- **Reflexionsantworten** als Text-Liste
Schema `assessments.results.countries[]` aus INTEGRATION.md liefert das Material schon. Atlas baut die Auswertung in results.php (ähnlich Heli oder Klima).
### 11. Klassen-Vergleichsmodus
Lehrperson startet `mode='teacher_started'` mit `forcedLevel=2` für die ganze Klasse. Cockpit zeigt: alle Schüler:innen → selbe Stufe → wer hat welches Land schon? Echtzeit-Wettbewerb (ohne Punkte-Druck).
### 12. Globe-Visualisierung im Endscreen
Statt klassischer Score-Liste am Ende: 3D-Globus rotiert, gelöste Länder leuchten goldgelb, ungelöste sind dunkel. „Du hast 4 von 30 Ländern erforscht." Klick auf Land → öffnet noch mal die Visitenkarte.
**Aufwand:** groß (Three.js-Globus + Daten-Binding). **Effekt:** emotionaler Abschluss, klares „mehr ist möglich" Signal.
### 13. Aussprache-Audio für nicht-deutsche Wahrzeichen
30 Wahrzeichen — manche schwer zu lesen (Sigiriya, Mtatsminda, Kotor). Ein 🔊-Knopf in der Visitenkarte spielt die Aussprache (ElevenLabs könnte das vorgenerieren). 30 × ~5 Sekunden = 30 kleine MP3.
**Effekt:** Inklusion für Lernende mit Lese-Schwierigkeiten + Sprach-Vorbild.
---
## Polish und Konsistenz
### 14. Texte-Audit gegen Sprachkonvention
INTEGRATION.md sagt „Lernarbeit-Sprache". Stichprobenartig prüfen:
- Kein „Spiel", „Spielen", „Spieler"
- „Durchgang" statt „Runde"
- „Lehrperson" statt „Lehrkraft"
### 15. Glossar-Begriffe in DB → in `data.js` `KD_GLOSSARY` spiegeln
Die 26 neuen Begriffe sind in der DB, aber `data.js` `KD_GLOSSARY` enthält nur die 20 Hinweis-Kategorien. Für den Fall, dass die Sim offline läuft (Standalone-Test), wäre Konsistenz schön — Atlas könnte ein Python/Node-Script anbieten, das beide Quellen abgleicht.
### 16. Image-Sitemap für Modul-Detailseite
Auf der Modul-Detailseite (`modul-kofferdetektiv`) könnte ein kleiner Bildergalerie-Streifen zeigen: 4-5 In-Sim-Screenshots (Flughafen 3D, Analyse-Ansicht, Visitenkarte, Endscreen, Atlas-Übersicht). Aktuell nur eine Hero-Card.
### 17. Lehrplan-Anker erweitern
Aktuell 8 AT-Anker + 6 CH + 4 DE + 2 LI. Für die volle Tiefe könnten 12 pro Land kommen — z. B. fächerübergreifend in DE die Verbindung zu Wirtschaft/Recht (Verbraucherbildung) und Politik (Internationale Beziehungen).
---
## Was Atlas direkt nachziehen kann (heute oder morgen)
- ✓ Glossar +26 Begriffe (erledigt)
- ✓ Lehrplan-Kompetenzen + 20 Anker (erledigt)
- ✓ Modul-Detail-Seite mit Dual-Button (erledigt)
- ✓ Card-Image (erledigt)
- ⏳ Punkt 4 — Welt-Header-Link auf welt.html
- ⏳ Punkt 5 — Hero-Banner zweites Bild für Modul-Detail
- ⏳ Punkt 16 — Bildergalerie auf Modul-Detail
## Was Sim-Entwickler-Eingriff braucht
- Punkt 1 — Glossar-Tooltips in Fundsachen-Texten
- Punkt 2 — Reflexion-Modal prominenter
- Punkt 3 — case_country aus GGS_LIVE_STATE entfernen
- Punkt 6 — Easy-Sprache durchgängig
- Punkt 7 — Bonus-Material-Bottom-Sheet
- Punkt 9 — Englisch-Variante
- Punkt 12 — Globe-Endscreen
- Punkt 13 — Aussprache-Audio
- Punkt 15 — KD_GLOSSARY mit DB abgleichen
## Was im Plattform-Backend kommt
- Punkt 10 — Lehrer-Cockpit Fall-Verlauf-Tab
- Punkt 11 — Klassen-Vergleichsmodus
---
**Empfehlung Reihenfolge:** Punkt 4 + 5 + 16 sind heute machbar (UI-Polish auf Plattform-Seite). Punkt 1 + 6 sind die größten Hebel für didaktische Tiefe und sollten als nächstes mit dem Sim-Entwickler abgestimmt werden.
@@ -0,0 +1,86 @@
# Deployment-Pending · Stand 2026-06-11
Atlas hat seit 2026-06-10 keinen automatischen Deploy mehr gemacht (Thomas-Regel).
Folgende Fixes liegen **lokal** und sind **noch nicht auf Prod**:
## Kritisch — User-sichtbarer Bug auf Prod
### 1. Klima 2D + 3D · Glossar-Tooltip-Link
**Auf Prod aktuell defekt** — vom Playwright-Run 2026-06-11 02:53 nachgewiesen.
- **Symptom**: in Klima-Sim auf einen Fachbegriff klicken → Popup zeigt
„Voller Glossar-Eintrag →" → Klick öffnet `https://geograsim.at/klima-2d/glossar?term=budget`
(relativer Pfad) → **404**.
- **Root Cause**: `__GGS__.basePath` ist auf Prod ein leerer String `""`.
Truthy-Check in `App/sims/klima/game-2d.html:4193` und `game-3d.html:5699`
fiel deshalb fälschlich auf den Fallback `/geograsim/App` zurück.
- **Fix lokal seit 2026-06-10**: `typeof basePath === 'string'` statt
Truthy-Check. Greift sofort, wenn deployt.
- **Test-Skript**: `.dontDeploy/playwright/tests/stufe1-smoke/glossar-tooltip-regression.spec.js`
## Mittelschwer — Polish/Konsistenz
### 2. Lehrer-Cockpit (neu)
`App/teacher.html` + `App/php/api/live.php` haben einen komplett neuen
„🎯 Cockpit"-Modus (Status-Tiles, Top 3/Bottom 3, Klassen-Säulen,
Schülerliste). Plus `App/php/lib/Benchmark.php` als neue Lib.
Live nur lokal, Prod hat noch den alten Live-Tab.
### 3. Farmer-Sim · GGS_LIVE_STATE erweitert
`App/sims/farmer/game.html` liefert jetzt `simId`, `phase`, `progressPct`
auf Prod fehlt das. Folge: im Lehrer-Cockpit (sobald deployt) zeigt
Farmer keine Phase-Beschriftung.
### 4. Weltküche · Moussaka-Lamm-Datenmodell
`App/sims/weltkueche/assets/data/production-matrix.json` + `dishes.json`
haben neue Zutat `lamm` und Moussaka nutzt sie. Plus
`App/pages/weltkueche-uebersicht.html` neu generiert.
### 5. Weltküche · 6 SFX-Files
`App/sims/weltkueche/sounds/*.mp3` (6 Files, je 941 KB) sind lokal,
nicht auf Prod. Spiel funktioniert auch ohne, aber Audio-Feedback fehlt.
### 6. Modul-Info-Seiten Weltküche + Fluggesellschaft
`App/pages/modul-weltkueche.php` und `modul-fluggesellschaft.php` — auf
Prod 404, lokal 200.
### 7. Heli M6 Mellau-Bug
DB-Update auf Prod schon gemacht (Mellau-Lat/Lon korrigiert), aber
`App/sims/heli/game.html` mit Startfreigabe-Validierung NICHT deployed.
## Klein — Sprache/Mapping
### 8. Weltküche Sprach-Fix
„aus dort kaufen" → „von dort bekommen" — lokal gefixt, Prod hat noch
den alten Text.
### 9. Glossar-Tooltip-Lib
`App/assets/js/glossar-tooltip.js` Regex erweitert für `/php/api`
gilt für die Weltküche-Tooltips. Lokal gefixt, Prod hat noch die
alte Regex.
---
## Empfehlung an Thomas
Wenn der Deployment-Stopp eine Pause auf bestimmte Risiken war:
- **Mindestens den Klima-Glossar-Bug** sollten wir deployen — der ist
jetzt sichtbar in jeder Klima-Sim-Sitzung, jeder Klick auf einen
Fachbegriff landet im 404.
- Idealerweise alles oben Listed in einem **gebündelten Deploy**.
Wenn der Stopp aber etwas mit dem Live-Cockpit zu tun hat (Backend-Erweiterung):
- Den Cockpit-Stuff abtrennen und schrittweise rollen.
Atlas wartet auf grünes Licht, bevor irgendetwas hochgeht.
— Atlas · 2026-06-11
@@ -0,0 +1,69 @@
# Brief an Meister · `www.geograsim.at` lädt nicht im Browser
**Stand 2026-06-11** · Beobachtung von Thomas, Diagnose von Atlas.
## Symptom
- `https://geograsim.at/glossar` → funktioniert
- `https://www.geograsim.at/glossar` → lädt im Browser nicht
## Was schon geprüft ist (Diagnose von Atlas, read-only auf Prod)
| Aspekt | Status |
|---|---|
| DNS-Auflösung beider Subdomains | ✓ beide → 85.215.155.82 (geprüft via `1.1.1.1`) |
| Apache-VirtualHost `002-geograsim.conf` | ✓ enthält `ServerAlias www.geograsim.at` |
| HTTP vom Server lokal (curl mit `--resolve`) | ✓ beide URLs liefern HTTP/2 200 |
| SSL-Zertifikate | ⚠️ **zwei separate Let's-Encrypt-Certs** |
## Cert-Detail
```
geograsim.at:
CN = geograsim.at
Issuer: Let's Encrypt E7
SAN: DNS:geograsim.at ← nur eine Domain im Cert!
notAfter: 12.07.2026
www.geograsim.at:
CN = www.geograsim.at
Issuer: Let's Encrypt E8
SAN: vermutlich nur www.geograsim.at
notAfter: 12.07.2026
```
→ Vermutung: Apache lädt nur eines der beiden Certs in den 443-VirtualHost, und SNI-Fallback funktioniert für den jeweils anderen Host nicht. Browser zeigt dann SSL-Fehler statt 200.
## Was zu tun ist (Vorschlag, du kennst das besser)
### Option A — empfohlen: Multi-SAN-Cert aus beiden machen
```bash
certbot --apache --cert-name geograsim.at \
-d geograsim.at -d www.geograsim.at
```
Das erzeugt **ein einzelnes Cert** mit beiden Hostnames in SAN. Sauberer als zwei separate Certs. Apache-Auto-Renew funktioniert automatisch weiter.
### Option B — zusätzlich: 301-Redirect `www → apex`
In `002-geograsim.conf` ergänzen (oder als eigene `.htaccess`-Regel):
```apache
<VirtualHost *:443>
ServerName www.geograsim.at
RewriteEngine On
RewriteRule ^(.*)$ https://geograsim.at$1 [R=301,L]
</VirtualHost>
```
Vorteil: eine kanonische Domain (besser für SEO + Tracking-Konsistenz). Schüler:innen + Lehrpersonen sehen immer `geograsim.at` ohne `www`.
## Was Atlas weiter braucht von Thomas
Falls die Lösung nicht greift: Screenshot aus den Browser-Devtools beim Aufruf von `https://www.geograsim.at/glossar` mit der konkreten Fehler-Meldung (z.B. `NET::ERR_CERT_COMMON_NAME_INVALID` oder `ERR_SSL_PROTOCOL_ERROR`) — daraus lässt sich genauer sehen, ob es ein Cert- oder Routing-Problem ist.
---
*Atlas-Memory:* die Datei liegt unter `.dontDeploy/notes/2026-06-11-domain-www-an-meister.md`,
nicht im Git getrackt (eigentlich schon gitignored, weil unter `.dontDeploy/`).
@@ -0,0 +1,42 @@
# Relevante Infos für Lehrpersonen — Zusammenfassung
Stand 2026-08-19. Konsolidiert, was wir zum Lehrer-Cockpit festgehalten und gebaut haben.
## Die Kernfrage der Lehrperson
> „Wer ist gerade **engagiert und macht es gut** — und wer **blödelt herum oder hängt fest**?"
Alles im Cockpit dient dieser Frage. Kein Zahlen-Dump, sondern wenige aussagekräftige Signale.
## Die 6 relevanten Signale
| Signal | Was es beantwortet | Woher |
|---|---|---|
| **Fortschritt** | Wie weit durch die Sim? (%) | `progressPct` / abgeleitet je Sim |
| **Qualität** | Blödeln vs. gut — Verhältnis **richtige ↔ falsche** Klicks (grafischer Balken) | `correctClicks`/`wrongClicks` (bisher Weltküche; andere ziehen nach) |
| **Fleiß** | Wie viel im **gewählten Zeitraum** geschafft? (z. B. Gerichte, gelieferte Aufträge) | assessments im Intervall |
| **Lage** | Klartext: **Stark / Solide / Viele Fehler** (kein „Kritisch"-Jargon) | `_liveMood` je Sim |
| **Aktivität** | aktiv / idle / **pausiert** — war das Kind die ganze Stunde dabei? | Heartbeat `__activeMs`/`__idleMs`/`__paused` |
| **Hilfe-Bedarf** | „🆘 braucht evtl. Hilfe" | `_liveNeedsHelp` je Sim (z. B. Budget < 0, viele Fehlversuche) |
## Die Ansichten (Menü „Ergebnisse")
- **📅 Tag** — Zeitstrahl aller Sessions des Tages je Kind, eingefärbt nach Erfolg. Balken tragen **Sim-Kürzel/Wort** (LOG, Logistik …), Klick öffnet Detail.
- **🔴 Aktuell** — Live-Tabelle je Sim mit **kuratierten** Spalten (LIVE_PRIMARY_FIELDS), Qualitäts-Balken, Fortschritt, Lage. Aktualisiert alle 4 s.
- **📊 Vergleich** — aktive Kinder EINER Sim nebeneinander (Fortschritt + Qualität) → direkter „wer gut / wer nicht".
- **📚 Gesamt** — Aggregat (Fleiß-Zeit, Modul-Karten, Matrix) über den gewählten Zeitraum.
## Design-Prinzipien (verbindlich)
1. **Kuratierte Spalten je Sim** — nie Roh-State dumpen, nie interne `__`-Heartbeat-Keys zeigen. Fehlt ein Schema → gefilterter Fallback (ohne Rausch-Felder).
2. **Klartext statt Jargon** — „Stark/Solide/Viele Fehler" statt „Kritisch/OK"; „Gerade dabei" statt „Phase".
3. **Ehrliche Zeit** — Aktivzeit zählt **nicht** bei Spiel-Pause und **nicht** bei Tab-weg/idle (>5 min). Sonst wirkt ein pausiertes Kind fälschlich „fleißig".
4. **Ein Wort + 3-Buchstaben-Kürzel je Sim** (SIM_LABELS) — enge Balken nutzen das Kürzel, breite das Wort.
5. **Datenschutz** — nur was die Lehrperson pädagogisch braucht; verdeckte Antworten (z. B. Kofferdetektiv-Ziel) bleiben verdeckt, solange das Kind sucht.
## Was jede Sim liefern sollte (GGS_LIVE_STATE)
- `progressPct` (0100), ein Fortschritts-Zähler (z. B. `completed`/`total`),
- **`correctClicks`/`wrongClicks`** (für die Qualitäts-Kennzahl — Ziel: alle Sims),
- optional `health` (good/ok/struggling) für die Lage,
- Heartbeat-Felder (`__activeMs` etc.) kommen automatisch aus live-client.js; bei Pause `window.__GGS_LIVE__.setPaused(true)` rufen.
## Offen (siehe project_backlog_2026-08-18 / project_lehrer_observability_overhaul)
- **Aktivitäts-/Qualitätsstreifen** pro Kind über die Stunde (grün→rot, aktiv→inaktiv & bullshit→ernsthaft) — braucht Zeitreihen-Aktivitätsdaten.
- Weltküche-**Gesamt-Kachel** (Gerichte · Ø Punkte · Fehlquote).
- **Andere Sims** mit `correctClicks`/`wrongClicks` nachrüsten (bisher nur Weltküche).
+6
View File
@@ -0,0 +1,6 @@
node_modules/
reports/
test-results/
playwright-report/
.playwright/
*.log
+145
View File
@@ -0,0 +1,145 @@
# GeoGraSim · Playwright-Test-Suite
**Zweck**: automatisierte Browser-Tests, die das halbe Hundertfach manuelles Durchklicken
ersetzen. Sucht tote Links, geänderte UI-Verhalten, Glossar-Tooltip-Bugs etc.
**Wichtig — Plattformregel**: dies ist eine **Lokal-Test-Suite**. Sie läuft gegen
`http://localhost/geograsim/App/...` (XAMPP). Sie deployt nichts und modifiziert keine
Prod-Daten. Falls jemals Prod-Tests gebraucht werden, muss das explizit konfiguriert sein.
---
## Drei Test-Stufen
| Stufe | Was wird getestet | Tool | Wer macht's | Wann läuft's |
|---|---|---|---|---|
| **1** | Statische Link-Checks · Glossar-Slugs · Sim-Wrapper · Nav-Routes | Playwright headless (Chromium) | Atlas baut + lässt laufen | täglich oder vor jedem Commit |
| **2** | End-to-End-Flows: Login · Sim durchspielen · Submit · Lehrer-Cockpit | Playwright headless + headed bei Debug | Atlas baut + lässt laufen | vor jedem geplanten Deploy |
| **3** | Semantische Review: passt didaktisch · UX · Sinnhaftigkeit | Codex (OpenAI) liest Stufe-1-/2-Reports | Atlas baut Briefing-Bundle · Codex schreibt Review · Thomas moderiert | nach größeren Änderungen oder ad hoc |
---
## Verzeichnisstruktur
```
.dontDeploy/playwright/
├── README.md ← dieses File
├── package.json ← npm-Dependencies
├── playwright.config.js ← globale Konfig (baseURL, retries, reporter)
├── .gitignore ← node_modules + Reports werden ignoriert
├── tests/
│ ├── stufe1-smoke/
│ │ ├── links-static.spec.js ← Nav-Links, Top-Menü, Footer
│ │ ├── glossar-all.spec.js ← jeder Glossar-Slug ist erreichbar
│ │ ├── sim-wrappers.spec.js ← jeder Sim-Wrapper antwortet 200 + rendert
│ │ └── api-routes.spec.js ← /api/*-Endpunkte antworten (auth-aware)
│ │
│ └── stufe2-flows/
│ ├── login-klassen-code.spec.js
│ ├── sim-klima-start-bis-submit.spec.js
│ ├── teacher-cockpit-tabs.spec.js
│ └── glossar-tooltip-popup.spec.js
├── fixtures/
│ ├── known-glossar-slugs.json ← aktuelle Liste aller Glossar-Slugs
│ ├── known-paths.json ← alle erwarteten Top-Level-Routes
│ └── test-accounts.json ← Lokal-Test-Schüler:innen + Lehrer
├── reports/ ← gitignored: HTML-Reports + Screenshots
│ └── (autogeneriert)
└── briefing/
├── CODEX-BRIEF.md ← Liebe-Kollege-Brief an Codex
├── codex-stufe-3-auftraege.md ← konkrete Review-Aufträge
└── _context-bundle/ ← Anlagen für Codex
├── geograsim-readme.md ← Auszug aus CLAUDE.md
├── projektkonventionen.md ← Sprach-Regeln, Bildstil, Lehrplan
├── glossar-pattern.md ← Tooltip-Pattern, Bug-Historie
└── sim-architektur.md ← Wrapper, GGS_LIVE_STATE, Live-Cockpit
```
---
## Einrichtung lokal
```bash
cd .dontDeploy/playwright
npm install
npx playwright install chromium # nur Chromium reicht für die meisten Tests
```
Hinweis: `npm install` zieht `node_modules/` ins Verzeichnis (~250 MB). Das ist
via `.gitignore` ausgeschlossen.
---
## Tests laufen lassen
```bash
# Stufe 1 (Smoke): ~30 Sekunden, headless
npm run test:smoke
# Stufe 2 (Flows): ~3 Minuten, headless
npm run test:flows
# Beides hintereinander
npm test
# Mit UI-Debugger (headed, langsam, sehr nützlich beim Debuggen)
npm run test:debug
# Nur ein Test-File
npx playwright test glossar-all.spec.js
# HTML-Report öffnen
npm run report
```
---
## Was Stufe 1 macht (Smoke)
- **Alle Glossar-Slugs anfahren** (aus DB-Dump in `fixtures/known-glossar-slugs.json`):
jeden `/glossar?term=<slug>` aufrufen, prüfen ob 200 + Begriff-Titel im DOM
- **Alle Top-Level-Routes** (`fixtures/known-paths.json`):
`/teacher`, `/schueler`, `/lehrplan`, `/glossar`, `/heli-game`, `/klima-2d`, ...
- **Alle Sim-Wrapper** öffnen, prüfen ob `window.__GGS__` gesetzt ist + game-DOM rendert
- **Glossar-Tooltip-Links auf zentralen Seiten** (Klima-Onboarding, Weltküche): clicken,
prüfen ob Popup öffnet + Link zum vollen Eintrag funktioniert (Regression für den
Bug vom 2026-06-10!)
## Was Stufe 2 macht (Flows)
- **Schüler-Login** mit Klassen-Code + Pseudonym + Passwort
- **Klima starten · 10 Klicks machen · submit**: prüft assessment-Submit + Heartbeat
- **Heli M1 · alle Wegpunkte klicken · Startfreigabe**: prüft die Startfreigabe-Logik
vom 2026-06-10 (Audio-Ende alleine reicht NICHT)
- **Weltküche Pizza · zwei richtige Länder · solved**: prüft Round-End-Anzeige
- **Lehrer-Cockpit · alle 5 Tabs durchklicken**: prüft alle Render-Funktionen
- **Glossar-Tooltip in Weltküche**: Begriff klicken → Popup → „Voller Eintrag" → 200
## Was Stufe 3 macht (Codex-Review)
Codex bekommt:
- HTML-Reports aus Stufe 1 + 2
- Screenshots der wichtigsten Stellen
- Briefing aus `briefing/CODEX-BRIEF.md`
- Kontext-Bundle aus `briefing/_context-bundle/`
Codex schreibt zurück:
- Markdown-Review mit Fundstellen, Schweregrad, Empfehlungen
- KEINE Code-Änderungen — nur Bewertung
---
## Wartung
- **Bei jedem Glossar-Update**: `fixtures/known-glossar-slugs.json` neu dumpen
(siehe `scripts/dump-glossar-slugs.sql`)
- **Bei jeder neuen Sim**: `fixtures/known-paths.json` ergänzen + neuen Flow-Test bauen
- **Bei jedem Bug-Fix**: ein Regressionstest dazu, damit der Bug nicht wiederkommt
---
Stand: 2026-06-11 · gepflegt von Atlas (Claude Opus 4.7) · Reviews durch Codex (GPT-5/4o)
+34
View File
@@ -0,0 +1,34 @@
const { chromium } = require('@playwright/test');
(async () => {
const b = await chromium.launch(); const p = await b.newPage();
const errs=[]; p.on('pageerror',e=>errs.push(e.message)); p.on('console',m=>{if(m.type()==='error')errs.push(m.text());});
await p.route('**/api/admin', r => r.fulfill({ contentType:'application/json', body: JSON.stringify({authenticated:true}) }));
await p.goto('http://localhost/geograsim/App/admin-weltkueche.html');
await p.waitForTimeout(2000);
const r = await p.evaluate(() => {
const dishes = [...document.querySelectorAll('.dish')];
const names = dishes.map(d => d.querySelector('.nm')?.textContent);
function dishInfo(namePart){
const d = dishes.find(x => (x.querySelector('.nm')?.textContent||'').toLowerCase().includes(namePart));
if(!d) return null;
const rows = [...d.querySelectorAll('tbody tr')].map(tr => ({
ing: tr.querySelector('.ing-nm')?.textContent.replace(/\s+/g,' ').trim(),
ok: [...tr.querySelectorAll('.cty.ok')].map(c=>c.textContent.replace(/\s+/g,' ').trim()),
dist: [...tr.querySelectorAll('.cty.dist')].map(c=>c.title.split(' · ')[0]),
}));
return rows;
}
const groestl = dishInfo('tiroler gröstl');
const kart = groestl && groestl.find(r => /Kartoffel/i.test(r.ing));
const eierRow = groestl && groestl.find(r => /Eier/i.test(r.ing));
return {
dishCount: dishes.length,
sampleNames: names.slice(0,6),
kartoffelOk: kart ? kart.ok : null,
eierOk: eierRow ? eierRow.ok : null,
warnCount: document.querySelectorAll('.warn').length,
};
});
console.log(JSON.stringify({result:r, pageErrors:errs}, null, 2));
await b.close();
})();
@@ -0,0 +1,133 @@
# Lieber Kollege Codex
Schön, dass du heute beim GeoGraSim-Projekt mitlest. Ich schreibe diesen Brief
einmal zentral — Atlas ist mein Name, ich bin der Claude (Opus 4.7), der dieses
Projekt seit Wochen mit Thomas zusammen entwickelt.
## Wer Thomas ist und worum es geht
Thomas Schroffenegger ist Lehrender im Bereich Geografie/Wirtschaftliche
Bildung. Er baut **GeoGraSim** — eine browser-basierte Lernplattform mit
zwölf interaktiven Simulationen für die Sekundarstufe I. Zielgruppe: DACH
+ Liechtenstein. Schüler:innen treffen Entscheidungen in komplexen Systemen
(Klimawandel, Hochwasser, Welternährung, …) und reflektieren die Folgen.
Lehrpersonen sehen Live-Daten der Klasse und können auswerten.
Die Plattform ist online: <https://geograsim.at>.
## Was deine Aufgabe ist
Wir haben gestern und heute eine **Playwright-Test-Suite** aufgebaut
(Stufe 1 + 2, siehe Verzeichnis `.dontDeploy/playwright/`). Die fängt
mechanische Bugs (404, Cert-Fehler, fehlende DOM-Elemente).
Was sie **nicht** kann: didaktische, sprachliche oder UX-Beurteilung.
Genau dafür kommst du ins Spiel. Du bekommst:
### ⚠ Wichtig: bitte ONLINE testen, nicht lokal
Test-Basis ist **`https://geograsim.at`** — also die **echte Produktions-Version**.
Den lokalen Code (XAMPP unter `localhost/geograsim/App`) hat Atlas auf der
Maschine. Atlas-Code-Stand und Prod-Stand können auseinander liegen
(Atlas hat seit 2026-06-10 nicht mehr automatisch deployt).
**Konkret bitte beide Hostnames anfahren:**
- `https://geograsim.at/...` (Hauptdomain)
- `https://www.geograsim.at/...` (mit www-Subdomain — derzeit bekannte SSL-Cert-Problematik, Meister ist informiert)
Wenn `www.geograsim.at` Cert-Fehler oder andere Auffälligkeiten zeigt:
in den Review aufnehmen unter „Schwer" mit Schweregrad-Kennzeichen.
Atlas hat die Diagnose schon, aber wir wollen aus deinem unabhängigen
Blick wissen, ob es weitere Auswirkungen gibt (z.B. Mixed-Content,
Subresource-Failures, etc.).
Wir verlassen uns auf den frischen Browser-Blick — Atlas und Thomas
kennen die Seite zu gut, um Deployment-Lücken noch zu sehen.
1. **HTML-Reports + Screenshots** aus Playwright-Läufen
2. **Diesen Brief** als allgemeine Einordnung
3. Ein **Kontext-Bundle** in `briefing/_context-bundle/` mit den
wichtigsten Projekt-Konventionen (Sprach-Regeln, Bildstil,
Lehrplan-Logik, Sim-Architektur)
4. Eine **konkrete Auftrags-Liste** in `briefing/codex-stufe-3-auftraege.md`
Du gibst zurück:
- Einen **strukturierten Markdown-Review**: pro Fundstelle Schweregrad,
Beobachtung, Empfehlung. Format siehe „Output-Format" unten.
- **KEINE Code-Änderungen.** Du bist hier als zweite Augenpaar-Instanz,
nicht als Entwickler. Atlas (ich) baut deine Empfehlungen dann ein.
## Warum eine zweite KI
Atlas und Codex haben unterschiedliche Trainingsdaten und damit
unterschiedliche Blindstellen. Wenn ich beim Entwickeln einen
didaktischen oder UX-Bug übersehe, übersehe ich ihn beim Selbst-Review
genauso. Eine andere KI sieht andere Dinge. Das ist ein bewährtes
Engineering-Pattern („Adversarial Review", „second pair of eyes").
## Was Atlas tut und nicht tut
- **Atlas baut und deployt** Code, schreibt PHP/JS/SQL, pflegt
Datenbanken, schreibt Memory-Snapshots.
- **Atlas hat eine persistente Memory** unter
`C:\Users\herr_\.claude\projects\c--xampp-htdocs-geograsim\memory\`
Konventionen, Bug-Historie, Architektur-Entscheidungen. Wenn du
Hintergründe brauchst, frag Thomas, ob er einen Memory-Auszug schickt.
- **Atlas deployt aktuell NICHT** auf Prod (Thomas-Regel ab 2026-06-10) —
alle Änderungen laufen lokal. Wenn du etwas Online testen willst,
weiß Thomas das ggf. nachzuholen.
## Wichtige Plattform-Konventionen (Kurz-Version)
- **Sprache**: „Lernarbeit" statt „Spielen", „Bearbeiter:in" statt
„Spieler:in", „Lehrperson" statt „Lehrkraft", „Simulation" statt
„Spiel". Hintergrund: Wygotski, Lerntätigkeit als Arbeit.
- **Lehrplan-Kontext**: AT „Geografie und wirtschaftliche Bildung"
(BGBl. II Nr. 1/2023), CH LP21 RZG/WAH/MI, DE KMK/NRW/Niedersachsen/Bayern.
GW als Kürzel OK, ausgeschrieben „Geografie und wirtschaftliche Bildung".
- **Schuljahr**: AT SepJul · CH AugJul · DE bundeslandabhängig · LI AugJul.
- **Bildstil**: Flat Scandinavian, Farben #1f4b37 / #4a7c4e / #e8c547 / #e8e4d8.
- **Kein KI im Produkt**: alle Auswertungen sind parametrisch, KI nur in
der Content-Erstellung (Bilder, Glossar, TTS).
Mehr Details in `briefing/_context-bundle/`.
## Output-Format
Bitte als Markdown, sortiert nach Schweregrad:
```markdown
# Review · <Modulname> · <Datum>
## Schwer (User-Blocker, dataloss-Risiko, Cert-Fehler)
- **Fundstelle:** <Datei oder URL>
**Beobachtung:** <Was du siehst>
**Empfehlung:** <Was zu tun ist>
## Mittel (UX-Bug, didaktisch fragwürdig, sprachlich falsch)
-
## Klein (Polish, Inkonsistenz, Tippfehler)
-
## Gut gemacht
- … (kurze Liste von Sachen die du explizit lobst — das ist nicht Höflichkeit,
sondern Information: bewahrt Atlas davor zu „verbessern" was schon gut ist)
```
Wenn du etwas nicht beurteilen kannst (z.B. ein didaktischer Kontext
fehlt), schreib **„braucht Klärung von Thomas"** statt zu raten. Das ist
hilfreicher als eine plausibel klingende falsche Bewertung.
## Wenn du Fragen hast
Antwort an Thomas. Atlas (ich) sehe diesen Brief nicht direkt — Thomas
ist der Übersetzer zwischen uns. Wenn etwas unklar ist, sag Thomas in
deinem Output: „Frage an Atlas: …". Er reicht's weiter.
Viel Spaß. Wir freuen uns über deine Lektüre.
— Atlas
(Claude Opus 4.7 · Anthropic · GeoGraSim-Atlas-Instanz)
Stand: 2026-06-11
@@ -0,0 +1,64 @@
# GeoGraSim · Quick-Overview für Codex
## Tech-Stack
- **Backend**: PHP 8.x + MariaDB (via XAMPP lokal / Docker auf Prod)
- **Frontend**: Vanilla JS + HTML + CSS (kein Build-Step, keine Framework-Abhängigkeit)
- **Karten**: Leaflet 1.9.4
- **3D** (Klima 3D, Sonnensystem): Three.js
- **Audio**: ElevenLabs für Sprach + SFX (Pipeline, nicht zur Laufzeit)
- **Tests**: Playwright (neu 2026-06-11)
Bewusst **kein React/Vue/TypeScript** — Begründung: Lehrpersonen sollen
ggf. selbst kleine Anpassungen machen können, ohne Build-Toolchain. Plus:
Hosting auf Standard-Apache, keine Node-Pipeline auf Prod.
## URL-Struktur (Auswahl)
| URL | Was |
|---|---|
| `/` | Landing |
| `/teacher` | Lehrer-Cockpit (SPA-ähnlich) |
| `/schueler` | Schüler:innen-Dashboard |
| `/login` | Login |
| `/lehrplan` | Lehrplan-Übersicht (261 Anker, filterbar nach Land + Modul) |
| `/glossar` | Glossar-Index |
| `/glossar?term=<slug>` | einzelner Begriff (Detail-Modal) |
| `/handbuch.html` | Lehrer-Handbuch (12 Sims dokumentiert) |
| `/modul-<sim>` | Modul-Info-Seite (eine pro Sim) |
| `/<simname>` | Sim selbst (z. B. `/klima-2d`, `/weltkueche`) |
| `/api/<endpoint>.php` | JSON-APIs |
## Wichtige Stand-Notizen
- **Live-Cockpit** (`/teacher` → Tab „Live" → „🎯 Cockpit") ist NEU
(2026-06-11): Status-Tiles, Top-3/Bottom-3, Klassen-Säulen, Schülerliste
- **Fleiß-Säulen-Matrix** (`/teacher` → Tab „Ergebnisse" → oberste Sektion):
pro Schüler:in eine Zeile, Säulen pro Zeitbucket, gestapelt nach Modul
- **Weltküche** ist seit 2026-06-08 die neuste Sim, mit NEU-Pill markiert
- **Fluggesellschaft** ist `status='geplant'` — taucht im normalen
Schüler-Flow nicht auf, aber Wrapper existiert
- **Entscheidungstag** existiert auf Disk, ist aber NICHT in `module_info`
registriert — wird nicht geheartbeated und nicht als Sim ausgespielt
## Was Codex testen kann
- Browser-Sicht jeder Sim (alle ohne Login als „Gast" zugänglich)
- Lehrer-Cockpit (Login nötig — frag Thomas)
- Glossar + Lehrplan + Handbuch + Mindmap
- Performance auf iPad-Größe simuliert (Playwright-Devtools)
## Was Codex NICHT testen kann
- Reale Klassen-Sessions mit echten Schüler:innen
- Lehrer-Dashboard mit „vielen aktiven Sessions" — wir haben heute
unter Test nicht so viele aktive User
- E-Mail-Versand (Ticketing-System ist nicht öffentlich)
## Was Codex bei Atlas anfragen kann (über Thomas)
- Memory-Auszüge zu spezifischen Themen
- Code-Auszüge wenn das Review-Material nicht ausreicht
- Klärung von Konventionen, die nicht im Briefing stehen
— Atlas
@@ -0,0 +1,62 @@
# GeoGraSim · Glossar-Pattern (Codex-Briefing)
## Was ist das Glossar?
Eine zentrale Begriffsdatenbank (Stand 2026-06-11: 266 Slugs).
Jeder Eintrag hat:
- **Standardsprache** (Sek-I-Niveau, 2-3 Sätze)
- **Leichte Sprache** (A1-A2, kurze Sätze, einfache Wörter)
- **Modul-Verknüpfungen**: in welchen Sims kommt der Begriff vor
- **Bild oder SVG** (wo passend)
- **Wikipedia-Link** zum Weiterlesen
## Tooltip-Pattern (überall in der Plattform)
In jeder Sim und auf jeder Inhalts-Seite werden Fachbegriffe als:
```html
<span class="ggs-g" data-glossar="meeresspiegel">Meeresspiegel</span>
```
oder
```html
<span data-glossar="meeresspiegel">Meeresspiegel</span>
```
markiert. Beim Klick öffnet sich ein **Tooltip-Popup** mit:
- Begriff-Titel
- Kurze Definition
- Link „**Voller Glossar-Eintrag →**" auf `/glossar?term=<slug>`
## Bug-Historie (wichtig für Review)
### 2026-06-10 · Tote Glossar-Links — JETZT GEFIXT
Zwei Bugs auf Prod sichtbar:
1. **Tooltip-Lib (`App/assets/js/glossar-tooltip.js`)**:
Der Regex `apiBase.replace(/\/api$/, '')` schnitt aus `/php/api`
nur das letzte `/api` ab → resultierender Link `/php/glossar?term=…` → 404.
**Fix**: erst `/php/api` strippen, dann `/api`.
2. **Klima 2D/3D (`App/sims/klima/game-2d.html` und `game-3d.html`)**:
`__GGS__.basePath` ist auf Prod ein **leerer String** `""`. Truthy-Check
`(__GGS__.basePath ? basePath : fallback)` fiel deshalb fälschlich auf
den Fallback `/geograsim/App` zurück. **Fix**: `typeof basePath === 'string'`
statt Truthy-Check.
Diese beiden Bugs werden im Playwright-Smoke-Test `glossar-tooltip-regression.spec.js`
abgefragt. Wenn die Tests grün sind, ist der Bug zumindest mechanisch nicht zurück.
## Was Codex prüfen kann (semantisch)
- Sind **Begriffe verständlich** auf Sek-I-Niveau erklärt?
- Funktioniert die **Leichte-Sprache-Variante** wirklich auf A1-A2-Niveau?
- Sind die **Modul-Verknüpfungen** sinnvoll? (Ein Begriff der nur in Heli
vorkommt, sollte auch nur dort verknüpft sein. Ein übergreifender Begriff
wie „CO₂" sollte in vielen Sims verknüpft sein.)
- Gibt es **Begriffs-Dopplungen** (gleicher Begriff in zwei Slugs)?
- Stimmt der **Bildstil** mit der Flat-Scandinavian-Linie überein?
## Was Codex NICHT prüfen kann (außerhalb deines Scope)
- Datenbank-Performance, Indizes
- SQL-Injection-Hardening (das macht Atlas)
- Cache-Strategie
@@ -0,0 +1,67 @@
# GeoGraSim · Projektkonventionen (Codex-Briefing)
## Sprachregeln (verbindlich)
| Statt | Verwende |
|---|---|
| spielen, Spiel, Spieler:in | arbeiten mit, Simulation, Bearbeiter:in |
| Lehrkraft | **Lehrperson** |
| Schüler/Schülerin | Schüler:innen, alternativ Schüler*innen |
**Hintergrund**: Bildungstheoretisch basiert das Projekt auf Wygotskis
Lerntätigkeit-Konzept. „Lernarbeit" ist explizite Wortwahl — Lernende sind
nicht Spieler:innen, sondern Bearbeiter:innen einer Aufgabe.
## Lehrplan-Anbindung
| Land | Bezeichnung | Quelle |
|---|---|---|
| 🇦🇹 AT | „Geografie und wirtschaftliche Bildung" | BGBl. II Nr. 1/2023 |
| 🇨🇭 CH | RZG / WAH / NMG / Medien und Informatik (MI) | LP21 (lehrplan.ch) |
| 🇩🇪 DE | Erdkunde/Geographie · Politik · Wirtschaft | KMK + NRW + Niedersachsen + Bayern + DGfG/VGDH |
| 🇱🇮 LI | wie CH | Lehrplan Liechtenstein |
**Kürzel**: GW = „Geografie und wirtschaftliche Bildung" (vorher
„Geographie und Wirtschaftskunde"). Im Volltext immer ausschreiben,
GW als Kürzel OK.
**Wichtig CH-Korrektur**: das Informatik-Fach in CH heißt **„Medien und
Informatik (MI)"** — NICHT „Informatische Bildung". Letzteres ist AT-Spezifikum.
## Bildstil
- **Flat Scandinavian** (skandinavische Flachgrafik)
- **Hauptfarben**: #1f4b37 (dunkelgrün) · #4a7c4e (mittelgrün) · #e8c547 (gold) · #e8e4d8 (creme)
- Generierung über DALL-E 3 / gpt-image-2 lokal (nicht im Produkt!)
## Keine KI im Produkt
Sehr wichtig für Codex-Bewertung: alle Auswertungen sind **parametrisch**.
- Klima-Score = Formel aus 4 Dimensionen, kein „LLM bewertet"
- Heli-Sterne = `planCorrect/planTotal × Faktor`
- Glossar-Erklärungen sind in DB redaktionell gepflegt, nicht LLM-generated
KI darf nur in der **Content-Erstellungs-Pipeline** ein
(Bildgenerierung, Glossar-Bilder, Text-to-Speech für Audio-Briefings,
Dispatcher-Stimme bei Heli).
Wenn dir auffällt, dass irgendwo eine LLM-Anbindung „live" zur Sim-Zeit
laufen sollte: das ist ein Showstopper, bitte als „Schwer" markieren.
## Zielgerät
iPad Landscape 1180 × 820 (Hauptzielgerät). Touch-Mindestziel 36px.
Hover-Effekte dürfen NIE essentiell sein. Keyboard funktioniert
natürlich auch, aber das ist nicht Hauptpfad.
## Live-Daten (Lehrer-Cockpit)
Jede aktive Sim exponiert `window.GGS_LIVE_STATE = function(){...}`
eine Plattform-Lib (`live-client.js`) ruft das alle 4 s ab und sendet
den State an `/api/live` als Heartbeat. Die Lehrperson sieht im Cockpit
unter `/teacher` Live-Werte aller aktiven Schüler:innen.
Stand der Integration:
- 11 von 12 Sims liefern voll
- Entscheidungstag ist noch nicht in der Plattform registriert
(kein `module_info`-Eintrag, kein Wrapper)
@@ -0,0 +1,116 @@
# GeoGraSim · Sim-Architektur (Codex-Briefing)
## Verzeichnis-Layout (vereinfacht)
```
App/
├── pages/ ← PHP-Wrapper für jede Sim und jede Top-Level-Route
│ ├── klima-2d.php ← injiziert __GGS__-Kontext, lädt sims/klima/game-2d.html
│ ├── weltkueche.php ← analog für Weltküche
│ ├── modul-klima.php ← „Info-Seite" für das Modul (öffnet aus /teacher)
│ └── _partials/modul_detail.php ← gemeinsamer Partial für alle Modul-Detail-Seiten
├── sims/ ← die eigentlichen Simulationen
│ ├── klima/
│ │ ├── game-2d.html ← 2D-Variante
│ │ ├── game-3d.html ← 3D-Variante
│ │ └── engine.js ← Engine, deterministisch, kein LLM
│ ├── weltkueche/
│ │ ├── game.html
│ │ ├── assets/data/ ← dishes.json, production-matrix.json, …
│ │ └── scripts/ ← Build-Tools, NICHT zur Laufzeit
│ └── …
├── php/
│ ├── api/ ← JSON-Endpunkte
│ │ ├── live.php ← Heartbeats + Lehrer-Cockpit
│ │ ├── results.php ← Auswertung + Benchmark
│ │ ├── glossar.php ← Begriffs-Lookup
│ │ └── …
│ ├── lib/ ← Plattform-Code
│ │ ├── Benchmark.php ← NEU (2026-06-11) — Score-Engine pro Modul
│ │ ├── Country.php ← Land + Schuljahr + Lehrplan-Logik
│ │ ├── EasyLang.php ← Leichte-Sprache-Helper
│ │ └── Session.php ← Cookie-basiert für Schüler:innen
│ └── config/ ← Constants, BASE_PATH, BASE_URL etc.
├── assets/
│ ├── js/
│ │ ├── live-client.js ← Heartbeat, alle 4 s
│ │ └── glossar-tooltip.js ← Plattform-Tooltip-Lib
│ └── img/ ← Glossar-Bilder, Avatare, Card-Images
├── teacher.html ← Lehrer-Cockpit (SPA-ähnlich)
├── schueler.html ← Schüler:innen-Dashboard
└── index.php ← Front-Controller, leitet auf pages/
```
## Wrapper-Pattern
Jede Sim wird über einen **PHP-Wrapper** geöffnet, nicht direkt als HTML.
Der Wrapper:
1. Setzt Session-Kontext (`__GGS__` mit sessionId, simId, baseUrl, basePath, apiUrl)
2. Lädt die `game.html` der Sim und gibt sie aus
3. Injiziert plattform-eigene Scripts: `live-client.js`, `glossar-tooltip.js`
Beispiel Klima:
```
GET /klima-2d → pages/klima-2d.php → liest sims/klima/game-2d.html
→ injiziert <script>window.__GGS__ = {...}</script>
→ injiziert live-client.js
→ Echo
```
## Live-Daten-Vertrag
Jede Sim kann optional `window.GGS_LIVE_STATE = function(){return {...};}`
exponieren. Erwartete Plattform-Felder (für die Lehrer-Cockpit-Anzeige):
- `simId: string` — z.B. `'klima'`, `'weltkueche'`, …
- `phase: string` — menschenlesbar, z.B. `'Jahr 12 von 75'`, `'Auftrag 3/8'`
- `progressPct: number` — 0-100, für die generische Fortschritts-Anzeige
Plus sim-spezifische Felder. Beispiele:
| Sim | Sim-spezifische Felder |
|---|---|
| Klima 2D | `temp`, `co2`, `budget`, `floodedPct`, `population`, `seaLevelCm` |
| Heli | `planIndex`, `planTotal`, `planCorrect`, `planAttempts`, `missionId` |
| Weltküche | `ingSolved`, `ingTotal`, `mistakesInDish`, `score`, `dishName` |
## Benchmark-System (neu 2026-06-11)
`App/php/lib/Benchmark.php` enthält pro Sim eine Score-Formel
(`scoreSubmission`) und eine Aggregation pro Schüler:in
(`studentOverall`). Wird aus `live.php` (Cockpit) aufgerufen.
Skala 0100, max. 3 beste Sessions pro Modul, dann Mittelwert über
alle gespielten Module. **Ist keine Note** — wird als „Benchmark"
bezeichnet, Schüler:innen sehen den Wert nicht direkt.
## Database
MariaDB. Wichtige Tabellen:
- `students` — class-Membership, display_name + username (display_name kann NULL sein!)
- `classes` — join_code, teacher_id
- `student_sessions` — UUID-Cookie-basiert
- `live_sessions` — aktive Heartbeats, gelöscht nach Timeout
- `assessments` — Submissions am Sim-Ende, mit results-JSON + process_log
- `glossar`, `glossar_modules` — Begriffe + Modul-Verknüpfung
- `lehrplan_anchors`, `kompetenzen`, `kompetenz_modules` — Lehrplan-System V1
(V2-Tabellen sind LEER, bitte ignorieren wenn dir die unterkommen)
- `module_info` — Modul-Stammdaten (Titel, Icon, Status, Sort-Order)
## Was du als Codex NICHT siehst, aber wissen solltest
- Es gibt eine **Meister-Atlas-Instanz** für Server-Konfig — die handelt
alle Apache/SSL/Cert-Themen. Wenn du etwas am Server findest, ist das
Meister-Sache, nicht Atlas-Sache.
- **Memory persistent**: Atlas hat Notizen unter
`C:\Users\herr_\.claude\projects\c--xampp-htdocs-geograsim\memory\`
die historische Entscheidungen tragen. Wenn dir was komisch vorkommt,
ist möglicherweise im Memory der Grund dokumentiert.
- **„kein Deploy" aktuell**: Stand 2026-06-10 sagt Thomas, dass alle
Änderungen lokal bleiben (kein scp auf Prod). Wenn du also etwas
online testest, das du im Repo nicht findest, ist das Prod-Stand, nicht
aktueller Code.
@@ -0,0 +1,47 @@
# Codex-Outputs · Drop-Off-Verzeichnis
Hier kommen die Markdown-Reviews von Codex hin. Wenn du (Codex) Disk-Zugriff
auf das Repo hast, leg deine Review-Dateien direkt hier ab — Atlas liest sie
beim nächsten Start automatisch.
## Datei-Naming
```
codex-review-<auftrag>-<modul-oder-thema>-<YYYY-MM-DD>.md
```
Beispiele:
```
codex-review-A1-klima-2d-2026-06-11.md
codex-review-B-cockpit-2026-06-11.md
codex-review-C-glossar-2026-06-12.md
```
Bei mehreren Reviews am gleichen Tag mit Suffix:
```
codex-review-A2-klima-3d-2026-06-11-a.md
codex-review-A2-klima-3d-2026-06-11-b.md
```
## Format
Wie im `CODEX-BRIEF.md` beschrieben:
- Schweregrade: **Schwer · Mittel · Klein · Gut gemacht**
- Pro Fundstelle: Beobachtung + Empfehlung
- Bei Unklarheit: „braucht Klärung von Thomas"
## Zusammenfassung am Schluss
Wenn du mehrere Reviews gemacht hast, ist eine Datei
`codex-zusammenfassung-<YYYY-MM-DD>.md` mit den 35 wichtigsten Befunden
besonders hilfreich. Atlas liest die zuerst und priorisiert dann.
## Was Atlas mit deinen Reviews macht
- Liest alle hier vorhandenen Reviews chronologisch
- Übersetzt Befunde in TODO-Items
- Setzt um, wo Code-Änderung möglich
- Markiert „braucht Thomas-Entscheidung" wo strategisch
Vielen Dank für deine Mitarbeit!
— Atlas
@@ -0,0 +1,52 @@
# Review · Weltküche · 2026-06-11
## Schwer (User-Blocker, dataloss-Risiko, Cert-Fehler)
- **Fundstelle:** https://geograsim.at/weltkueche
**Beobachtung:** Im gelesenen Produktions-HTML kein didaktischer oder sprachlicher Schwer-Blocker erkennbar. Mechanische Status-, Glossar- und `__GGS__`-Checks sind laut Thomas/Atlas bereits durch Stufe 1+2 abgedeckt und wurden hier nicht erneut bewertet.
**Empfehlung:** Kein Schwer-Fund aus diesem Stufe-3-Review.
## Mittel (UX-Bug, didaktisch fragwürdig, sprachlich falsch)
- **Fundstelle:** Tutorial-Schritt "Begriffe nachschlagen"
**Beobachtung:** Der sichtbare Satz "Du stößt im Spiel auf Fachbegriffe?" verletzt die Projektkonvention "Simulation/Lernarbeit" statt "Spiel". Gerade im Tutorial setzt dieser Satz den Frame für die ganze Aktivität.
**Empfehlung:** In "Du stößt in der Simulation auf Fachbegriffe?" oder "Bei der Forschungsarbeit triffst du auf Fachbegriffe?" ändern.
- **Fundstelle:** Feedback bei falscher, aber grundsätzlich plausibler Auswahl
**Beobachtung:** Die Formulierung "du hast eines der Spiel-Slot-Länder noch nicht entdeckt" wirkt wie interner Entwicklungsjargon. Für 12-jährige ist "Spiel-Slot" schwer verständlich; zugleich bricht es die Lernarbeits-Konvention.
**Empfehlung:** Lernendennah formulieren, z. B. "Du hast eines der gesuchten Herkunftsländer noch nicht entdeckt."
- **Fundstelle:** Markt-Feedback zu `eu`, `global`, `world-far`, `off-season`, `local`
**Beobachtung:** Die Rückmeldungen selbst sind stark, aber die Marktlogik wird vor der ersten Aufgabe nicht explizit als Legende eingeführt. Begriffe wie "Hauptlieferant für uns", "Welt-Hauptproduzent", "Off-Season-Lieferant" und "fast alles bleibt im eigenen Land" erscheinen erst im Ergebnis. Das kann dazu führen, dass Lernende anfangs glauben, es gehe nur um "größter Produzent weltweit", obwohl eigentlich "kommt zu uns auf den Markt" gefragt ist.
**Empfehlung:** Vor oder bei der ersten Kartenaufgabe eine kurze Legende einführen: "Gesucht ist nicht immer der größte Produzent der Welt, sondern woher die Zutaten kommen, die wir bei uns kaufen." Die vier Kategorien knapp benennen: Hauptlieferant, Welt-Hauptproduzent, Winter-/Off-Season-Lieferant, Eigenkonsum/regionaler Markt.
- **Fundstelle:** Aufgabenformulierung "in welchem Land wird das vor allem angebaut?"
**Beobachtung:** Diese Frage ist für einfache Zutaten verständlich, passt aber nicht sauber zur eigentlichen Marktlogik. Bei Zutaten mit EU-Handel, Eigenkonsum oder Off-Season kann "vor allem angebaut" mit "größter Produzent" verwechselt werden.
**Empfehlung:** Näher an der Lernlogik formulieren, z. B. "Aus welchem Land kommt diese Zutat häufig zu uns auf den Markt?" Bei reinen Weltmarkt-Fragen kann "Hauptproduzent" zusätzlich genannt werden.
- **Fundstelle:** Lehrplan-/Infoseite nach Abschluss, Label "Geographie & Wirtschaft"
**Beobachtung:** Das Label weicht von der Projektkonvention "Geografie und wirtschaftliche Bildung" ab. Zusätzlich ist "Geographie" die ältere/abweichende Schreibweise gegenüber der im Kontext-Bundle festgelegten Form.
**Empfehlung:** Label an die Plattformkonvention angleichen: "Geografie und wirtschaftliche Bildung" oder kurz "GW", wenn der Platz knapp ist.
- **Fundstelle:** Tutorial-Schritt "So funktioniert die Forschung"
**Beobachtung:** Der Ablauf ist fachlich sinnvoll, aber für 12-jährige relativ dicht: Rezept, Nährwerte, Welt-Zutaten, Weltmarktführer, Welternte, Preis, Lehrplan-Bezug, Trivia und Klimabilanz werden in einem Schritt gebündelt. Das ist verständlich für Lehrpersonen, aber als Erstkontakt etwas kognitiv schwer.
**Empfehlung:** Den Tutorialtext stärker trennen: erst "Was mache ich jetzt?", dann "Was lerne ich dabei?". "Lehrplan-Bezug" und "Trivia" eher in der Abschlussansicht erklären oder leiser formulieren.
## Klein (Polish, Inkonsistenz, Tippfehler)
- **Fundstelle:** Start-/Tutorialsprache
**Beobachtung:** "weite Reise" für jede Mahlzeit ist eingängig, aber bei regionalen Gerichten wie Tiroler Gröstl oder Apfelstrudel etwas pauschal. Die Sim korrigiert das später über CO2- und Regionalitätslogik, im Einstieg klingt es zunächst absolut.
**Empfehlung:** Weicher formulieren, z. B. "Viele Mahlzeiten..." oder "Manche Zutaten haben eine weite Reise hinter sich."
- **Fundstelle:** Punkte & Abzeichen
**Beobachtung:** Punkte, Abzeichen und Namen wie "Sterne-Koch" sind motivierend, rücken aber nahe an klassisches Spiel-Framing. Das ist kein Problem, solange die Plattform bewusst mit Belohnungen arbeitet, kann aber mit der Sprachkonvention kollidieren.
**Empfehlung:** "Punkte & Abzeichen" beibehalten, aber "richtig erratene" durch "richtig zugeordnete" ersetzen. Das klingt weniger nach Ratespiel und stärker nach fachlicher Zuordnung.
- **Fundstelle:** Glossar-Link "Alle Weltküche-Begriffe öffnen"
**Beobachtung:** Der Glossar-Block im Tutorial ist didaktisch gut, aber sehr lang. Für Lernende kann die Begriffswolke vor der ersten Aufgabe etwas erschlagend wirken.
**Empfehlung:** Im Tutorial nur 4-6 Kernbegriffe zeigen und den vollständigen Glossar-Link danach anbieten.
## Gut gemacht
- Das Grundframing "Forschungs-Werkstatt", "Forscher:in", "Gericht erforscht" passt sehr gut zur Lernarbeits-Konvention und wirkt deutlich professioneller als ein reines Quiz.
- Die Feedbacklogik ist didaktisch stark: falsche Antworten werden nicht nur als falsch markiert, sondern erklären Klima, Markt, Eigenkonsum, Exportwege oder Saisonbezug.
- Die Unterscheidung zwischen "Welt-Hauptproduzent" und "kommt selten in den AT-Handel" ist fachlich wertvoll, weil sie eine typische Schüler:innen-Fehlannahme sichtbar macht.
- Der Hinweis "Die falschen Länder sind kein Zufall" ist sehr gelungen: Er macht Distraktoren zu Lernmaterial statt zu bloßen Fallen.
- Glossar-Anbindung im Tutorial ist sinnvoll platziert und nutzt relevante Fachbegriffe wie Welthandel, Klimazone, Monokultur, Fair Trade, Saisonal & Regional und CO2-Fußabdruck.
- Die Abschlusslogik mit Rezept, Nährwerten, Klimabilanz und "Wenn dich das interessiert" wirkt auf Lehrpersonen hochwertig und unterrichtsnah.
@@ -0,0 +1,113 @@
# Codex · Stufe-3-Aufträge
Konkrete Review-Aufgaben, die Atlas und Thomas dir auf den Tisch legen.
Du wählst je nach Materiallage, Thomas priorisiert.
## ⚠ Test-Basis
**Alle URLs sind Produktions-URLs.** Bitte testen gegen
`https://geograsim.at/...` (nicht localhost).
Plus: stichprobenartig auch `https://www.geograsim.at/...` anfahren —
dort gibt es eine bekannte Cert-Problematik, und wir wollen wissen,
ob sie nur das Cert-Warning betrifft oder auch Inhalte/Verhalten.
## A · Sim-Reviews (didaktisch + UX)
Für jede Sim ein Mini-Review nach den Konventionen aus `CODEX-BRIEF.md`:
| Sim | URL | Schwerpunkt der Bewertung |
|---|---|---|
| 🌍 Klimawächter 2D | https://geograsim.at/klima-2d | Onboarding-Verständlichkeit, Tutorial-Logik, Glossar-Anbindung, Fortschrittsbalken |
| 🌍 Klimawächter 3D | https://geograsim.at/klima-3d | gleiche Logik wie 2D, plus 3D-Steuerung intuitiv? |
| 🌊 Flussmanagement | https://geograsim.at/fluss | Hochwasser-Mechanik plausibel? Werkzeuge klar beschriftet? |
| 🚁 Heli-Navigation | https://geograsim.at/heli-game | Audio-Briefing verständlich? Wegpunkt-Klicks intuitiv? |
| 📦 Logistik Europa | https://geograsim.at/logistik | Wirtschaftslogik nachvollziehbar? UI für junge Lernende? |
| 🚌 Busfahrt | https://geograsim.at/busfahrt | Hauptstadt-Klick mit km-Abweichung sinnvoll? Quiz-Design? |
| 🌾 Landwirtschaft | https://geograsim.at/farmer | Anbau-Logik klar? Klimazonen-Feedback hilfreich? |
| ⚡ Energiemanager | https://geograsim.at/energiemanager | Tagesblöcke verständlich? Pumpspeicher-Mechanik intuitiv? |
| 🏛️ EU-Werkstatt | https://geograsim.at/eu-werkstatt | Karten-Sortier-Mechanik klar? Lehrplanbezug Politische Bildung |
| 🪐 Sonnensystem | https://geograsim.at/sonnensystem | 3D-Modell intuitiv steuerbar? Aufgaben-Texte verständlich? |
| 🍽️ Weltküche | https://geograsim.at/weltkueche | Markt-Feedback (EU vs. world-far vs. off-season) klar? |
Hinweis: Die Sims öffnen ohne Login als „Gast". Du kannst dich durch
das Onboarding klicken und einige Aktionen probieren, ohne dass etwas
gespeichert wird.
**Mini-Check pro Sim:** öffne die URL einmal mit `geograsim.at` und
einmal mit `www.geograsim.at`. Wenn sich das Verhalten unterscheidet
(Cert-Fehler, andere Inhalte, Mixed-Content-Warnings, gebrochene
Subresources), bitte unter „Schwer" reporten.
## B · Lehrer-Cockpit-Review
URL: `/teacher` (Login nötig → frag Thomas nach Test-Account)
Spezifisch zu reviewen:
- **Cockpit-Tab** (neuer Default): 4 Status-Tiles, Top 3 / Schwächste 3,
Klassen-Fleiß-Säulen, Klassenliste. Macht das in einer Klasse mit
25 Schüler:innen Sinn?
- **Fleiß-Säulen-Matrix** (Ergebnisse-Tab oben): Säulen-Höhe = Lernzeit,
Stack-Farbe = Modul. Lesbar für eine Lehrperson, die das zum ersten Mal sieht?
- **Benchmark-Prozent**: Wert wird als „Benchmark gesamt" angezeigt.
Ist die Bezeichnung treffend? Verstehen Lehrpersonen was der Wert misst?
## C · Glossar-Review
URL: `/glossar`
- Sind die Begriffe **didaktisch sauber erklärt** auf Sek-I-Niveau?
- Funktioniert die **Leichte-Sprache-Variante** (A1-A2) bei jedem Eintrag?
- Sind die **Verknüpfungen zu Modulen** richtig (eine Tooltip aus Klima
öffnet einen Begriff, der auch in Weltküche relevant ist)?
- **Bug-Regression**: tote Links waren am 2026-06-10 ein Problem
(Tooltip-Lib + Klima basePath). Wir haben gefixt — bitte stichprobenartig
nachprüfen, dass aus jeder Sim heraus der „Voller Glossar-Eintrag →"-Link
ein echtes Ziel hat.
## D · Lehrplan-Review
URL: `/lehrplan`
- 261 Anker insgesamt (AT 134 · CH 56 · DE 64 · LI 7), Stand Juni 2026
- Sind die **Quellen-Referenzen** plausibel und auffindbar?
- Sind die **Kompetenz-Verknüpfungen** auf die richtigen Sims gelegt?
(z. B. Welternährung sollte auf Weltküche zeigen, nicht nur auf Farmer)
- Fehlen offensichtlich wichtige Lehrplan-Bezüge?
## E · Plattform-Texte
Konkrete Stellen die Atlas nicht selbst beurteilen kann:
- Landing `/` — wirkt sie professionell und einladend für Lehrpersonen?
- Handbuch `/handbuch.html` — 12 Sims mit Lernzielen und Vor-/Nach-Fragen.
Sind die Lernziele konkret genug?
- Modul-Info-Seiten `/modul-<simname>` — eine pro Sim. Konsistent gestylt?
## Was Atlas NICHT von dir braucht
- **Keine Code-Vorschläge** — die mache ich, basierend auf deinem Review.
- **Keine Spekulation über Architektur** ohne dass du den relevanten
Code gesehen hast. Wenn du Code brauchst, sag Thomas, er gibt dir den
Auszug.
- **Keine generischen „Web-App-Tipps"** wie „Du solltest TypeScript
nutzen" — wir wissen warum wir vanilla JS + PHP nutzen (Hosting auf
Standard-Apache, kein Build-Step bei Schulen, Lehrpersonen können
ggf. selbst was anpassen).
## Format pro Review-Auftrag
Schreib pro Auftrag eine Markdown-Datei, am besten so:
```
codex-review-A1-klima-2d-2026-06-XX.md
codex-review-B-cockpit-2026-06-XX.md
codex-review-C-glossar-2026-06-XX.md
```
Thomas legt die in `.dontDeploy/playwright/briefing/codex-outputs/` ab
und Atlas liest sie dort. Wenn du mehrere Reviews machst, gerne einen
**zusammenfassenden Top-Befunde-Brief** an Atlas obendrauf.
Vielen Dank!
— Atlas (über Thomas)
+15
View File
@@ -0,0 +1,15 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('http://localhost/geograsim/App/sims/energiemanager/game.html');
await page.waitForTimeout(2500);
const info = await page.evaluate(() => ({
onb: typeof window.startOnboarding,
tutorial: typeof window.GGS_TUTORIAL,
title: window.GGS_TUTORIAL && window.GGS_TUTORIAL.title,
simId: window.GGS_TUTORIAL && window.GGS_TUTORIAL.simId,
}));
console.log('em direct:', JSON.stringify(info));
await browser.close();
})();
+21
View File
@@ -0,0 +1,21 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const errors = [];
page.on('pageerror', e => errors.push('PAGE:' + e.message.substring(0,150)));
page.on('console', m => { if (m.type()==='error') errors.push('CON:'+m.text().substring(0,150)); });
const res = await page.goto('https://geograsim.at/entscheidungstag');
console.log('status:', res?.status());
await page.waitForTimeout(3000);
const info = await page.evaluate(() => ({
lib: typeof window.GgsQuickIntro,
conv: typeof window.GGS_TUTORIAL,
live: typeof window.__GGS_LIVE__,
body: document.querySelector('body > *:first-child')?.tagName,
title: document.title,
}));
console.log(JSON.stringify(info, null, 2));
if (errors.length) console.log('errors:', errors.slice(0,8));
await browser.close();
})();
+15
View File
@@ -0,0 +1,15 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://geograsim.at/sims/entscheidungstag/game.html');
await page.waitForTimeout(2500);
const info = await page.evaluate(() => ({
lib: typeof window.GgsQuickIntro,
conv: typeof window.GGS_TUTORIAL,
simId: window.GGS_TUTORIAL && window.GGS_TUTORIAL.simId,
title: window.GGS_TUTORIAL && window.GGS_TUTORIAL.title,
}));
console.log('et direct:', JSON.stringify(info));
await browser.close();
})();
+19
View File
@@ -0,0 +1,19 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const r = await page.request.get('https://geograsim.at/teacher.html');
const html = await r.text();
console.log('STATUS', r.status(), 'LEN', html.length);
console.log('has renderLiveGesamt:', html.includes('renderLiveGesamt'));
console.log('has _liveQualityBar:', html.includes('_liveQualityBar'));
console.log('has tab-bar:', html.includes('tab-bar'));
console.log('has Ergebnisse:', html.includes('Ergebnisse'));
console.log('has external script src (module?):', /<script[^>]+src=/i.test(html));
// show script tags
const scripts = [...html.matchAll(/<script[^>]*>/gi)].map(m=>m[0]);
console.log('SCRIPT TAGS:', JSON.stringify(scripts, null, 2));
// show first 600 chars to see redirect logic
console.log('HEAD:', html.slice(0, 1200));
await browser.close();
})();
+22
View File
@@ -0,0 +1,22 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const r = await page.request.get('https://geograsim.at/teacher.html');
const html = await r.text();
const idxLogin = html.indexOf('login.html');
console.log('--- context around first login.html reference ---');
console.log(html.slice(idxLogin - 400, idxLogin + 120));
for (const name of ['renderLiveGesamt', '_liveQualityBar']) {
const idx = html.indexOf('function ' + name);
console.log('\n--- "function ' + name + '" @' + idx + ' ---');
if (idx >= 0) console.log(html.slice(idx - 60, idx + 120));
}
const idxErg = html.indexOf('Ergebnisse');
console.log('\n--- context around Ergebnisse ---');
console.log(html.slice(idxErg - 220, idxErg + 80));
await browser.close();
})();
+29
View File
@@ -0,0 +1,29 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const r = await page.request.get('https://geograsim.at/teacher.html');
const html = await r.text();
// 1) Static tab-bar markup
const tb = html.indexOf('id="tab-bar"');
console.log('--- #tab-bar markup ---');
console.log(tb >= 0 ? html.slice(tb - 40, tb + 700) : 'tab-bar id NOT found');
// 2) Extract _liveQualityBar function body and run it in isolation
const start = html.indexOf('function _liveQualityBar');
// find matching braces
let i = html.indexOf('{', start), depth = 0, end = -1;
for (; i < html.length; i++) {
if (html[i] === '{') depth++;
else if (html[i] === '}') { depth--; if (depth === 0) { end = i + 1; break; } }
}
const fnSrc = html.slice(start, end);
const fn = new Function(fnSrc + '\nreturn _liveQualityBar;')();
const sample = fn(8, 2);
console.log('\n--- _liveQualityBar(8,2) OUTPUT ---');
console.log(sample);
console.log('\ncontains "80":', String(sample).includes('80'));
await browser.close();
})();
@@ -0,0 +1,272 @@
{
"generated": "2026-06-11",
"total": 266,
"slugs": [
"achsneigung",
"aequator",
"albedo",
"alpenpass",
"aquakultur",
"asfinag",
"astronomische-einheit",
"auftrag",
"bahnterminal",
"barten",
"bartenwale",
"beduerfnisse",
"befrderungsvertrag",
"bestaeubung",
"bevoelkerung",
"bims",
"biodiversitaet",
"blackout",
"blas",
"blasloch",
"bodenmuedigkeit",
"breitengrad",
"bremsfading",
"brennerkorridor",
"bubble-net",
"buckelwal",
"budget",
"bycatch",
"caldera",
"cites",
"co2",
"co2_filter",
"co2-aequivalent",
"co2-flugverkehr",
"co2-fussabdruck",
"co2-fussabdruck-lebensmittel",
"cockpit-air-traffic",
"container",
"daemmung",
"datumsgrenze",
"disposition",
"drafting",
"dunkelflaute",
"echolokation",
"eignungsklasse",
"einsatzkoordinate",
"einzugsgebiet",
"ekliptik",
"emissionen",
"energiemix",
"erdgas",
"erneuerbar",
"erneuerbare-energie",
"etf",
"eu-aussengrenze",
"eu-gesetz",
"eu-kommission",
"eu-parlament",
"eu-petition",
"eu-zustaendigkeit",
"europaeische-buergerinitiative",
"europaeischer-rat",
"fahrzeug",
"fair-trade",
"fernwaerme",
"flaechenwidmung",
"fluke",
"flussbegradigung",
"fossile-brennstoffe",
"fotosynthese",
"freiwillige-vereinbarung",
"frist",
"geisternetz",
"gewitter-flug",
"go-box",
"gps",
"gradnetz",
"graue-energie",
"grokreis",
"grundlast",
"hafen",
"hartweizen",
"hauptstadt",
"hawaiianisch",
"heizoel",
"heli-kennzeichen",
"hochrechnung",
"hochwasserschutz",
"hoehenlinie",
"hoehenstufe",
"hotspot",
"hub-flughafen",
"imo",
"importpreis-abschlag",
"initiativrecht",
"intermodal",
"iwc",
"jetstream",
"kakao-bohne",
"kanaltunnel",
"kilowatt",
"kilowattstunde",
"kinetose",
"kipppunkt",
"klimafolgen",
"klimaneutralitaet",
"klimawandel",
"klimazone",
"knochenleitung",
"knoten",
"koeppen-geiger",
"kohlekraftwerk",
"kohlenstoffkreislauf",
"kompass",
"konsum",
"kontinent",
"koordinate",
"krankheits-ausbruch",
"krater",
"krill",
"kuestenschutz",
"laengengrad",
"lahar",
"lapilli",
"lastabwurf",
"lastausgleich",
"lastprofil",
"lava",
"leerfahrt",
"lenkzeit",
"lobbyismus",
"logistik",
"logistikkette",
"luftlinie",
"luftverkehr",
"maar",
"maeander",
"magma",
"magmakammer",
"mangrove",
"marge",
"marine-sanctuary",
"massstab",
"maut",
"meeresspiegel",
"megawatt",
"methan",
"migration",
"mischfonds",
"mitentscheidung",
"mobilitaet",
"mofette",
"mondfinsternis",
"mondphase",
"monokultur",
"monsun",
"mont-blanc-tunnel",
"motorbremse",
"myoglobin",
"nachhaltigkeit",
"nahversorgung",
"netzfrequenz",
"norroena-faehre",
"nutzungskonflikt",
"obsidian",
"oekosystem",
"oepnv",
"oeresundbruecke",
"opportunitaetskosten",
"panne",
"pariser-abkommen",
"pauschalreise",
"pektoralflosse",
"pellets",
"pendler",
"permafrost",
"photon",
"photovoltaik",
"phreatomagmatisch",
"plantage",
"plinianisch",
"polarkreis",
"polarnacht",
"polartag",
"ppm",
"pumpspeicher",
"pyroklastischer-strom",
"rat-der-eu",
"raumtemperatur",
"reefer-container",
"regelreserve",
"renaturierung",
"residuallast",
"retention",
"routing",
"saisonal-regional",
"schengen",
"schildvulkan",
"schlot",
"schneekettenpflicht",
"ship-strike",
"siedlungsdichte",
"solaranlage",
"sonnenfinsternis",
"sonnenwende",
"sparen",
"spedition",
"spitzenlast",
"standkosten",
"steuern",
"stratovulkan",
"strom",
"strombolisch",
"stromnetz",
"stromspeicher",
"subduktionszone",
"subtropisch-cfa",
"suffizienz",
"tachograph",
"tag-und-nacht-gleiche",
"tarif",
"temperatur",
"tephra",
"teu",
"topografie",
"topografische-karte",
"tourismus",
"treibhauseffekt",
"treibhausgas",
"tss",
"tubercles",
"turbine",
"ueberflutung",
"ueberschwemmungsgebiet",
"ueberweidung",
"ufervegetation",
"umschlag",
"umweg",
"urbanisierung",
"vei",
"verderblich",
"verdichtung",
"verkaufspreis",
"vignette",
"viskositaet",
"vogelschlag",
"vulkanasche",
"waermepumpe",
"wahrzeichen",
"walgesang",
"warndreieck",
"wartung",
"wasserkraft",
"watt",
"welt-hafen",
"welternte",
"welthandel",
"wendekreis",
"windenergie",
"windpark",
"wirkungsgrad",
"zeitzone",
"zenit",
"zersiedelung",
"zinseszins",
"zoll"
]
}
@@ -0,0 +1,31 @@
{
"generated": "2026-06-11",
"description": "Top-Level-Routes die mindestens 200 antworten müssen. Sortiert nach Funktionsgruppe.",
"public": {
"landing": ["", "index.html"],
"info": ["handbuch.html", "kontakt", "datenschutz", "impressum", "barrierefreiheit"],
"glossar": ["glossar"],
"lehrplan": ["lehrplan"],
"mindmap": ["mindmap"],
"forschung": ["forschung/", "forschung/literatur.php"]
},
"schueler_lehrer_einstieg": {
"login_login": ["login", "schueler", "teacher", "profil"]
},
"module_info": {
"active": [
"modul-klima", "modul-klima-3d", "modul-fluss", "modul-heli",
"modul-logistik", "modul-busfahrt", "modul-farmer", "modul-energiemanager",
"modul-eu-werkstatt", "modul-sonnensystem", "modul-weltkueche"
],
"planned_should_404": ["modul-fluggesellschaft", "modul-entscheidungstag"]
},
"sims_wrapper": {
"active": [
"klima-2d", "klima-3d", "fluss", "heli-game", "logistik",
"busfahrt", "farmer", "energiemanager", "eu-werkstatt",
"sonnensystem", "weltkueche", "kofferdetektiv-3d"
],
"planned": ["fluggesellschaft", "kofferdetektiv-2d", "wal"]
}
}
@@ -0,0 +1,15 @@
{
"comment": "Lokal-Test-Accounts. Auf Prod NICHT verwenden — andere IDs.",
"generated": "2026-06-11",
"klassenCode": "VRGETF",
"teacher": {
"email": "test@localhost",
"password": "test123",
"note": "Diesen Account musst du lokal in der DB anlegen, bevor die Login-Flows laufen können. Siehe README."
},
"schueler": {
"displayName": "schueler01",
"password": "test",
"klassenCode": "VRGETF"
}
}
+22
View File
@@ -0,0 +1,22 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
page.on('console', msg => console.log(' [console]', msg.type(), msg.text().substring(0,150)));
page.on('pageerror', err => console.log(' [PAGEERROR]', err.message));
const res = await page.goto('http://localhost/geograsim/App/fluss?level=1');
console.log(' status:', res.status());
await page.waitForTimeout(2000);
let snap;
try {
snap = await page.evaluate(() => {
try {
const fn = window.GGS_LIVE_STATE;
if (typeof fn !== 'function') return { _type: typeof fn };
return fn();
} catch (e) { return { _error: String(e) }; }
});
} catch (e) { console.log(' EVAL ERROR:', e.message); }
console.log(' snapshot:', JSON.stringify(snap, null, 2));
await browser.close();
})();
+15
View File
@@ -0,0 +1,15 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
// Mit baseURL ohne trailing slash:
const ctx1 = await browser.newContext({ baseURL: 'http://localhost/geograsim/App' });
const p1 = await ctx1.newPage();
const r1 = await p1.goto('/fluss?level=1');
console.log('no-trail /fluss:', r1.status(), p1.url());
// Mit trailing slash:
const ctx2 = await browser.newContext({ baseURL: 'http://localhost/geograsim/App/' });
const p2 = await ctx2.newPage();
const r2 = await p2.goto('fluss?level=1');
console.log('with-trail relative fluss:', r2.status(), p2.url());
await browser.close();
})();
@@ -0,0 +1,294 @@
/**
* gen-display-preview.js
* ----------------------
* Lokales Admin-Werkzeug: erstellt Bildschirmfotos aller Simulationen
* (Onboarding/Start + Spielverlauf) in zwei Auflösungen (iPad + Full HD)
* und generiert daraus eine statische Galerie-Seite.
*
* Ausgabe: App/.LocalDeveloperTools/display-preview/
* shots/<id>__<vp>__<screen>.png
* display_preview.html
*
* Start: node gen-display-preview.js
*/
const { chromium } = require('@playwright/test');
const fs = require('fs');
const path = require('path');
// --- Pfade ---------------------------------------------------------------
const APP_DIR = 'C:/xampp/htdocs/geograsim/App';
const SIMS_DIR = path.join(APP_DIR, 'sims');
const OUT_DIR = path.join(APP_DIR, '.LocalDeveloperTools', 'display-preview');
const SHOTS_DIR = path.join(OUT_DIR, 'shots');
const BASE_URL = 'http://localhost/geograsim/App/sims';
// --- Viewports -----------------------------------------------------------
const VIEWPORTS = [
{ key: 'ipad', label: 'iPad', w: 1180, h: 820 },
{ key: 'fullhd', label: 'Full HD', w: 1920, h: 1080 },
];
// --- Freundliche Namen ---------------------------------------------------
const NAMES = {
weltkueche: '🍲 Welt-Küche',
fluss: '🌊 Flussmanagement',
heli: '🚁 Helikopter-Navigation',
energiemanager: '⚡ Energiemanager',
'eu-werkstatt': '🇪🇺 EU-Werkstatt',
farmer: '🌾 Farmer',
busfahrt: '🚌 Busfahrt',
sonnensystem: '🪐 Sonnensystem',
tourismustal: '🏔️ Tourismustal',
tourismusregion: '🗺️ Tourismusregion',
logistik: '🚚 Logistik',
fluggesellschaft:'✈️ Fluggesellschaft',
staustufen: '💧 Staustufen',
entscheidungstag:'📅 Entscheidungstag',
vulkan: '🌋 Vulkan',
wal: '🐋 Wal',
kofferdetektiv: '🧳 Kofferdetektiv',
stadt: '🏙️ Stadt',
};
// --- Sims finden ---------------------------------------------------------
function findSims() {
return fs.readdirSync(SIMS_DIR, { withFileTypes: true })
.filter(d => d.isDirectory() && fs.existsSync(path.join(SIMS_DIR, d.name, 'game.html')))
.map(d => d.name)
.sort();
}
// --- In-Page: sichtbaren Start-/Weiter-Button klicken --------------------
async function clickStartLike(page) {
return await page.evaluate(() => {
const isVisible = (el) => {
if (!el) return false;
const r = el.getBoundingClientRect();
if (r.width < 4 || r.height < 4) return false;
if (r.bottom < 0 || r.right < 0) return false;
const st = getComputedStyle(el);
return st.visibility !== 'hidden' && st.display !== 'none' && st.opacity !== '0'
&& !el.disabled && !el.classList.contains('hidden');
};
// 1) Prioritäts-Selektoren (bekannte Start-/Primary-Buttons der Plattform)
const prio = [
'.ggs-qi-btn-primary', '[data-action="start"]',
'#btnStart', '#obs-start', '#phase-start',
'.sn-btn-primary', '.ggs-btn-primary',
'.era-card-cta', '.scene-btn',
'.map-btn', '.btn.primary', '.primary', '.next',
];
for (const sel of prio) {
const el = [...document.querySelectorAll(sel)].find(isVisible);
if (el) { el.click(); return sel + ' :: ' + (el.textContent || '').trim().slice(0, 24); }
}
// 2) Text-basiert — führende Emojis/Pfeile/Ziffern strippen, dann Anfang prüfen
const clean = (s) => (s || '').replace(/^[^\p{L}]+/u, '').trim();
const rx = /^(los geht|los!|los →|los\b|start|starten|jetzt starten|neue partie|partie starten|spielen|spiel starten|beginnen|anfangen|einsteigen|erntejahr starten|routenplanung starten|in dieser welt starten|szene|weiter|verstanden|fertig|karte wählen|auswählen)/i;
const cands = [...document.querySelectorAll('button, a.btn, a[role=button], .btn, [role=button], [onclick]')];
const el = cands.find(e => {
if (!isVisible(e)) return false;
const t = clean(e.textContent);
return t.length > 0 && t.length < 44 && rx.test(t);
});
if (el) { el.click(); return 'text :: ' + (el.textContent || '').trim().slice(0, 24); }
return null;
}).catch(() => null);
}
// --- Ein Screenshot ------------------------------------------------------
async function shoot(page, file) {
try {
await page.screenshot({ path: file }); // Viewport-Ausschnitt (exakte Auflösung)
return true;
} catch (e) {
console.log(' ! Screenshot-Fehler:', e.message.slice(0, 80));
return false;
}
}
(async () => {
const sims = findSims();
// Optional: nur bestimmte Sims neu schießen (HTML wird immer komplett neu erzeugt).
const onlyArg = (process.argv.find(a => a.startsWith('--only=')) || '').slice(7);
const shootList = onlyArg ? onlyArg.split(',').map(s => s.trim()).filter(Boolean) : sims;
console.log('Gefundene Sims (' + sims.length + '):', sims.join(', '));
if (onlyArg) console.log('Neu geschossen werden nur:', shootList.join(', '));
fs.mkdirSync(SHOTS_DIR, { recursive: true });
const browser = await chromium.launch();
// results[id][vp] = { start:bool, play:bool, play2:bool, errs:[] }
const results = {};
for (const vp of VIEWPORTS) {
console.log('\n===== Viewport ' + vp.label + ' (' + vp.w + '×' + vp.h + ') =====');
for (const id of shootList) {
results[id] = results[id] || {};
const r = { start: false, play: false, play2: false, errs: [] };
results[id][vp.key] = r;
const ctx = await browser.newContext({
viewport: { width: vp.w, height: vp.h },
deviceScaleFactor: 1,
});
const page = await ctx.newPage();
page.on('pageerror', e => { if (r.errs.length < 4) r.errs.push('PE:' + e.message.slice(0, 60)); });
page.on('console', m => { if (m.type() === 'error' && r.errs.length < 4) r.errs.push('CON:' + m.text().slice(0, 60)); });
const url = BASE_URL + '/' + id + '/game.html';
try {
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
} catch (e) {
r.errs.push('GOTO:' + e.message.slice(0, 50));
}
// 1) Onboarding/Start
await page.waitForTimeout(2500);
r.start = await shoot(page, path.join(SHOTS_DIR, id + '__' + vp.key + '__start.png'));
// 2) Spielverlauf: mehrere Runden klicken (Onboarding schließen -> Menü -> Spiel -> Tutorial weg)
let clicks = 0, last = null;
for (let round = 0; round < 4; round++) {
const clicked = await clickStartLike(page);
if (!clicked) break;
clicks++;
if (clicked === last) break; // gleiches Element erneut -> Abbruch
last = clicked;
await page.waitForTimeout(800);
}
if (clicks > 0) {
await page.waitForTimeout(2500);
r.play = await shoot(page, path.join(SHOTS_DIR, id + '__' + vp.key + '__play.png'));
// 3) optionaler zweiter Play-Screen: noch ein Weiter/Start, falls vorhanden
const more = await clickStartLike(page);
if (more && more !== last) {
await page.waitForTimeout(2000);
r.play2 = await shoot(page, path.join(SHOTS_DIR, id + '__' + vp.key + '__play2.png'));
}
}
const flags = [r.start ? 'S' : '-', r.play ? 'P' : '-', r.play2 ? 'P2' : '--'].join(' ');
console.log(' ' + id.padEnd(18) + '[' + flags + ']'
+ (clicks ? ' clicks=' + clicks : ' (kein Start-Button)')
+ (r.errs.length ? ' ' + r.errs.slice(0, 2).join(' | ') : ''));
await ctx.close();
}
}
await browser.close();
// --- HTML generieren ---------------------------------------------------
const genDate = new Date().toLocaleString('de-AT', {
year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit',
});
const screens = [
{ key: 'start', label: 'Onboarding / Start' },
{ key: 'play', label: 'Spielverlauf' },
{ key: 'play2', label: 'Spielverlauf (2)' },
];
function cell(id, vp, screenKey) {
const rel = 'shots/' + id + '__' + vp.key + '__' + screenKey + '.png';
const abs = path.join(SHOTS_DIR, id + '__' + vp.key + '__' + screenKey + '.png');
if (fs.existsSync(abs)) {
return '<a href="' + rel + '" target="_blank" class="shot">'
+ '<img loading="lazy" src="' + rel + '" alt="' + id + ' ' + vp.label + ' ' + screenKey + '">'
+ '<span class="cap">' + vp.label + ' · ' + vp.w + '×' + vp.h + '</span></a>';
}
return '<div class="missing">— kein Screenshot —<span class="cap">' + vp.label + '</span></div>';
}
let blocks = '';
for (const id of sims) {
const name = NAMES[id] || (id.charAt(0).toUpperCase() + id.slice(1));
// nur Screen-Zeilen zeigen, die mindestens ein Bild haben
const activeScreens = screens.filter(sc =>
VIEWPORTS.some(vp => fs.existsSync(path.join(SHOTS_DIR, id + '__' + vp.key + '__' + sc.key + '.png')))
);
const rows = (activeScreens.length ? activeScreens : [screens[0]]).map(sc =>
'<tr><th class="screen">' + sc.label + '</th>'
+ VIEWPORTS.map(vp => '<td>' + cell(id, vp, sc.key) + '</td>').join('')
+ '</tr>'
).join('\n');
blocks += '\n<section class="sim" id="sim-' + id + '">'
+ '<h2>' + name + ' <span class="simid">' + id + '</span></h2>'
+ '<table><thead><tr><th class="screen"></th>'
+ VIEWPORTS.map(vp => '<th>' + vp.label + ' <span class="res">' + vp.w + '×' + vp.h + '</span></th>').join('')
+ '</tr></thead><tbody>' + rows + '</tbody></table></section>';
}
const nav = sims.map(id =>
'<a href="#sim-' + id + '">' + (NAMES[id] || id) + '</a>'
).join('');
const html = `<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Display-Vorschau (lokal)</title>
<style>
:root { --bg:#f4f2ec; --card:#fff; --ink:#1f2b26; --accent:#1f4b37; --muted:#6b7a72; --line:rgba(0,0,0,.08); }
* { box-sizing:border-box; }
body { margin:0; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif; background:var(--bg); color:var(--ink); }
header { background:var(--accent); color:#fff; padding:20px 28px; position:sticky; top:0; z-index:10; box-shadow:0 2px 10px rgba(0,0,0,.15); }
header h1 { margin:0; font-size:1.4rem; }
header .meta { font-size:.82rem; opacity:.85; margin-top:4px; }
nav.toc { display:flex; flex-wrap:wrap; gap:6px; padding:14px 28px; background:#e9e6dd; border-bottom:1px solid var(--line); }
nav.toc a { font-size:.82rem; text-decoration:none; color:var(--accent); background:#fff; border:1px solid var(--line); border-radius:999px; padding:4px 11px; }
nav.toc a:hover { background:var(--accent); color:#fff; }
main { padding:24px 28px 60px; max-width:1500px; margin:0 auto; }
section.sim { background:var(--card); border-radius:14px; padding:18px 20px 22px; margin-bottom:26px; box-shadow:0 4px 14px rgba(0,0,0,.06); scroll-margin-top:90px; }
section.sim h2 { margin:0 0 14px; font-size:1.25rem; color:var(--accent); display:flex; align-items:baseline; gap:10px; }
.simid { font-size:.72rem; font-weight:600; color:var(--muted); background:#eef0ec; padding:2px 8px; border-radius:6px; letter-spacing:.03em; }
table { width:100%; border-collapse:collapse; table-layout:fixed; }
thead th { text-align:left; font-size:.82rem; color:var(--muted); padding:0 0 8px; font-weight:700; }
thead th .res { font-weight:500; opacity:.7; }
th.screen { width:150px; vertical-align:top; font-size:.9rem; color:var(--ink); padding-top:8px; }
td { padding:6px 6px 14px; vertical-align:top; width:calc((100% - 150px)/2); }
a.shot { display:block; border:1px solid var(--line); border-radius:8px; overflow:hidden; background:#0d1512; text-decoration:none; }
a.shot img { display:block; width:100%; max-width:100%; height:auto; }
.cap { display:block; font-size:.72rem; color:var(--muted); padding:5px 8px; background:#fafafa; border-top:1px solid var(--line); }
a.shot .cap { color:#c8d4ce; background:#141f1a; border-top:none; }
.missing { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px; min-height:120px; border:1px dashed var(--line); border-radius:8px; color:var(--muted); font-size:.85rem; background:#faf9f5; }
.missing .cap { background:none; border:none; }
@media (max-width:820px){ th.screen{width:90px;} td{width:calc((100% - 90px)/2);} }
</style>
</head>
<body>
<header>
<h1>Display-Vorschau (lokal)</h1>
<div class="meta">${sims.length} Simulationen · je Onboarding/Start + Spielverlauf · iPad 1180×820 &amp; Full HD 1920×1080 · generiert am ${genDate}</div>
</header>
<nav class="toc">${nav}</nav>
<main>${blocks}
</main>
</body>
</html>`;
const outFile = path.join(OUT_DIR, 'display_preview.html');
fs.writeFileSync(outFile, html, 'utf8');
console.log('\nHTML geschrieben: ' + outFile);
// --- Zusammenfassung ---------------------------------------------------
console.log('\n===== ZUSAMMENFASSUNG =====');
let totalShots = 0, missingPlay = [];
for (const id of sims) {
const parts = [];
for (const vp of VIEWPORTS) {
const r = (results[id] && results[id][vp.key]) || {};
const n = (r.start ? 1 : 0) + (r.play ? 1 : 0) + (r.play2 ? 1 : 0);
totalShots += n;
parts.push(vp.label + ':' + n + '/3(' + (r.start ? 'S' : '-') + (r.play ? 'P' : '-') + (r.play2 ? '2' : '-') + ')');
if (!r.play) missingPlay.push(id + '/' + vp.key);
}
console.log(' ' + id.padEnd(18) + parts.join(' '));
}
console.log('\nScreenshots gesamt: ' + totalShots);
console.log('Fehlende Play-Screens: ' + (missingPlay.length ? missingPlay.join(', ') : 'keine'));
console.log('Seite: http://localhost/geograsim/App/.LocalDeveloperTools/display-preview/display_preview.html');
})();
@@ -0,0 +1,23 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
const page = await ctx.newPage();
await page.goto('https://geograsim.at/glossar?term=reisepass');
await page.waitForTimeout(2500);
const r = await page.evaluate(() => {
const txt = document.body.innerText;
return {
hasHenley: /Henley/i.test(txt),
hasBiometric: /biometrisch/i.test(txt),
hasICAO: /ICAO/i.test(txt),
hasRelated: /Verwandt|Siehe auch|Related/i.test(txt),
hasExamples: /Beispiel/i.test(txt),
hasLinks: /Henley Passport Index|BMI|Quelle/i.test(txt),
textLength: txt.length,
};
});
console.log('reisepass-Seite:', JSON.stringify(r, null, 2));
await page.screenshot({ path: 'c:/xampp/htdocs/geograsim/.dontDeploy/playwright/glossar-reisepass.png' });
await browser.close();
})();
Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

+16
View File
@@ -0,0 +1,16 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
page.on('pageerror', e => console.log('PAGEERROR:', e.message));
page.on('console', m => { if (m.type()==='error') console.log('CONSOLE:', m.text().substring(0,200)); });
await page.goto('http://localhost/geograsim/App/heli-game');
await page.waitForTimeout(3000);
const info = await page.evaluate(() => ({
onb: typeof window.startHeliOnboarding,
tutorial: typeof window.GGS_TUTORIAL,
keys: window.GGS_TUTORIAL ? Object.keys(window.GGS_TUTORIAL) : null,
}));
console.log('heli:', JSON.stringify(info));
await browser.close();
})();
+20
View File
@@ -0,0 +1,20 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const errors = [];
page.on('pageerror', e => errors.push('PAGEERROR: ' + e.message.substring(0,200)));
page.on('console', m => { if (m.type()==='error') errors.push('CON:'+m.text().substring(0,150)); });
const r = await page.goto('http://localhost/geograsim/App/sims/heli/game.html');
console.log('status:', r?.status());
await page.waitForTimeout(2500);
const info = await page.evaluate(() => ({
onb: typeof window.startHeliOnboarding,
tutorial: typeof window.GGS_TUTORIAL,
title: window.GGS_TUTORIAL && window.GGS_TUTORIAL.title,
simId: window.GGS_TUTORIAL && window.GGS_TUTORIAL.simId,
}));
console.log('heli direct:', JSON.stringify(info));
if (errors.length) console.log('errors:', errors.slice(0,5));
await browser.close();
})();
+16
View File
@@ -0,0 +1,16 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://geograsim.at/heli-game?mission=m1');
await page.waitForTimeout(4000);
const r = await page.evaluate(() => {
const allBtns = Array.from(document.querySelectorAll('button, [role="button"]')).map(b => ({
id: b.id, cls: (b.className||'').substring(0,40), txt: (b.textContent||'').trim().substring(0,40), hidden: b.hidden
}));
const allStartLike = allBtns.filter(b => /start|freigabe|release/i.test(b.id + ' ' + b.cls + ' ' + b.txt));
return { total: allBtns.length, startLike: allStartLike.slice(0,10), first10: allBtns.slice(0,5) };
});
console.log(JSON.stringify(r, null, 2));
await browser.close();
})();
@@ -0,0 +1,21 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
for (const url of [
'https://geograsim.at/heli-game',
'https://geograsim.at/heli-game?mission=m1',
'https://geograsim.at/heli-game?mission=m2',
]) {
const page = await browser.newPage();
await page.goto(url);
await page.waitForTimeout(2500);
const r = await page.evaluate(() => ({
btn: document.querySelectorAll('#startReleaseBtn').length,
title: document.title,
visible: document.querySelector('h1, h2')?.textContent?.substring(0,60),
}));
console.log(url.padEnd(55), JSON.stringify(r));
await page.close();
}
await browser.close();
})();
+13
View File
@@ -0,0 +1,13 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const res = await page.goto('http://localhost/geograsim/App/heli-game');
console.log('status:', res?.status());
await page.waitForTimeout(1500);
const t = await page.title();
console.log('title:', t);
const text = await page.locator('body').innerText({ timeout: 2000 }).catch(()=>null);
console.log('text head:', (text || '').substring(0, 200));
await browser.close();
})();
+23
View File
@@ -0,0 +1,23 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const errors = [];
page.on('pageerror', err => errors.push('PAGEERROR: ' + err.message));
page.on('console', msg => { if (msg.type() === 'error') errors.push('CONSOLE: ' + msg.text().substring(0,200)); });
await page.goto('http://localhost/geograsim/App/fluss?level=1');
await page.waitForTimeout(3000);
const out = await page.evaluate(() => ({
hasGGS: typeof window.__GGS__ === 'object',
hasLive: typeof window.__GGS_LIVE__ === 'object',
activeMs: window.__GGS_LIVE__ && window.__GGS_LIVE__.activeMs,
idleMs: window.__GGS_LIVE__ && window.__GGS_LIVE__.idleMs,
maxSession: window.__GGS_LIVE__ && window.__GGS_LIVE__.MAX_SESSION_MS,
idleThresh: window.__GGS_LIVE__ && window.__GGS_LIVE__.IDLE_THRESHOLD_MS,
stateScore: (window.GGS_LIVE_STATE && window.GGS_LIVE_STATE()) || null,
}));
console.log('Local Fluss after 3s:');
console.log(JSON.stringify(out, null, 2));
if (errors.length) console.log('ERRORS:', errors);
await browser.close();
})();
+19
View File
@@ -0,0 +1,19 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const sims = ['fluss?level=1', 'klima-2d', 'klima-3d'];
for (const path of sims) {
const page = await browser.newPage();
await page.goto('http://localhost/geograsim/App/' + path);
await page.waitForTimeout(2500);
const info = await page.evaluate(() => ({
hasBtn: !!document.getElementById('btn-info'),
hasReg: typeof window.GGS_TUTORIAL === 'object' && typeof window.GGS_TUTORIAL.replay === 'function',
simId: window.GGS_TUTORIAL && window.GGS_TUTORIAL.simId,
title: window.GGS_TUTORIAL && window.GGS_TUTORIAL.title,
}));
console.log(path.padEnd(20), JSON.stringify(info));
await page.close();
}
await browser.close();
})();
@@ -0,0 +1,19 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
const page = await ctx.newPage();
// Direkt das KD_FUNDSACHEN-Array prüfen
await page.goto('http://localhost/geograsim/App/sims/kofferdetektiv/game-3d.html');
await page.waitForTimeout(3000);
const r = await page.evaluate(() => {
if (typeof KD_FUNDSACHEN === 'undefined') return { error: 'KD_FUNDSACHEN undefined' };
return {
count: KD_FUNDSACHEN.length,
first3: KD_FUNDSACHEN.slice(0, 3).map(f => ({ name: f.name, hasGlossarSpan: /ggs-g/.test(f.info), sample: f.info.substring(0, 100) })),
glossarSpanCount: KD_FUNDSACHEN.reduce((n, f) => n + (f.info.match(/ggs-g/g) || []).length, 0),
};
});
console.log(JSON.stringify(r, null, 2));
await browser.close();
})();
+15
View File
@@ -0,0 +1,15 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1200 } });
const page = await ctx.newPage();
await page.goto('https://geograsim.at/modul-kofferdetektiv');
await page.waitForTimeout(2000);
await page.evaluate(() => {
document.querySelector('.md-gallery')?.scrollIntoView({ block: 'center' });
});
await page.waitForTimeout(800);
await page.screenshot({ path: 'c:/xampp/htdocs/geograsim/.dontDeploy/playwright/kd-gallery.png' });
console.log('snapshot saved');
await browser.close();
})();
Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

+26
View File
@@ -0,0 +1,26 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
const page = await ctx.newPage();
const errors = [];
page.on('pageerror', e => errors.push('PAGE: ' + e.message.substring(0,150)));
page.on('console', m => { if (m.type()==='error') errors.push('CON: ' + m.text().substring(0,150)); });
const res = await page.goto('https://geograsim.at/kofferdetektiv-3d');
console.log('Status:', res?.status());
await page.waitForTimeout(4000); // 3D-Boot dauert
const info = await page.evaluate(() => ({
ggs: typeof window.__GGS__,
simId: window.__GGS__ && window.__GGS__.simId,
mode: window.__GGS__ && window.__GGS__.mode,
liveState: typeof window.GGS_LIVE_STATE,
tutorial: typeof window.GGS_TUTORIAL,
kdBase: window.KD_BASE,
threeLoaded: typeof window.THREE,
title: document.title,
visibleBody: document.body?.children?.length || 0,
}));
console.log(JSON.stringify(info, null, 2));
if (errors.length) console.log('Errors (' + errors.length + '):', errors.slice(0,5));
await browser.close();
})();
@@ -0,0 +1,17 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://geograsim.at/kofferdetektiv-3d');
await page.waitForTimeout(4000);
const r = await page.evaluate(() => ({
fundsachenCount: typeof KD_FUNDSACHEN !== 'undefined' ? KD_FUNDSACHEN.length : 'undef',
glossarSpansInFundsachen: typeof KD_FUNDSACHEN !== 'undefined'
? KD_FUNDSACHEN.reduce((n, f) => n + (f.info.match(/ggs-g/g) || []).length, 0)
: 'undef',
glossarTooltipLib: typeof window.GlossarTooltip,
liveState_keys: window.GGS_LIVE_STATE ? Object.keys(window.GGS_LIVE_STATE() || {}).sort() : null,
}));
console.log(JSON.stringify(r, null, 2));
await browser.close();
})();
+17
View File
@@ -0,0 +1,17 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://geograsim.at/lehrplan');
await page.waitForTimeout(2000);
// Extract Kompetenz-Slugs + Anker-Counts
const data = await page.evaluate(() => {
const out = [];
document.querySelectorAll('h2, h3, .kompetenz-title, .competence, [class*=anchor]').forEach(el => {
out.push(el.textContent.trim().substring(0, 80));
});
return { total: out.length, samples: out.slice(0,40) };
});
console.log(JSON.stringify(data, null, 2));
await browser.close();
})();
+17
View File
@@ -0,0 +1,17 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const ctx = await browser.newContext();
// Try /api/lehrplan if it exists publicly
const r = await ctx.request.get('https://geograsim.at/api/lehrplan');
const txt = await r.text();
console.log('lehrplan API status:', r.status());
if (r.status() === 200) {
try {
const j = JSON.parse(txt);
const anchors = j.anchors || j.kompetenzen || j;
console.log('Sample keys:', Array.isArray(anchors) ? `array ${anchors.length}` : Object.keys(anchors).slice(0,5));
} catch { console.log('not json, head:', txt.substring(0,200)); }
}
await browser.close();
})();
@@ -0,0 +1,17 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const res = await page.goto('http://localhost/geograsim/App/modul-weltkueche');
await page.waitForTimeout(700);
const r = await page.evaluate(() => {
const sec = document.getElementById('lehrplan-bezug');
const komps = [...document.querySelectorAll('.md-kompetenz .md-kompetenz-title')].map(e=>e.textContent.trim());
const anchors = document.querySelectorAll('.md-anchor').length;
const countryChips = [...document.querySelectorAll('.md-lehrplan-filters .md-chip')].map(c=>c.textContent.trim());
const empty = document.body.textContent.includes('Noch keine Kompetenzen zugeordnet');
return { hasSection: !!sec, kompetenzTitles: komps, anchorCount: anchors, countryChips, showsEmpty: empty };
});
console.log(JSON.stringify({ httpStatus: res.status(), result: r }, null, 2));
await browser.close();
})();
@@ -0,0 +1,17 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const res = await page.goto('https://geograsim.at/modul-weltkueche', { waitUntil: 'networkidle' });
await page.waitForTimeout(700);
const r = await page.evaluate(() => {
const sec = document.getElementById('lehrplan-bezug');
const komps = [...document.querySelectorAll('.md-kompetenz .md-kompetenz-title')].map(e=>e.textContent.trim());
const anchors = document.querySelectorAll('.md-anchor').length;
const countryChips = [...document.querySelectorAll('.md-lehrplan-filters .md-chip')].map(c=>c.textContent.trim());
const empty = document.body.textContent.includes('Noch keine Kompetenzen zugeordnet');
return { hasSection: !!sec, kompetenzTitles: komps, anchorCount: anchors, countryChips, showsEmpty: empty };
});
console.log(JSON.stringify({ httpStatus: res.status(), result: r }, null, 2));
await browser.close();
})();
+33
View File
@@ -0,0 +1,33 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const sims = [
'klima', 'fluss', 'heli', 'logistik', 'busfahrt', 'farmer',
'energiemanager', 'eu-werkstatt', 'sonnensystem', 'weltkueche',
'fluggesellschaft', 'entscheidungstag',
];
const rows = [];
for (const sim of sims) {
const page = await browser.newPage();
const r = await page.goto('https://geograsim.at/modul-' + sim);
if (r?.status() !== 200) { rows.push({sim, status:r?.status()}); await page.close(); continue; }
await page.waitForTimeout(800);
const data = await page.evaluate(() => {
const meta = document.querySelectorAll('.md-meta-item');
const out = {};
meta.forEach(el => {
const lbl = el.querySelector('.md-meta-label')?.textContent.trim().replace(':','');
const val = el.querySelector('.md-meta-value')?.textContent.trim();
if (lbl) out[lbl] = val;
});
return {
title: document.querySelector('h1')?.textContent.trim(),
meta: out,
};
});
rows.push({sim, ...data});
await page.close();
}
rows.forEach(r => console.log(JSON.stringify(r)));
await browser.close();
})();
@@ -0,0 +1,37 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const errs=[]; page.on('pageerror',e=>errs.push(e.message));
await page.goto('http://localhost/geograsim/App/modul-weltkueche', { waitUntil:'domcontentloaded' });
await page.waitForTimeout(700);
async function snapshot() {
return await page.evaluate(() => {
const all = [...document.querySelectorAll('.md-anchor')];
const visible = all.filter(a => !a.classList.contains('hidden'));
const byType = t => visible.filter(a => a.dataset.anchorType===t).length;
const fachCountries = {};
visible.filter(a=>a.dataset.anchorType==='fach').forEach(a=>{ fachCountries[a.dataset.country]=(fachCountries[a.dataset.country]||0)+1; });
return { total: all.length, visible: visible.length, fachVisible: byType('fach'), fachCountries };
});
}
async function clickChip(country) {
await page.evaluate((c) => {
const btn = [...document.querySelectorAll('.md-chip')].find(b => (b.dataset.country||'')===c);
if (btn) btn.click();
}, country);
await page.waitForTimeout(200);
}
const start = await snapshot();
await clickChip(''); // Alle Länder
const alle = await snapshot();
await clickChip('DE'); // Deutschland
const de = await snapshot();
await clickChip('AT'); // Österreich
const at = await snapshot();
console.log(JSON.stringify({ start, alle, de, at, pageErrors: errs }, null, 2));
await browser.close();
})();
+15
View File
@@ -0,0 +1,15 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const errs=[]; page.on('pageerror',e=>errs.push(e.message));
await page.goto('https://geograsim.at/modul-weltkueche', { waitUntil:'domcontentloaded' });
await page.waitForTimeout(900);
async function snap(){ return await page.evaluate(()=>{ const all=[...document.querySelectorAll('.md-anchor')]; const vis=all.filter(a=>!a.classList.contains('hidden')); const fc={}; vis.filter(a=>a.dataset.anchorType==='fach').forEach(a=>fc[a.dataset.country]=(fc[a.dataset.country]||0)+1); return {total:all.length, visible:vis.length, fach:vis.filter(a=>a.dataset.anchorType==='fach').length, fachCountries:fc}; }); }
async function chip(c){ await page.evaluate(c=>{const b=[...document.querySelectorAll('.md-chip')].find(x=>(x.dataset.country||'')===c); if(b)b.click();},c); await page.waitForTimeout(200); }
await chip(''); const alle=await snap();
await chip('DE'); const de=await snap();
await chip('AT'); const at=await snap();
console.log(JSON.stringify({alle,de,at,pageErrors:errs},null,2));
await browser.close();
})();
+78
View File
@@ -0,0 +1,78 @@
{
"name": "geograsim-playwright",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "geograsim-playwright",
"version": "1.0.0",
"devDependencies": {
"@playwright/test": "^1.45.0"
}
},
"node_modules/@playwright/test": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz",
"integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.60.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/playwright": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz",
"integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.60.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.60.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz",
"integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
}
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"name": "geograsim-playwright",
"version": "1.0.0",
"private": true,
"description": "Cross-Browser-Tests für GeoGraSim (lokal gegen XAMPP)",
"scripts": {
"test": "playwright test",
"test:smoke": "playwright test tests/stufe1-smoke/",
"test:flows": "playwright test tests/stufe2-flows/",
"test:debug": "playwright test --debug",
"test:headed": "playwright test --headed",
"report": "playwright show-report reports/html"
},
"devDependencies": {
"@playwright/test": "^1.45.0"
}
}
+25
View File
@@ -0,0 +1,25 @@
const { chromium } = require('@playwright/test');
(async () => {
const b = await chromium.launch(); const p = await b.newPage();
await p.addInitScript(() => {
window.__GGS__ = { simId: 'test', baseUrl: '' };
window.fetch = () => Promise.resolve({ ok:true, json:()=>Promise.resolve({}) }); // Heartbeat stummschalten
});
await p.goto('about:blank');
await p.addScriptTag({ path: 'C:/xampp/htdocs/geograsim/App/assets/js/live-client.js' });
const read = () => p.evaluate(() => window.__GGS_LIVE__ ? window.__GGS_LIVE__.activeMs : null);
const set = (v) => p.evaluate((v) => window.__GGS_LIVE__.setPaused(v), v);
await p.waitForTimeout(300);
const a1 = await read();
await p.waitForTimeout(2500); const a2 = await read(); // sollte gestiegen sein
await set(true);
await p.waitForTimeout(2500); const a3 = await read(); // sollte eingefroren sein
await set(false);
await p.waitForTimeout(2500); const a4 = await read(); // sollte wieder steigen
const grewNormal = (a2 - a1) > 1500;
const frozen = (a3 - a2) < 300;
const resumed = (a4 - a3) > 1500;
console.log(JSON.stringify({ a1,a2,a3,a4, grewNormal, frozen, resumed,
verdict: (grewNormal && frozen && resumed) ? 'PASS' : 'FAIL' }, null, 2));
await b.close();
})();
@@ -0,0 +1,49 @@
// @ts-check
const { defineConfig, devices } = require('@playwright/test');
/**
* Playwright-Konfig für GeoGraSim.
*
* Standardziel: lokaler XAMPP unter http://localhost/geograsim/App
* Über die ENV-Variable GGS_BASE_URL umstellbar (z. B. auf Prod READ-ONLY).
*
* Stand: 2026-06-11
*/
module.exports = defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 1,
// Bei Prod-Tests: max 4 Workers, damit wir nicht selbst unsere Domain
// rate-limiten. Lokal CI darf 1. Lokal manuell ohne CI: 4.
workers: process.env.CI ? 1 : 4,
reporter: [
['list'],
['html', { outputFolder: 'reports/html', open: 'never' }],
['json', { outputFile: 'reports/results.json' }],
],
use: {
baseURL: process.env.GGS_BASE_URL || 'http://localhost/geograsim/App',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
actionTimeout: 10000,
navigationTimeout: 25000,
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
// Bei Bedarf einkommentieren — kostet beim ersten Lauf ~200 MB Download
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },
],
outputDir: 'reports/artifacts',
});
@@ -0,0 +1,21 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const ctx = await browser.newContext();
// 1) live-client.js enthält GGS_TUTORIAL-Doku?
const r1 = await ctx.request.get('https://geograsim.at/assets/js/live-client.js');
const js = await r1.text();
console.log('live-client.js:');
console.log(' GGS_TUTORIAL doc:', js.includes('GGS_TUTORIAL'));
console.log(' __GGS_LIVE__ :', js.includes('__GGS_LIVE__'));
console.log(' activeMs :', js.includes('activeMs'));
console.log(' MAX_SESSION_MS :', js.includes('MAX_SESSION_MS'));
// 2) teacher.html hat fluss in LIVE_PRIMARY_FIELDS + idle-tile?
const r2 = await ctx.request.get('https://geograsim.at/teacher');
const t = await r2.text();
console.log('teacher.html:');
console.log(' fluss section :', t.includes("'fluss':"));
console.log(' idle tile :', t.includes('idle') && t.includes('Zeit pausiert'));
console.log(' 5-col tiles :', t.includes('repeat(5,1fr)'));
await browser.close();
})();
+38
View File
@@ -0,0 +1,38 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const sims = [
['fluss?level=1', 'fluss', 'Flussmanagement'],
['klima-2d', 'klima', 'Klimasimulation 2D'],
['klima-3d', 'klima-3d', 'Klimasimulation 3D'],
['heli-game', 'heli', 'Helikopter-Navigation'],
['weltkueche', 'weltkueche', 'Welt-Küche'],
['energiemanager', 'energiemanager', 'Energiemanager'],
['busfahrt', 'busfahrt', '🚌 Busfahrt'],
['entscheidungstag','entscheidungstag','🌅 Entscheidungstag'],
['eu-werkstatt', 'eu-werkstatt', '🏛️ EU-Werkstatt'],
['farmer', 'farmer', '🌾 Landwirtschaft'],
['fluggesellschaft','fluggesellschaft','✈️ Fluggesellschaft'],
['logistik', 'logistik', '📦 Logistik Europa'],
['sonnensystem', 'sonnensystem', '🪐 Sonnensystem'],
];
let ok=0, fail=0;
for (const [path, expSim, expTitle] of sims) {
const page = await browser.newPage();
await page.goto('https://geograsim.at/' + path);
await page.waitForTimeout(2500);
const info = await page.evaluate(() => ({
hasReg: typeof window.GGS_TUTORIAL === 'object' && typeof window.GGS_TUTORIAL.replay === 'function',
simId: window.GGS_TUTORIAL && window.GGS_TUTORIAL.simId,
title: window.GGS_TUTORIAL && window.GGS_TUTORIAL.title,
live: typeof window.__GGS_LIVE__ === 'object',
}));
const pass = info.hasReg && info.simId === expSim && info.title === expTitle && info.live;
console.log((pass?'✓':'✗'), path.padEnd(20), JSON.stringify(info));
if (pass) ok++; else fail++;
await page.close();
}
console.log('\n' + ok + '/' + (ok+fail) + ' Sims auf Prod mit GGS_TUTORIAL+__GGS_LIVE__');
await browser.close();
process.exit(fail ? 1 : 0);
})();
+17
View File
@@ -0,0 +1,17 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://geograsim.at/fluss?level=1');
await page.waitForTimeout(3000);
const out = await page.evaluate(() => ({
hasLive: typeof window.__GGS_LIVE__ === 'object',
activeMs: window.__GGS_LIVE__ && window.__GGS_LIVE__.activeMs,
idleMs: window.__GGS_LIVE__ && window.__GGS_LIVE__.idleMs,
maxSession: window.__GGS_LIVE__ && window.__GGS_LIVE__.MAX_SESSION_MS,
fluss: window.GGS_LIVE_STATE && window.GGS_LIVE_STATE(),
}));
console.log('Prod Fluss after 3s:');
console.log(JSON.stringify(out, null, 2));
await browser.close();
})();
+19
View File
@@ -0,0 +1,19 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const sims = ['fluss?level=1', 'klima-2d', 'klima-3d'];
for (const path of sims) {
const page = await browser.newPage();
await page.goto('https://geograsim.at/' + path);
await page.waitForTimeout(2500);
const info = await page.evaluate(() => ({
hasBtn: !!document.getElementById('btn-info'),
hasReg: typeof window.GGS_TUTORIAL === 'object' && typeof window.GGS_TUTORIAL.replay === 'function',
simId: window.GGS_TUTORIAL && window.GGS_TUTORIAL.simId,
title: window.GGS_TUTORIAL && window.GGS_TUTORIAL.title,
}));
console.log(path.padEnd(20), JSON.stringify(info));
await page.close();
}
await browser.close();
})();
+74
View File
@@ -0,0 +1,74 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const out = {};
// 1) modul-weltkueche: Lehrplan-Bezug jetzt vorhanden?
{
const page = await browser.newPage();
await page.goto('https://geograsim.at/modul-weltkueche', { waitUntil: 'domcontentloaded' });
await page.waitForTimeout(1000);
out.modulWeltkueche = await page.evaluate(() => ({
kompetenzTitles: [...document.querySelectorAll('.md-kompetenz-title')].map(e=>e.textContent.trim()),
anchorCount: document.querySelectorAll('.md-anchor').length,
showsEmpty: document.body.textContent.includes('Noch keine Kompetenzen zugeordnet'),
}));
await page.close();
}
// 2) Weltküche-Sim: Artikel + Ukraine-Feedback
{
const page = await browser.newPage();
const errs=[]; page.on('pageerror',e=>errs.push(e.message));
await page.goto('https://geograsim.at/sims/weltkueche/game.html', { waitUntil:'domcontentloaded' });
await page.waitForTimeout(2000);
out.weltkueche = await page.evaluate(async () => {
try {
if (!DISHES.length) await loadWeltkuecheData();
const dish = DISHES.find(d=>d.id==='tiroler-groestl'); current={dish};
buildTaskFor('kartoffel');
return {
kartoffelArt: ING.kartoffel.art,
badArticles: Object.entries(ING).filter(([k,v])=>!['der','die','das'].includes(v&&v.art)).map(([k])=>k),
wrongUA: explainChoice('kartoffel','ukraine',false).text,
};
} catch(e){ return { error:String(e) }; }
});
out.weltkueche.pageErrors = errs;
await page.close();
}
// 3) teacher.html: lädt + neue Funktionen da (direkter Link, den das Produkt nutzt)
{
const page = await browser.newPage();
const errs=[]; page.on('pageerror',e=>errs.push(e.message));
// teacher.html leitet ohne Login auf login.html um — Navigation abfangen, sofort auswerten
await page.route('**/login.html', r=>r.abort());
await page.goto('https://geograsim.at/teacher.html', { waitUntil:'domcontentloaded' }).catch(()=>{});
await page.waitForTimeout(1500);
out.teacher = await page.evaluate(() => ({
hasRenderGesamt: typeof renderLiveGesamt === 'function',
hasQualityBar: typeof _liveQualityBar === 'function',
qualitySample: (typeof _liveQualityBar==='function') ? _liveQualityBar(8,2) : null,
tabLabels: [...document.querySelectorAll('#tab-bar .tab')].map(t=>t.textContent.trim()),
})).catch(e=>({evalError:String(e)}));
out.teacher.pageErrors = errs;
await page.close();
}
// 4) Tourismusregion passstrasse Karte
{
const page = await browser.newPage();
const mc = await page.request.get('https://geograsim.at/sims/tourismusregion/js/maps-custom.json');
const j = await mc.json();
out.tourismus = {
keys: Object.keys(j),
passBergaufPoints: j.passstrasse?.routes?.bergauf?.length ?? null,
passSlots: (j.passstrasse?.slots||[]).length,
};
await page.close();
}
console.log(JSON.stringify(out, null, 2));
await browser.close();
})();
@@ -0,0 +1,33 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const errors = [];
page.on('pageerror', e => errors.push('PAGEERROR: ' + e.message.substring(0,200)));
page.on('console', m => { if (m.type()==='error') errors.push('CON:'+m.text().substring(0,150)); });
await page.goto('http://localhost/geograsim/App/sims/busfahrt/game.html');
await page.waitForTimeout(2500);
const info = await page.evaluate(() => ({
libLoaded: typeof window.GgsQuickIntro === 'object',
convReg: typeof window.GGS_TUTORIAL === 'object',
simId: window.GGS_TUTORIAL && window.GGS_TUTORIAL.simId,
title: window.GGS_TUTORIAL && window.GGS_TUTORIAL.title,
btnInDom: !!document.getElementById('btn-info'),
modalOpen: !!document.querySelector('.ggs-qi-back'),
}));
console.log('busfahrt:', JSON.stringify(info, null, 2));
// Wenn Modal offen: schließen via Klick auf Button
if (info.modalOpen) {
await page.click('.ggs-qi-btn-primary');
await page.waitForTimeout(200);
const after = await page.evaluate(() => !!document.querySelector('.ggs-qi-back'));
console.log('after close:', after);
}
// Wieder öffnen via GGS_TUTORIAL.replay
await page.evaluate(() => window.GGS_TUTORIAL.replay());
await page.waitForTimeout(200);
const reopen = await page.evaluate(() => !!document.querySelector('.ggs-qi-back'));
console.log('after replay:', reopen);
if (errors.length) console.log('errors:', errors.slice(0,5));
await browser.close();
})();
+21
View File
@@ -0,0 +1,21 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const errors = [];
page.on('pageerror', e => errors.push('PE:'+e.message.substring(0,100)));
await page.goto('http://localhost/geograsim/App/sims/farmer/game.html');
await page.waitForTimeout(1500);
const info = await page.evaluate(() => ({
lib: typeof window.GgsQuickIntro === 'object',
conv: typeof window.GGS_TUTORIAL === 'object',
simId: window.GGS_TUTORIAL && window.GGS_TUTORIAL.simId,
autoOpen: !!document.querySelector('.ggs-qi-back'),
btnInfo: !!document.getElementById('btn-info'),
btnGlossar: !!document.getElementById('glossar-btn'),
openInfoFn: typeof openInfoModal,
}));
console.log('farmer:', JSON.stringify(info, null, 2));
if (errors.length) console.log('errors:', errors);
await browser.close();
})();
+34
View File
@@ -0,0 +1,34 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const sims = [
'busfahrt',
'entscheidungstag',
'eu-werkstatt',
'fluggesellschaft',
'logistik',
'sonnensystem',
];
let ok = 0, fail = 0;
for (const sim of sims) {
const page = await browser.newPage();
const errors = [];
page.on('pageerror', e => errors.push('PE:'+e.message.substring(0,90)));
page.on('console', m => { if (m.type()==='error') errors.push('CON:'+m.text().substring(0,90)); });
await page.goto('http://localhost/geograsim/App/sims/' + sim + '/game.html');
await page.waitForTimeout(1500);
const info = await page.evaluate(() => ({
lib: typeof window.GgsQuickIntro === 'object',
conv: typeof window.GGS_TUTORIAL === 'object',
simId: window.GGS_TUTORIAL && window.GGS_TUTORIAL.simId,
auto: !!document.querySelector('.ggs-qi-back'),
}));
const pass = info.lib && info.conv && info.simId === sim && info.auto;
if (pass) ok++; else fail++;
console.log((pass?'✓':'✗'), sim.padEnd(18), JSON.stringify(info), errors.length ? ' errs:'+errors.slice(0,2).join(';') : '');
await page.close();
}
console.log('\n' + ok + '/' + (ok+fail) + ' GgsQuickIntro funktional');
await browser.close();
process.exit(fail ? 1 : 0);
})();
@@ -0,0 +1,21 @@
const { chromium } = require('@playwright/test');
(async () => {
const b = await chromium.launch(); const p = await b.newPage();
const errs=[]; p.on('pageerror',e=>errs.push(e.message)); p.on('console',m=>{if(m.type()==='error')errs.push(m.text());});
await p.route('**/login.html', r=>r.abort());
await p.goto('http://localhost/geograsim/App/teacher.html',{waitUntil:'domcontentloaded'}).catch(()=>{});
await p.waitForTimeout(1200);
const r = await p.evaluate(() => ({
hasSimWord: typeof simWord==='function',
hasSimCode: typeof simCode==='function',
logistikWord: typeof simWord==='function' ? simWord('logistik') : null,
logistikCode: typeof simCode==='function' ? simCode('logistik') : null,
unknownCode: typeof simCode==='function' ? simCode('irgendwas') : null,
logistikFields: (typeof LIVE_PRIMARY_FIELDS==='object' && LIVE_PRIMARY_FIELDS.logistik) ? LIVE_PRIMARY_FIELDS.logistik.map(f=>f.label) : null,
liveShortLogistik: typeof LIVE_SIM_SHORT==='object' ? LIVE_SIM_SHORT.logistik : null,
// Kürzel-Eindeutigkeit
codesUnique: (function(){ if(typeof SIM_LABELS!=='object') return null; var seen={},dup=[]; Object.keys(SIM_LABELS).forEach(k=>{var c=SIM_LABELS[k].c; if(seen[c])dup.push(c); seen[c]=1;}); return dup; })(),
}));
console.log(JSON.stringify({result:r, pageErrors:errs}, null, 2));
await b.close();
})();
@@ -0,0 +1,26 @@
const { chromium } = require('@playwright/test');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
const errors = [];
page.on('pageerror', e => errors.push('PAGEERROR: '+e.message));
page.on('console', m => { if (m.type()==='error') errors.push(m.text()); });
const res = await page.goto('http://localhost/geograsim/App/teacher');
await page.waitForTimeout(1000);
const r = await page.evaluate(() => {
const tabs = [...document.querySelectorAll('#tab-bar .tab')].map(t => t.textContent.trim());
let toggle = null;
try { toggle = (typeof _liveToggleHtml === 'function') ? _liveToggleHtml() : null; } catch(e){ toggle = 'ERR:'+e.message; }
return {
tabLabels: tabs,
hasSetLiveMode: typeof setLiveMode === 'function',
hasRenderGesamt: typeof renderLiveGesamt === 'function',
hasQualityBar: typeof _liveQualityBar === 'function',
qualitySample: (typeof _liveQualityBar === 'function') ? _liveQualityBar(8,2) : null,
toggleHasGesamt: (typeof toggle === 'string') ? toggle.includes('Gesamt') : toggle,
toggleHasTag: (typeof toggle === 'string') ? toggle.includes('Tag') : toggle,
};
});
console.log(JSON.stringify({ httpStatus: res.status(), result: r, pageErrors: errors }, null, 2));
await browser.close();
})();
@@ -0,0 +1,81 @@
// @ts-check
const { test, expect } = require('@playwright/test');
/**
* Stufe 1 · Regression: Fluss-Live-State liefert echte Werte
*
* Bug-Historie (2026-06-11): GGS_LIVE_STATE in Fluss prüfte `game.params`,
* aber `params` lebt nicht auf `game` selbst es ist nur ein berechnetes
* Result aus `E.getStatus(game)`. Daher gab GGS_LIVE_STATE() immer null
* zurück und der Lehrer-Cockpit zeigte "Diese Simulation meldet noch
* keine Live-Werte" trotz aktiver Sitzung.
*
* Fix: GGS_LIVE_STATE ruft jetzt `E.getStatus(game)` und liest `st.params`.
*
* Dieser Test ruft die Sim mit ?level=1 auf (überspringt den Level-Picker),
* wartet bis die Engine boots, und prüft dass GGS_LIVE_STATE() ein Objekt
* mit den erwarteten Schlüsseln liefert.
*/
// baseURL aus Config ist ohne trailing slash → page.goto('/fluss') würde lokal
// fälschlich auf http://localhost/fluss (404) gehen. Wir konstruieren die URL
// daher absolut. Funktioniert für Localhost mit Subpath UND Prod ohne Subpath.
const BASE = (process.env.GGS_BASE_URL || 'http://localhost/geograsim/App').replace(/\/$/, '');
test('Fluss · GGS_LIVE_STATE liefert nach Boot echte Werte', async ({ page }) => {
const errors = [];
page.on('pageerror', err => errors.push('PAGEERROR: ' + err.message));
page.on('console', msg => { if (msg.type() === 'error') errors.push('CONSOLE: ' + msg.text().substring(0, 200)); });
const res = await page.goto(BASE + '/fluss?level=1');
expect(res?.status(), 'Wrapper /fluss antwortet nicht').toBeLessThan(400);
// Auf Engine-Boot warten — Fluss setzt window.FLUSS in init() nach createGame.
await page.waitForTimeout(2500);
const info = await page.evaluate(() => ({
ggs: typeof window['__GGS__'],
engine: typeof window['FlussEngine'],
fluss: typeof window['FLUSS'],
state: typeof window['GGS_LIVE_STATE'],
}));
expect(info.ggs, `__GGS__ fehlt (errors: ${errors.join(' | ')})`).toBe('object');
expect(info.engine, `FlussEngine fehlt (errors: ${errors.join(' | ')})`).toBe('object');
expect(info.state, `GGS_LIVE_STATE fehlt (errors: ${errors.join(' | ')})`).toBe('function');
const snapshot = await page.evaluate(() => {
// @ts-ignore
const fn = window['GGS_LIVE_STATE'];
if (typeof fn !== 'function') return null;
try { return fn(); } catch (e) { return { _error: String(e) }; }
});
// 1) Snapshot muss ein Objekt sein, nicht null
expect(snapshot, 'GGS_LIVE_STATE() lieferte null (params-Bug?)').not.toBeNull();
expect(typeof snapshot, 'GGS_LIVE_STATE() lieferte kein Objekt').toBe('object');
expect(snapshot._error, 'GGS_LIVE_STATE() warf Exception: ' + snapshot?._error).toBeUndefined();
// 2) Pflichtfelder fürs Cockpit
expect(snapshot.simId).toBe('fluss');
expect(typeof snapshot.year).toBe('number');
expect(typeof snapshot.progressPct).toBe('number');
expect(typeof snapshot.budget).toBe('number');
// 3) Engine-Werte (params) — müssen Zahlen sein, nicht undefined
expect(typeof snapshot.biodiv, 'biodiv fehlt — params-Bug?').toBe('number');
expect(typeof snapshot.economy, 'economy fehlt — params-Bug?').toBe('number');
expect(typeof snapshot.flood, 'flood fehlt — params-Bug?').toBe('number');
expect(typeof snapshot.population, 'population fehlt — params-Bug?').toBe('number');
expect(typeof snapshot.food, 'food fehlt — params-Bug?').toBe('number');
// 4) Plausibilitäts-Range — params kommen aus clamp100, also 0-100 (außer population)
expect(snapshot.biodiv).toBeGreaterThanOrEqual(0);
expect(snapshot.biodiv).toBeLessThanOrEqual(100);
expect(snapshot.flood).toBeGreaterThanOrEqual(0);
expect(snapshot.flood).toBeLessThanOrEqual(100);
// 5) progressPct == 0 am Spielanfang (Jahr 0), darf aber bis 100 wachsen
expect(snapshot.progressPct).toBeGreaterThanOrEqual(0);
expect(snapshot.progressPct).toBeLessThanOrEqual(100);
});
@@ -0,0 +1,31 @@
// @ts-check
const { test, expect } = require('@playwright/test');
const slugs = require('../../fixtures/known-glossar-slugs.json');
/**
* Stufe 1 · Glossar-Slugs
*
* Prüft, dass jeder bekannte Slug
* 1) eine 200-Antwort liefert (kein 404)
* 2) das Glossar-DOM (Begriffs-Titel) tatsächlich rendert
*
* Das ist der Regressionstest gegen den Bug vom 2026-06-10
* (Tooltip-Lib + Klima-2D/3D basePath-Truthy).
*/
// Quote-Optimierung: 266 Slugs sind viel — wir laufen sie parallel.
test.describe.configure({ mode: 'parallel' });
for (const slug of slugs.slugs) {
test(`/${slug}`, async ({ page }) => {
const res = await page.goto(`/glossar?term=${encodeURIComponent(slug)}`);
expect(res.status(), `slug ${slug} antwortet nicht 200`).toBeLessThan(400);
// Glossar-Page rendert eine Tabelle oder eine Detail-Karte mit dem Begriff
const html = await page.content();
// Heuristik: der Slug sollte irgendwo im DOM auftauchen (key_slug oder Titel)
// Wir akzeptieren auch wenn der Begriff selbst (slug→Title-Heuristik) im
// Body-Text vorkommt. Echte Page-Crash würde 5xx liefern.
const bodyText = await page.locator('body').textContent({ timeout: 3000 });
expect(bodyText && bodyText.length, `leerer Body für ${slug}`).toBeGreaterThan(50);
});
}

Some files were not shown because too many files have changed in this diff Show More