diff --git a/edu/api/import.php b/edu/api/import.php
new file mode 100644
index 0000000..58c321a
--- /dev/null
+++ b/edu/api/import.php
@@ -0,0 +1,145 @@
+ 0, 'cards' => 0, 'tutorials' => 0, 'cheatsheets' => 0];
+
+ $flashcard_dir = CONTENT_DIR . '/flashcards';
+ if (is_dir($flashcard_dir)) {
+ $sort = 0;
+ foreach (glob($flashcard_dir . '/deck-*.md') as $file) {
+ $count = import_flashcard_deck($pdo, $file, $sort++);
+ $stats['decks']++;
+ $stats['cards'] += $count;
+ }
+ }
+
+ $tutorial_dir = CONTENT_DIR . '/tutorials';
+ if (is_dir($tutorial_dir)) {
+ $sort = 0;
+ foreach (glob($tutorial_dir . '/*.md') as $file) {
+ $basename = basename($file);
+ if ($basename === 'onboarding-path.md' || $basename === 'progress.md') continue;
+ import_tutorial($pdo, $file, $sort++);
+ $stats['tutorials']++;
+ }
+ }
+
+ $cheatsheet_dir = CONTENT_DIR . '/cheatsheets';
+ if (is_dir($cheatsheet_dir)) {
+ foreach (glob($cheatsheet_dir . '/*-cheatsheet.md') as $file) {
+ import_cheatsheet($pdo, $file);
+ $stats['cheatsheets']++;
+ }
+ }
+
+ return $stats;
+}
+
+function import_flashcard_deck($pdo, $filepath, $deck_sort) {
+ $content = file_get_contents($filepath);
+ $basename = basename($filepath);
+
+ preg_match('/^# Flashcard Deck: (.+)$/m', $content, $m);
+ $deck_name = $m[1] ?? str_replace(['deck-', '.md'], '', $basename);
+ $slug = slugify($deck_name);
+
+ $stmt = $pdo->prepare("
+ INSERT INTO decks (slug, name, sort_order, source_file, updated_at)
+ VALUES (:slug, :name, :sort, :src, NOW())
+ ON CONFLICT (slug) DO UPDATE SET name = :name, sort_order = :sort, source_file = :src, updated_at = NOW()
+ RETURNING id
+ ");
+ $stmt->execute([':slug' => $slug, ':name' => $deck_name, ':sort' => $deck_sort, ':src' => $basename]);
+ $deck_id = $stmt->fetchColumn();
+
+ // Parse cards
+ preg_match_all(
+ '/## Karte (\d+) \| (\w+)\s*\n\*\*Q:\*\*\s*(.+?)(?:\n)\*\*A:\*\*\s*(.+?)(?=\n---|\n## Karte|\n## Spaced|\Z)/s',
+ $content, $matches, PREG_SET_ORDER
+ );
+
+ // Delete old cards for this deck, then re-insert
+ $pdo->prepare("DELETE FROM cards WHERE deck_id = :did")->execute([':did' => $deck_id]);
+
+ $card_count = 0;
+ foreach ($matches as $i => $match) {
+ $level = strtolower(trim($match[2]));
+ $question = trim($match[3]);
+ $answer = trim($match[4]);
+
+ $stmt = $pdo->prepare("
+ INSERT INTO cards (deck_id, question, answer, level, sort_order, source_file)
+ VALUES (:did, :q, :a, :lvl, :sort, :src)
+ ");
+ $stmt->execute([
+ ':did' => $deck_id, ':q' => $question, ':a' => $answer,
+ ':lvl' => $level, ':sort' => $i, ':src' => $basename
+ ]);
+ $card_count++;
+ }
+
+ $pdo->prepare("UPDATE decks SET card_count = :c WHERE id = :id")
+ ->execute([':c' => $card_count, ':id' => $deck_id]);
+
+ return $card_count;
+}
+
+function import_tutorial($pdo, $filepath, $sort_order) {
+ $content = file_get_contents($filepath);
+ $basename = basename($filepath);
+
+ preg_match('/^# (.+)$/m', $content, $m);
+ $title = $m[1] ?? str_replace('.md', '', $basename);
+ $slug = slugify($basename);
+
+ preg_match('/\*\*Dauer\*\*:\s*ca\.\s*(\d+)/i', $content, $dm);
+ $duration = $dm[1] ?? null;
+
+ preg_match('/\*\*Ziel\*\*:\s*(.+)$/m', $content, $desc);
+ $description = $desc[1] ?? '';
+
+ $stmt = $pdo->prepare("
+ INSERT INTO tutorials (slug, title, description, content_md, duration_min, sort_order, source_file, updated_at)
+ VALUES (:slug, :title, :desc, :content, :dur, :sort, :src, NOW())
+ ON CONFLICT (slug) DO UPDATE SET
+ title = :title, description = :desc, content_md = :content,
+ duration_min = :dur, sort_order = :sort, source_file = :src, updated_at = NOW()
+ ");
+ $stmt->execute([
+ ':slug' => $slug, ':title' => $title, ':desc' => $description,
+ ':content' => $content, ':dur' => $duration,
+ ':sort' => $sort_order, ':src' => $basename
+ ]);
+}
+
+function import_cheatsheet($pdo, $filepath) {
+ $content = file_get_contents($filepath);
+ $basename = basename($filepath);
+
+ preg_match('/^# (.+)$/m', $content, $m);
+ $title = $m[1] ?? str_replace(['-cheatsheet', '.md'], ['', ''], $basename);
+ $slug = slugify($basename);
+ $category = ucfirst(str_replace(['-cheatsheet.md', '-'], ['', ' '], $basename));
+
+ $stmt = $pdo->prepare("
+ INSERT INTO cheatsheets (slug, title, category, content_md, source_file, updated_at)
+ VALUES (:slug, :title, :cat, :content, :src, NOW())
+ ON CONFLICT (slug) DO UPDATE SET
+ title = :title, category = :cat, content_md = :content, source_file = :src, updated_at = NOW()
+ ");
+ $stmt->execute([
+ ':slug' => $slug, ':title' => $title, ':cat' => $category,
+ ':content' => $content, ':src' => $basename
+ ]);
+}
+
+function slugify($text) {
+ $text = str_replace(['.md', 'deck-'], '', $text);
+ $text = strtolower($text);
+ $text = preg_replace('/[^a-z0-9\-]/', '-', $text);
+ $text = preg_replace('/-+/', '-', $text);
+ return trim($text, '-');
+}
diff --git a/edu/content/cheatsheets/commands-cheatsheet.md b/edu/content/cheatsheets/commands-cheatsheet.md
new file mode 100644
index 0000000..ac835a0
--- /dev/null
+++ b/edu/content/cheatsheets/commands-cheatsheet.md
@@ -0,0 +1,81 @@
+# Omnis Commands Cheat Sheet
+
+## Die 10 wichtigsten Befehle
+
+| # | Befehl | Was es tut | JS-Aequivalent |
+|---|--------|-----------|----------------|
+| 1 | `Calculate lX as expr` | Berechnung + Zuweisung | `let x = expr` |
+| 2 | `Do obj.$method()` | Methodenaufruf | `obj.method()` |
+| 3 | `If ... Else ... End If` | Bedingung | `if ... else ...` |
+| 4 | `For i from a to b step c` | Zaehlschleife | `for (let i=a; i<=b; i+=c)` |
+| 5 | `For each line in list` | Listen-Iteration | `for (const item of arr)` |
+| 6 | `While ... End While` | Bedingungsschleife | `while (...)` |
+| 7 | `Quit method lVal` | Methode beenden + Return | `return val` |
+| 8 | `OK message {text}` | Nachricht anzeigen | `alert(text)` |
+| 9 | `Switch ... Case ... End Switch` | Fallunterscheidung | `switch ... case` |
+| 10 | `Do inherited` | Super-Methode rufen | `super.method()` |
+
+## Variablen & Berechnung
+```omnis
+Calculate lName as "Harry" String zuweisen
+Calculate lSum as lA + lB Berechnen
+Calculate lText as con("Hallo ", lName) String verketten
+Calculate lUpper as upp(lName) Grossbuchstaben
+Calculate lLen as len(lName) String-Laenge
+Calculate lSub as mid(lName, 2, 3) Substring
+Calculate lPos as pos("r", lName) Zeichen finden
+```
+
+## Kontrollfluss
+```omnis
+If lAge >= 18 If lStatus = 'A' | lStatus = 'B'
+ # erwachsen # A oder B
+Else If lAge >= 14 End If
+ # jugendlich
+Else Switch lStatus
+ # kind Case 'A'
+End If # aktiv
+ Case 'I'
+While lX < 100 # inaktiv
+ Calculate lX as lX + 1 Default
+End While # sonstiges
+ End Switch
+For lI from 1 to 10 step 1
+ # lI = 1, 2, 3, ...10
+End For
+```
+
+## Dialoge
+```omnis
+OK message {Fertig!} Info-Dialog
+Yes/No message {Loeschen?} Ja/Nein Dialog
+ If flag true ... End If Ergebnis pruefen
+Prompt for input {Name?} lName Eingabe-Dialog
+No/Yes message {Abbrechen?} Nein/Ja (Default=Nein)
+```
+
+## Debugging
+```omnis
+Breakpoint Debugger oeffnen (= debugger; in JS)
+OK message {Debug: [lVar]} Quick-Debug mit Message
+Trace log {[lVar]} In Trace-Log schreiben
+```
+
+## SQL-Befehle (ueber Session/Statement)
+```omnis
+Do lSess.$logon(host, user, pw, 'sess') Returns #F Verbinden
+Do lStmt.$prepare('SELECT...WHERE id=:1') Returns #F Vorbereiten
+Do lStmt.$execute(lID) Returns #F Ausfuehren
+Do lStmt.$fetch(lList, kFetchAll) Returns #F Ergebnis holen
+Do lStmt.$execdirect('INSERT...') Returns #F Direkt ausfuehren
+```
+
+## Merkhilfe: Die Befehls-Familien
+```
+DATEN: Calculate, Do, Set current list
+FLUSS: If/Else/End If, For/End For, While/End While, Switch/Case/End Switch
+METHODE: Quit method, Do inherited, Quit all methods
+DIALOG: OK message, Yes/No message, Prompt for input
+FENSTER: Open window, Close window, Redraw, Enter data
+SQL: $logon, $prepare, $execute, $fetch, $execdirect
+```
diff --git a/edu/content/cheatsheets/notation-cheatsheet.md b/edu/content/cheatsheets/notation-cheatsheet.md
new file mode 100644
index 0000000..df96b3b
--- /dev/null
+++ b/edu/content/cheatsheets/notation-cheatsheet.md
@@ -0,0 +1,88 @@
+# Omnis $-Notation Cheat Sheet
+
+## Navigation durch die Hierarchie
+```
+$root Die Omnis-Wurzel
+ .$libs Alle geoeffneten Libraries
+ .myLib Bestimmte Library
+ .$classes Alle Klassen
+ .myClass Bestimmte Klasse
+ .$methods Alle Methoden
+ .$objs Alle UI-Objekte
+ .$ivars Alle Instanz-Variablen
+```
+
+## Kontext-Shortcuts
+| Pfad | Bedeutung | JS-Aequivalent |
+|------|-----------|---------------|
+| `$cinst` | Aktuelle Instanz | `this` |
+| `$cclass` | Aktuelle Klasse | `this.constructor` |
+| `$clib` | Aktuelle Library | - |
+| `$ctask` | Aktueller Task | Global scope |
+| `$cmethod` | Aktuelle Methode | - |
+| `$cwind` | Aktuelles Window | `window` |
+
+## UI-Objekte
+```omnis
+$cinst.$objs.feldName.$contents Wert lesen
+$cinst.$objs.feldName.$contents.$assign(x) Wert setzen
+$cinst.$objs.feldName.$visible Sichtbar?
+$cinst.$objs.feldName.$enabled Aktiv?
+$cinst.$objs.feldName.$textcolor Textfarbe
+$cinst.$objs.feldName.$backcolor Hintergrundfarbe
+$cinst.$objs.feldName.$font Schriftart
+$cinst.$objs.feldName.$tooltip Tooltip-Text
+$cinst.$objs.feldName.$dataname Gebundene Variable
+```
+
+## Listen-Notation
+```omnis
+lList.$linecount Anzahl Zeilen
+lList.$line Aktuelle Zeile (1-basiert)
+lList.$colcount Anzahl Spalten
+lList.$cols Spalten-Collection
+lList.$cols.$add(...) Spalte hinzufuegen
+lList.$add() Zeile hinzufuegen
+lList.$remove(n) Zeile n entfernen
+lList.$clear() Alle Zeilen loeschen
+lList.$search(expr) Suchen
+lList.$sort(col, asc) Sortieren
+lList.$sendall(expr) Auf alle Zeilen anwenden
+lList.$merge(other) Listen zusammenfuegen
+lList.$selected Auswahl-Flag der akt. Zeile
+lList.[3].colname Spalte in Zeile 3
+lList.colname Spalte in aktueller Zeile
+```
+
+## Methoden aufrufen
+```omnis
+Do iObj.$method() Ohne Rueckgabe
+Do iObj.$method() Returns lResult Mit Rueckgabe
+Do iObj.$method(p1, p2) Returns lR Mit Parametern
+Do $cinst.$objs.btn.$visible.$assign(kTrue) Property setzen
+Do inherited Super-Methode
+```
+
+## Wichtige Konstanten
+```
+kTrue / kFalse Boolean
+kCharacter String-Typ
+kInteger Ganzzahl-Typ
+kNumber Dezimal-Typ
+kDate Datum-Typ
+kList Listen-Typ
+kRow Row-Typ
+kBoolean Boolean-Typ
+kFetchAll Alle Zeilen holen
+kFetchOne Eine Zeile holen
+kSimplechar Einfacher String-Subtyp
+k2dp 2 Dezimalstellen
+```
+
+## Merkhilfe: LESEN vs SETZEN
+```
+LESEN: Calculate lVal as $cinst.$objs.feld.$contents
+SETZEN: Do $cinst.$objs.feld.$contents.$assign(lVal)
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ Nie = verwenden! Immer $assign() !
+```
diff --git a/edu/content/cheatsheets/solution2-cheatsheet.md b/edu/content/cheatsheets/solution2-cheatsheet.md
new file mode 100644
index 0000000..7b35357
--- /dev/null
+++ b/edu/content/cheatsheets/solution2-cheatsheet.md
@@ -0,0 +1,112 @@
+# Solution2 ERP Cheat Sheet
+
+## Klassen-Praefixe - Was ist was?
+```
+w = Window (UI-Formular) wOrders, wLogin, wCustomerInvoicesWithList
+o = Object (Business-Logik) oOrders, oLogin, oCreateInvoice
+T_ = Table (Daten-Cursor) T_Orders, T_Parts, T_Super
+sc = Schema (Datenstruktur) scOrderItems, scStaffWorkTimeLog
+Q = Query QMOResourcesProcessingQuantity
+r = Report (Druckbericht) rInvoice, rDeliveryNote, rCompanyList
+js = JS Remote Form (Web) jsMainForm, jsSubForm, jsMainMenu
+b = Base-Objekt (Submodul) bobjs
+```
+
+## Superklassen-Hierarchie
+```
+wMainWindow -----> Haupt-Dateneingabe-Fenster
+wBaseWindow -----> Modale/Hilfs-Fenster
+wSubWindow -----> Eingebettete Sub-Panels
+oSuperData -----> Basis fuer Business-Logik-Objekte
+T_Super -----> Basis fuer alle Table-Klassen
+T_SuperVersion --> Versionierte Table-Klasse
+```
+
+## Die 22 Libraries
+| Library | Bereich |
+|---------|---------|
+| `solution2` | Kern: Login, Navigation, Enterprise, Mitarbeiter, Kalender |
+| `solution2Services` | Hintergrund-Services, Push, Callbacks |
+| `BasicTools` | HTML, Import, Edit-Utilities |
+| `DBAccess` | Datenbank-Zugriff, Berechtigungen |
+| `Sales` | Auftraege, Lieferscheine, Rechnungen |
+| `Purchase` | Einkauf, Lieferanten, E-Rechnungen |
+| `Customer` | Kundenstamm, Berichte, Aktivitaeten |
+| `Manufacturing` | Fertigungsauftraege, Stuecklisten, Kalkulation |
+| `QualityInspection` | Qualitaetsprotokolle, Labor |
+| `AdminAccounting` | Buchhaltung, Export, ZUGFeRD |
+| `PPS` | Produktionsplanung (MRP) |
+| `StaffWorkTime` | Arbeitszeit, Urlaub |
+| `MobileWork` | Mobile/Web-Client (Remote Forms) |
+| `_Analysis_SIM` | Analyse/Simulation |
+
+## Datenbank-Pattern
+```
+DB: masterdemo Schema: soluser Engine: PostgreSQL
+
+JEDE Tabelle hat:
+- created (TIMESTAMP) Erstellungszeitpunkt
+- modtime (TIMESTAMP) Letzte Aenderung
+- revisor (INTEGER) Wer hat geaendert (Employee-ID)
+- valid (BOOLEAN) Soft-Delete Flag
+
+VIELE Tabellen haben Dual-Signature:
+- firstsigned (BOOLEAN)
+- needsecondsignature (BOOLEAN)
+- secondrevisor (INTEGER)
+- secondsigned (BOOLEAN)
+```
+
+## Die wichtigsten Tabellen
+```
+STAMMDATEN:
+ contact Kontakte/Ansprechpartner
+ warehouse Lager
+ dunninglevels Mahnstufen
+
+VERKAUF:
+ orders Auftraege (103 Spalten!)
+ orderitems Auftragspositionen
+ deliverynoteitems Lieferscheinpositionen
+ customerinvoices Ausgangsrechnungen (90 Spalten)
+ customerinvoiceitems Rechnungspositionen
+
+EINKAUF:
+ purchaseorder Bestellungen (72 Spalten)
+ supplierinvoices Eingangsrechnungen
+ stockreceipt Wareneingaenge
+ stockreceiptitems Wareneingangs-Positionen
+
+LAGER:
+ stockrotationlog Lagerbewegungen
+ inventoryquantities Bestandsmengen
+ inventoryvalues Bestandswerte
+
+FERTIGUNG:
+ qualityprotocolpending Qualitaetspruefungen
+```
+
+## Typischer Geschaeftsprozess-Flow
+```
+Auftrag (orders)
+ |
+ v
+Lieferschein (deliverynoteitems)
+ |
+ v
+Rechnung-Freigabe (customerinvoicerequirement)
+ |
+ v
+Rechnung (customerinvoices)
+ |
+ v
+Buchhaltung-Export (transfertoadminaccounting)
+```
+
+## RBAC-System
+```
+Berechtigungen = Form-Path-basiert
+ -> Berechtigungsgruppen werden Form-Pfaden zugewiesen
+ -> Nicht pro Screen, sondern pro Modul/Navigation
+ -> T_ModuleAccess2Methods steuert Methoden-Zugriff
+```
diff --git a/edu/content/cheatsheets/sql-cheatsheet.md b/edu/content/cheatsheets/sql-cheatsheet.md
new file mode 100644
index 0000000..b3dab06
--- /dev/null
+++ b/edu/content/cheatsheets/sql-cheatsheet.md
@@ -0,0 +1,104 @@
+# SQL in Omnis Cheat Sheet
+
+## Verbindung aufbauen
+```omnis
+# Session-Objekt erstellen (im IDE: Variable lSess vom Typ Object)
+Do lSess.$logon('localhost', 'postgres', 'passwort', 'meineSess') Returns #F
+If flag true
+ # Verbunden!
+End If
+```
+
+## SELECT - Daten lesen
+```omnis
+# SICHER mit Bind-Variablen (:1, :2)
+Do lStmt.$prepare('SELECT * FROM contact WHERE companyid = :1 AND valid = :2') Returns #F
+Do lStmt.$execute(lCompanyID, kTrue) Returns #F
+Do lStmt.$fetch(lList, kFetchAll) Returns #F
+
+# Alternative: @[]-Syntax (auch sicher)
+Do lStmt.$execdirect('SELECT * FROM orders WHERE orderid = @[lOrderID]') Returns #F
+Do lStmt.$fetch(lList, kFetchAll) Returns #F
+```
+
+## INSERT - Daten einfuegen
+```omnis
+Do lStmt.$execdirect(con( |
+ 'INSERT INTO mytable (name, status, created, revisor, valid) ', |
+ 'VALUES (@[lName], @[lStatus], now(), @[tEmployeeID], true)' |
+ )) Returns #F
+```
+
+## UPDATE - Daten aendern
+```omnis
+Do lStmt.$prepare(con( |
+ 'UPDATE orders SET status = :1, modtime = now(), revisor = :2 ', |
+ 'WHERE orderid = :3' |
+ )) Returns #F
+Do lStmt.$execute(lNewStatus, tEmployeeID, lOrderID) Returns #F
+```
+
+## DELETE - Daten loeschen (VORSICHT!)
+```omnis
+# In Solution2: Soft-Delete bevorzugen!
+Do lStmt.$execdirect(con( |
+ 'UPDATE orders SET valid = false, modtime = now(), revisor = @[tEmployeeID] ', |
+ 'WHERE orderid = @[lOrderID]' |
+ )) Returns #F
+```
+
+## Transaktionen
+```omnis
+Do lSess.$begin() Returns #F ;; START TRANSACTION
+ # ... mehrere SQL-Operationen ...
+If lAllesOK
+ Do lSess.$commit() Returns #F ;; COMMIT
+Else
+ Do lSess.$rollback() Returns #F ;; ROLLBACK
+End If
+```
+
+## Fehlerbehandlung
+```omnis
+Do lStmt.$execute(lParam) Returns #F
+If not(flag true)
+ Calculate lError as lSess.$lasterrortext
+ OK message {SQL-Fehler: [lError]}
+End If
+```
+
+## Fetch-Modi
+```
+kFetchAll -> Alle Zeilen in Liste (fuer kleine/mittlere Ergebnisse)
+kFetchOne -> Eine Zeile (fuer Schleifen bei grossen Ergebnissen)
+kFetchBulk -> Block von N Zeilen (Kompromiss)
+```
+
+## WICHTIGE REGELN
+```
+IMMER: @[lVar] oder :1 (Bind-Variablen = SICHER)
+NIE: [lVar] (Text-Substitution = SQL-INJECTION!)
+
+IMMER: WHERE valid = true (Soft-Delete beachten!)
+IMMER: modtime, revisor setzen bei UPDATE
+IMMER: created, revisor, valid setzen bei INSERT
+IMMER: #F pruefen nach jedem SQL-Aufruf
+```
+
+## Solution2-spezifische SQL-Tipps
+```
+Schema: soluser
+Datenbank: masterdemo
+Engine: PostgreSQL
+
+Typische WHERE-Klausel:
+ WHERE valid = true
+ AND companyid = @[lCompanyID]
+ ORDER BY modtime DESC
+
+Datum/Zeit:
+ now() -> aktueller Timestamp
+ current_date -> aktuelles Datum
+ @[#D] -> Omnis-Datum als Parameter
+ @[#T] -> Omnis-Timestamp als Parameter
+```
diff --git a/edu/content/cheatsheets/windows-cheatsheet.md b/edu/content/cheatsheets/windows-cheatsheet.md
new file mode 100644
index 0000000..5925fd9
--- /dev/null
+++ b/edu/content/cheatsheets/windows-cheatsheet.md
@@ -0,0 +1,143 @@
+# Window-Architektur Cheat Sheet
+
+## Klassenhierarchie
+```
+wBaseWindow (Wurzel - keine Superklasse)
+ +-- wMainWindow (eigenstaendige Fenster: wOrders, wParts...)
+ +-- wSubWindow (eingebettete Panels: wOrder, wOrderItems...)
+```
+
+## Wichtigste Instanzvariablen
+
+### Auf ALLEN Fenstern (von wBaseWindow):
+| Variable | Zweck |
+|----------|-------|
+| `ioCode` | Business-Logik-Objekt |
+| `iiRef2SubWindow` | Referenz auf aktives SubWindow |
+| `ibControl2SubWindow` | `kTrue` = Commands gehen an SubWindow |
+| `ilList` | Haupt-Datenliste |
+| `irRow` | Aktuelle Datenzeile |
+| `inEDMode` | Bearbeitungsmodus |
+| `icCurrTabPane` | Aktiver Tab-Reiter |
+
+### Nur auf wSubWindow:
+| Variable | Zweck |
+|----------|-------|
+| `iiRef2Parent` | Referenz ZURUECK zum Elter |
+| `icFormWindow` | Name fuer Rechte-Check |
+| `icSubWindow` | SubWindow-Feldname |
+
+## DAS zentrale Pattern: Command-Delegation
+```omnis
+## Auf jedem Fenster identisch:
+If $cinst.ibControl2SubWindow
+ Do iiRef2SubWindow.$cXxx() ## -> Weiter ans SubWindow
+Else
+ Do $cinst.ioCode.$cXxx() ## -> Business-Logik
+End If
+```
+
+## Lebenszyklus
+
+### $construct (SubWindow meldet sich an):
+```omnis
+Do inherited
+Set reference iiRef2SubWindow to $cinst.$objs.wSubWindow.$ref
+Set reference iiRef2Parent to pRef2Parent
+Do iiRef2Parent.$setControl2SubWindow(kTrue) ## KONTROLLE UEBERNEHMEN
+```
+
+### $destruct (SubWindow meldet sich ab):
+```omnis
+If iiRef2Parent
+ Do iiRef2Parent.$setControl2SubWindow(kFalse) ## KONTROLLE ZURUECKGEBEN
+End If
+Do ilList.$define()
+Do irRow.$define()
+```
+
+### $closeWindow (delegiert nach oben):
+```omnis
+Do iiRef2Parent.$closeWindow() ## Parent schliesst das Fenster
+```
+
+### $EnableTabPane (sperrt Tabs nach oben):
+```omnis
+Do iiRef2Parent.$EnableTabPane($cinst.inEDMode) ## Parent sperrt seine Tabs
+```
+
+## Command-Fluss Beispiel (Sales/wOrders)
+```
+User klickt [Edit]
+ -> wOrders.$cEdit (wMainWindow)
+ -> $closeRelatedWindow()
+ -> ibControl2SubWindow = kTrue
+ -> wOrder.$cEdit (wSubWindow Ebene 1)
+ -> ibControl2SubWindow = kTrue
+ -> wOrderItems.$cEdit (wSubWindow Ebene 2)
+ -> ibControl2SubWindow = kFalse
+ -> ioCode.$cEdit() == oOrderItems.$cEdit()
+ -> HIER passiert die Aktion!
+```
+
+## Alle delegierten Commands
+
+### wMainWindow (mit $closeRelatedWindow):
+`$cEdit`, `$cCancel`, `$cAudit`, `$cNextVersion`
+
+### wSubWindow (ohne $closeRelatedWindow):
+`$cEdit`, `$cCancel`, `$cInsert`, `$cDelete`, `$cSave`, `$cPrint`, `$cAudit`, `$cNextVersion`, `$cFirst`, `$cLast`
+
+## Referenz-Kette
+```
+ iiRef2SubWindow iiRef2SubWindow
+MainWindow ----------> SubWindow ----------> SubSubWindow
+ <---------- <----------
+ iiRef2Parent iiRef2Parent
+```
+
+## Cross-Library Vererbung
+```
+# Innerhalb solution2-Library:
+wMeinFenster extends wSubWindow
+
+# Aus anderer Library (Sales, Manufacturing...):
+wMeinFenster extends solution2.wSubWindow ## Library-Prefix noetig!
+```
+
+## TabPane-Konvention
+```
+TabPane-Objekt heisst IMMER: TP
+Zugriff: $cwind.$objs.TP.$enableTabPanes(pEDMode)
+```
+
+## 7 Goldene Regeln
+```
+1. ibControl2SubWindow bestimmt ALLES
+2. $construct: inherited aufrufen = Kette aufbauen
+3. $destruct: Kontrolle zurueckgeben
+4. Commands fliessen ABWAERTS (Main -> Sub -> SubSub)
+5. TabPane-Sperren fliessen AUFWAERTS (Sub -> Parent)
+6. $closeWindow delegiert AUFWAERTS
+7. ioCode ist der letzte Stopp (kein SubWindow aktiv)
+```
+
+## Debugging Quick-Check
+```omnis
+# 1. Wer hat die Kontrolle?
+Calculate lDebug as $cinst.ibControl2SubWindow
+
+# 2. Ist SubWindow-Referenz gueltig?
+If iiRef2SubWindow
+ # Referenz existiert
+End If
+
+# 3. Welches SubWindow ist aktiv?
+Calculate lClass as iiRef2SubWindow.$classname
+
+# 4. Hat $construct inherited aufgerufen?
+# -> Breakpoint in wSubWindow.$construct setzen
+
+# 5. Ist Superklasse korrekt?
+# -> Class Inspector -> Superclass pruefen
+```
diff --git a/edu/content/flashcards/deck-commands.md b/edu/content/flashcards/deck-commands.md
new file mode 100644
index 0000000..61debf4
--- /dev/null
+++ b/edu/content/flashcards/deck-commands.md
@@ -0,0 +1,163 @@
+# Flashcard Deck: Omnis Commands
+
+**Thema**: Die wichtigsten Omnis-Befehle
+**Schwierigkeit**: Anfaenger -> Fortgeschritten
+**Karten**: 25
+
+---
+
+## Karte 1 | Basis
+**Q:** Was macht der Befehl `Calculate`?
+**A:** Berechnet einen Ausdruck und weist das Ergebnis einer Variablen zu. `Calculate lX as 5 + 3` -> lX = 8. Aequivalent zu `let x = 5 + 3` in JS.
+
+---
+
+## Karte 2 | Basis
+**Q:** Was macht der Befehl `Do`?
+**A:** Fuehrt eine Methode oder einen Notation-Ausdruck aus. `Do iObj.$method() Returns lResult`. Wie ein Funktionsaufruf.
+
+---
+
+## Karte 3 | Basis
+**Q:** Was macht `Quit method`?
+**A:** Beendet die aktuelle Methode und gibt optional einen Wert zurueck. `Quit method lResult` = `return lResult` in JS.
+
+---
+
+## Karte 4 | Basis
+**Q:** Was macht `OK message`?
+**A:** Zeigt ein Dialogfeld mit einer Nachricht. `OK message {Hallo [lName]!}` - wie `alert()` in JS. Variablen in `[]` werden ersetzt.
+
+---
+
+## Karte 5 | Basis
+**Q:** Was ist der Unterschied zwischen `[lVar]` und `@[lVar]` in SQL?
+**A:** `[lVar]` = direkte Text-Substitution (SQL-Injection-Gefahr!). `@[lVar]` = Bind-Variable (sicher, parametrisiert). **Immer `@[lVar]` in SQL verwenden!**
+
+---
+
+## Karte 6 | Basis
+**Q:** Was macht `If / Else / End If`?
+**A:** Bedingte Ausfuehrung - identisch zu anderen Sprachen. `If lAge >= 18 ... Else ... End If`. WICHTIG: `End If` ist Pflicht!
+
+---
+
+## Karte 7 | Basis
+**Q:** Was macht `For ... End For`?
+**A:** Zaehlschleife: `For lI from 1 to 10 step 1 ... End For`. Oder Listen-Iteration: `For each line in list from 1 to lList.$linecount step 1 ... End For`.
+
+---
+
+## Karte 8 | Basis
+**Q:** Was macht `While ... End While`?
+**A:** Bedingungsschleife: `While lCounter < 100 ... End While`. Wie `while` in jeder anderen Sprache.
+
+---
+
+## Karte 9 | Mittel
+**Q:** Was macht `Switch / Case / Default / End Switch`?
+**A:** Fallunterscheidung: `Switch lStatus / Case 'A' ... Case 'B' ... Default ... End Switch`. Wie `switch` in JS, aber OHNE `break` noetig!
+
+---
+
+## Karte 10 | Mittel
+**Q:** Was macht `Quit all methods`?
+**A:** Bricht die GESAMTE Methodenkette ab (alle aufrufenden Methoden). Wie ein sofortiger Hard-Exit. Vorsichtig verwenden!
+
+---
+
+## Karte 11 | Mittel
+**Q:** Was macht `Enter data`?
+**A:** Uebergibt die Kontrolle an den Benutzer, damit er Daten eingeben kann. Die Methode pausiert, bis der User fertig ist. Gibt es in modernen Sprachen nicht!
+
+---
+
+## Karte 12 | Mittel
+**Q:** Was macht `Yes/No message`?
+**A:** Zeigt einen Dialog mit Ja/Nein-Buttons. Ergebnis in `#F`: `Yes/No message {Sicher?} / If flag true ... End If`. Wie `confirm()` in JS.
+
+---
+
+## Karte 13 | Mittel
+**Q:** Was ist `#F` (der Flag)?
+**A:** Ein globaler Boolean-Wert, der von vielen Befehlen gesetzt wird. `Returns #F` bei SQL-Operationen. Wie ein impliziter Return-Code. Abfrage mit `If flag true`.
+
+---
+
+## Karte 14 | Mittel
+**Q:** Was macht `Breakpoint`?
+**A:** Haelt die Ausfuehrung an und oeffnet den Debugger - wie `debugger;` in JavaScript.
+
+---
+
+## Karte 15 | Mittel
+**Q:** Was macht `Begin/End reversible block`?
+**A:** Definiert Code, der automatisch rueckgaengig gemacht wird, wenn die aeussere Methode endet. Fuer temporaere Aenderungen (Cursor, Fenster-Zustand etc.).
+
+---
+
+## Karte 16 | Mittel
+**Q:** Was macht `Set main file`?
+**A:** Setzt die Hauptdatei (Tabelle) fuer Datenoperationen. Legacy-Konzept aus der Omnis-eigenen DB, wird bei SQL-DAMs weniger gebraucht.
+
+---
+
+## Karte 17 | Fortgeschritten
+**Q:** Was macht `Do inherited`?
+**A:** Ruft die gleichnamige Methode der Superklasse auf - wie `super.method()` in JS oder `super().method()` in Python.
+
+---
+
+## Karte 18 | Fortgeschritten
+**Q:** Was macht `Install menu`?
+**A:** Installiert ein Menu-Objekt in der Menueleiste. Omnis hat ein eigenes Menu-System (nicht HTML-basiert fuer Desktop-Apps).
+
+---
+
+## Karte 19 | Fortgeschritten
+**Q:** Was macht `Prepare for insert` / `Prepare for edit`?
+**A:** Bereitet einen Datensatz zum Einfuegen/Bearbeiten vor. Setzt interne Flags und initialisiert Felder. Teil des Omnis-Daten-Workflows.
+
+---
+
+## Karte 20 | Fortgeschritten
+**Q:** Was macht `Update files`?
+**A:** Schreibt geaenderte Daten zurueck in die Datenbank. Wie `COMMIT` in SQL + `$flush()`. Teil des legacy Daten-Workflow.
+
+---
+
+## Karte 21 | Basis
+**Q:** Was macht `Set current list`?
+**A:** Setzt die "aktive" Liste fuer Listenbefehle. `Set current list lMyList`. Legacy-Ansatz - besser: direkte $-Notation verwenden.
+
+---
+
+## Karte 22 | Mittel
+**Q:** Was macht `Redraw`?
+**A:** Erzwingt das Neuzeichnen eines UI-Elements. `Do $cinst.$objs.myField.$redraw()` oder `Redraw {myField}`. Wie `forceUpdate()` in React.
+
+---
+
+## Karte 23 | Mittel
+**Q:** Was macht `Send to back / Bring to front`?
+**A:** Aendert die Z-Order von UI-Elementen (welches Element vorne liegt). Desktop-UI-Konzept.
+
+---
+
+## Karte 24 | Fortgeschritten
+**Q:** Was macht `Do redirect`?
+**A:** Leitet eine Methode auf ein anderes Objekt um. `Do redirect (lOtherObj, 'methodName')`. Wie Delegation/Proxy in JS.
+
+---
+
+## Karte 25 | Fortgeschritten
+**Q:** Was macht `Queue` Befehle (Queue set, Queue send, etc.)?
+**A:** Reiht Events in die Event-Queue ein, die nach der aktuellen Methode verarbeitet werden. Wie `setTimeout(fn, 0)` / `queueMicrotask()` in JS - asynchrone Ausfuehrung.
+
+---
+
+## Spaced Repetition Tracking
+
+| Karte | Zuletzt | Naechste | Schwierigkeit |
+|-------|---------|----------|---------------|
+| (wird beim Ueben gefuellt) | | | |
diff --git a/edu/content/flashcards/deck-gotchas.md b/edu/content/flashcards/deck-gotchas.md
new file mode 100644
index 0000000..a2c6600
--- /dev/null
+++ b/edu/content/flashcards/deck-gotchas.md
@@ -0,0 +1,102 @@
+# Flashcard Deck: Gotchas & Haeufige Fehler
+
+**Thema**: Typische Fallen und wie man sie vermeidet
+**Karten**: 15
+
+---
+
+## Karte 1 | Kritisch
+**Q:** Warum liefert meine Berechnung manchmal unerwartet 0 oder leer?
+**A:** NULL-Propagation! Wenn IRGEND ein Wert in einem Ausdruck NULL ist, wird das GANZE Ergebnis NULL. Loesung: Immer vorher auf NULL pruefen mit `isnull(lVar)` oder `nullValuesWhenORtestedBecomeZero` setzen.
+
+---
+
+## Karte 2 | Kritisch
+**Q:** Ich rufe eine Methode auf, aber sie wird nicht gefunden. Warum?
+**A:** Die Methode ist wahrscheinlich auf der SUPERKLASSE definiert. Omnis-Klassen erben Methoden. Pruefe: `oSuperData`, `T_Super`, `wMainWindow`, `wBaseWindow`. Auch: Tippfehler im $-Methodennamen!
+
+---
+
+## Karte 3 | Kritisch
+**Q:** Mein `lList.spalte` gibt immer leer zurueck. Warum?
+**A:** Die Liste hat keine aktuelle Zeile gesetzt (`$line = 0`). Setze zuerst `$line`: `Do lList.$line.$assign(1)` oder nutze `For each line in list`.
+
+---
+
+## Karte 4 | Kritisch
+**Q:** `Do $cinst.$objs.feld.$contents = "Test"` funktioniert nicht. Warum?
+**A:** Man kann Notation-Properties NICHT mit `=` setzen! Immer `$assign()` verwenden: `Do $cinst.$objs.feld.$contents.$assign("Test")`. Das ist DER haeufigste Anfaengerfehler!
+
+---
+
+## Karte 5 | Wichtig
+**Q:** Mein Remote Form reagiert traege. Warum?
+**A:** Jeder `$event()` macht einen Server-Roundtrip! Loesung: Validierung und UI-Logik in `$eventclient()` (Browser-seitig) ausfuehren. Nur Datenbankzugriff im Server-Event.
+
+---
+
+## Karte 6 | Wichtig
+**Q:** Mein SQL mit `[lVar]` erzeugt seltsame Fehler bei Sonderzeichen. Warum?
+**A:** `[lVar]` macht direkte Text-Substitution - Apostroph im Namen -> SQL-Fehler oder SQL-Injection! Immer `@[lVar]` (Bind-Variable) oder `:1`-Parameter verwenden!
+
+---
+
+## Karte 7 | Wichtig
+**Q:** Meine Aenderungen an einer Variable werden in einem anderen Window nicht sichtbar. Warum?
+**A:** Variable-Scoping! `lVar` (lokal) lebt nur in der Methode. `iVar` (Instanz) nur im Objekt. Fuer fenster-uebergreifend: `tVar` (Task) verwenden oder Daten explizit uebergeben.
+
+---
+
+## Karte 8 | Wichtig
+**Q:** Ich habe eine Klasse geaendert, aber die Aenderung wirkt sich nicht aus. Warum?
+**A:** Moeglicherweise laeuft noch eine alte Instanz! Schliesse alle offenen Fenster/Instanzen und oeffne neu. Oder: Die Methode ist auf der Superklasse und du aenderst die falsche Klasse.
+
+---
+
+## Karte 9 | Mittel
+**Q:** Warum zaehlt `$linecount` von 1 und nicht von 0?
+**A:** Omnis-Listen sind 1-basiert (wie VB, anders als JS/Python). `$line = 1` ist die ERSTE Zeile. `$line = 0` bedeutet "keine Zeile gewaehlt".
+
+---
+
+## Karte 10 | Mittel
+**Q:** Mein `$search` findet nichts, obwohl der Wert existiert. Warum?
+**A:** String-Vergleich ist Case-Sensitive in Omnis! `"Harry" <> "harry"`. Loesung: `Do lList.$search(upp(lList.name) = upp(lSearchTerm))` oder `low()` verwenden.
+
+---
+
+## Karte 11 | Mittel
+**Q:** Was passiert wenn ich `Calculate lList as lOtherList` mache?
+**A:** Du bekommst eine KOPIE, keine Referenz! Aenderungen an `lList` aendern `lOtherList` NICHT. Das ist anders als bei Arrays in JavaScript!
+
+---
+
+## Karte 12 | Mittel
+**Q:** Mein `$sendall` wirft einen Fehler bei leeren Listen. Warum?
+**A:** `$sendall` auf eine leere Liste ist eigentlich ok, aber wenn der Ausdruck auf Spalten zugreift die nicht existieren, kommt ein Fehler. Immer vorher pruefen: `If lList.$linecount > 0`.
+
+---
+
+## Karte 13 | Fortgeschritten
+**Q:** Mein `Quit method` gibt nichts zurueck, obwohl ich einen Wert angebe. Warum?
+**A:** Der Aufrufer muss `Returns lVar` verwenden! Ohne `Returns` geht der Rueckgabewert verloren: `Do iObj.$method() Returns lResult` (richtig) vs `Do iObj.$method()` (Wert geht verloren).
+
+---
+
+## Karte 14 | Fortgeschritten
+**Q:** In Solution2: Warum sehe ich einen Datensatz mit `valid = false`?
+**A:** Soft-Delete! `valid = false` bedeutet "geloescht" aber noch in der DB. Immer `WHERE valid = true` in Abfragen verwenden. Solution2 nutzt `valid` konsequent statt physischem DELETE.
+
+---
+
+## Karte 15 | Fortgeschritten
+**Q:** Mein Report druckt leere Seiten. Warum?
+**A:** Die Report-Klasse hat keine Daten! Reports brauchen eine definierte Liste als Datenquelle (`$dataname`). Pruefe: Wurde die Liste vor dem Report-Aufruf korrekt befuellt?
+
+---
+
+## Spaced Repetition Tracking
+
+| Karte | Zuletzt | Naechste | Schwierigkeit |
+|-------|---------|----------|---------------|
+| (wird beim Ueben gefuellt) | | | |
diff --git a/edu/content/flashcards/deck-lists.md b/edu/content/flashcards/deck-lists.md
new file mode 100644
index 0000000..5cf8fb2
--- /dev/null
+++ b/edu/content/flashcards/deck-lists.md
@@ -0,0 +1,166 @@
+# Flashcard Deck: Listen & Rows
+
+**Thema**: Omnis Listen - der universelle Datencontainer
+**Schwierigkeit**: Anfaenger -> Fortgeschritten
+**Karten**: 20
+
+---
+
+## Karte 1 | Basis
+**Q:** Was ist eine "List" in Omnis?
+**A:** Ein zweidimensionaler Datencontainer mit benannten, typisierten Spalten und beliebig vielen Zeilen. Wie ein Array von Objects in JS, ein DataFrame in Python, oder ein RecordSet in VB.
+
+---
+
+## Karte 2 | Basis
+**Q:** Was ist eine "Row" in Omnis?
+**A:** Eine einzelne Datenzeile mit benannten Spalten. Wie ein Object/Dictionary. Eine List besteht aus vielen Rows.
+
+---
+
+## Karte 3 | Basis
+**Q:** Wie erstellst du eine Liste mit Spalten?
+**A:**
+```omnis
+Do lList.$cols.$add('id', kInteger)
+Do lList.$cols.$add('name', kCharacter, kSimplechar, 100)
+Do lList.$cols.$add('price', kNumber, k2dp, 10)
+```
+
+---
+
+## Karte 4 | Basis
+**Q:** Wie fuegst du eine Zeile zu einer Liste hinzu?
+**A:**
+```omnis
+Do lList.$add()
+Calculate lList.id as 1
+Calculate lList.name as "Produkt A"
+Calculate lList.price as 29.99
+```
+`$add()` fuegt eine Zeile hinzu UND macht sie zur aktuellen Zeile.
+
+---
+
+## Karte 5 | Basis
+**Q:** Wie iterierst du ueber alle Zeilen einer Liste?
+**A:**
+```omnis
+For each line in list from 1 to lList.$linecount step 1
+ # lList.spaltenname gibt den Wert der aktuellen Zeile
+ Calculate lTotal as lTotal + lList.price
+End For
+```
+
+---
+
+## Karte 6 | Mittel
+**Q:** Wie suchst du in einer Liste?
+**A:** `Do lList.$search(lList.name = "Produkt A")` - Setzt `$line` auf den Treffer. `If flag true` prueft ob gefunden.
+
+---
+
+## Karte 7 | Mittel
+**Q:** Wie sortierst du eine Liste?
+**A:** `Do lList.$sort(lList.name, kTrue)` - Sortiert nach Spalte "name". `kTrue` = aufsteigend, `kFalse` = absteigend.
+
+---
+
+## Karte 8 | Mittel
+**Q:** Wie entfernst du eine Zeile aus einer Liste?
+**A:** `Do lList.$remove(lList.$line)` entfernt die aktuelle Zeile. `Do lList.$remove(3)` entfernt Zeile 3. `Do lList.$clear()` leert alle Zeilen.
+
+---
+
+## Karte 9 | Mittel
+**Q:** Was ist `$linecount`?
+**A:** Die Anzahl der Zeilen in einer Liste. `Calculate lAnzahl as lList.$linecount`. Wie `array.length` in JS.
+
+---
+
+## Karte 10 | Mittel
+**Q:** Was ist `$line`?
+**A:** Die aktuelle Zeilennummer einer Liste (1-basiert!). Wird durch `$search`, `$add`, Klick auf Grid gesetzt. `0` = keine Zeile gewaehlt.
+
+---
+
+## Karte 11 | Mittel
+**Q:** Wie liest du eine bestimmte Zeile (nicht die aktuelle)?
+**A:** `Calculate lVal as lList.[3].name` liest Spalte "name" von Zeile 3. Die eckigen Klammern addressieren die Zeilennummer.
+
+---
+
+## Karte 12 | Fortgeschritten
+**Q:** Was macht `$sendall()`?
+**A:** Fuehrt einen Ausdruck fuer JEDE Zeile der Liste aus (wie `forEach` + `map` in einem):
+```omnis
+Do lList.$sendall($ref.price.$assign($ref.price * 1.19))
+```
+`$ref` = aktuelle Zeile.
+
+---
+
+## Karte 13 | Fortgeschritten
+**Q:** Wie filterst du eine Liste?
+**A:** `$search` mit `kTrue` im 2. Parameter zeigt nur Treffer:
+```omnis
+Do lList.$search(lList.status = 'active', kTrue)
+```
+Nicht-Treffer werden "ausgeblendet" (nicht geloescht). `Do lList.$search(kTrue)` zeigt wieder alle.
+
+---
+
+## Karte 14 | Fortgeschritten
+**Q:** Was ist der Unterschied zwischen `$merge()` und `$add()`?
+**A:** `$add()` fuegt eine leere Zeile hinzu. `$merge(lOtherList)` fuegt ALLE Zeilen einer anderen Liste hinzu - wie `array.concat()` in JS.
+
+---
+
+## Karte 15 | Fortgeschritten
+**Q:** Wie definierst du eine Liste aus einer Schema-Klasse?
+**A:** `Do lList.$definefromsqlclass('scMySchema')` - Uebernimmt die Spaltenstruktur aus dem Schema. Wie ein Typ/Interface fuer die Liste.
+
+---
+
+## Karte 16 | Fortgeschritten
+**Q:** Was ist `$colcount`?
+**A:** Die Anzahl der Spalten einer Liste. `Calculate lCols as lList.$colcount`. Fuer dynamische Listen nuetzlich.
+
+---
+
+## Karte 17 | Fortgeschritten
+**Q:** Wie kopierst du eine Liste?
+**A:** `Calculate lCopy as lOriginal` erstellt eine **Kopie** (nicht Referenz!). Omnis-Listen sind Value-Types, nicht Reference-Types wie in JS!
+
+---
+
+## Karte 18 | Fortgeschritten
+**Q:** Wie befuellst du eine Liste aus SQL?
+**A:**
+```omnis
+Do lStmt.$execdirect('SELECT * FROM orders') Returns #F
+If flag true
+ Do lStmt.$fetch(lList, kFetchAll) Returns #F
+End If
+```
+`kFetchAll` holt alle Zeilen auf einmal.
+
+---
+
+## Karte 19 | Fortgeschritten
+**Q:** Was ist `$selected` bei einer Liste?
+**A:** Ein Boolean-Flag pro Zeile, das anzeigt ob die Zeile "ausgewaehlt" ist (z.B. per Checkbox im Grid). `lList.$selected` liest/setzt den Wert der aktuellen Zeile.
+
+---
+
+## Karte 20 | Fortgeschritten
+**Q:** Was ist der Unterschied zwischen Listen und Rows beim SQL-Fetch?
+**A:** `$fetch(lList, kFetchAll)` holt mehrere Zeilen in eine Liste. `$fetch(lRow, kFetchOne)` holt nur eine Zeile in eine Row. Row = einzelner Datensatz, Liste = Ergebnismenge.
+
+---
+
+## Spaced Repetition Tracking
+
+| Karte | Zuletzt | Naechste | Schwierigkeit |
+|-------|---------|----------|---------------|
+| (wird beim Ueben gefuellt) | | | |
diff --git a/edu/content/flashcards/deck-notation.md b/edu/content/flashcards/deck-notation.md
new file mode 100644
index 0000000..59420e0
--- /dev/null
+++ b/edu/content/flashcards/deck-notation.md
@@ -0,0 +1,193 @@
+# Flashcard Deck: $ Notation
+
+**Thema**: Das Omnis $-Notationssystem
+**Schwierigkeit**: Anfaenger -> Fortgeschritten
+**Karten**: 30
+
+---
+
+## Karte 1 | Basis
+**Q:** Was bedeutet `$cinst` in Omnis?
+**A:** Die aktuelle Instanz - aequivalent zu `this` in JavaScript oder `self` in Python. Zeigt auf das aktuelle Window/Objekt.
+
+---
+
+## Karte 2 | Basis
+**Q:** Wie liest du den Inhalt eines Feldes "customerName" auf dem aktuellen Window?
+**A:** `Calculate lVal as $cinst.$objs.customerName.$contents`
+
+---
+
+## Karte 3 | Basis
+**Q:** Wie SETZT du den Inhalt eines Feldes? Warum geht `= ` nicht?
+**A:** `Do $cinst.$objs.customerName.$contents.$assign("Meier")` - Man muss `$assign()` verwenden, weil Notation-Pfade Objekte sind, keine einfachen Variablen.
+
+---
+
+## Karte 4 | Basis
+**Q:** Was ist der Unterschied zwischen `$objs` und `$bobjs`?
+**A:** `$objs` = Objekte auf dem aktuellen Fenster/Form. `$bobjs` = "Background objects" / Objekte auf einer Superklasse (geerbte Objekte).
+
+---
+
+## Karte 5 | Basis
+**Q:** Was gibt `$clib` zurueck?
+**A:** Die aktuelle Library (das Omnis-Projekt / .lbs Datei), in der der Code laeuft.
+
+---
+
+## Karte 6 | Basis
+**Q:** Wie greifst du auf eine andere Klasse in der gleichen Library zu?
+**A:** `Do $clib.$classes.ClassName.$open() Returns lRef`
+
+---
+
+## Karte 7 | Mittel
+**Q:** Was bedeutet `$ctask`?
+**A:** Der aktuelle Task - eine Art globaler Kontext/Session. Task-Variablen (`tVar`) leben hier.
+
+---
+
+## Karte 8 | Mittel
+**Q:** Wie machst du ein Feld unsichtbar?
+**A:** `Do $cinst.$objs.fieldName.$visible.$assign(kFalse)`
+
+---
+
+## Karte 9 | Mittel
+**Q:** Wie rufst du eine Methode auf einem anderen Objekt auf?
+**A:** `Do iMyObject.$methodName(param1, param2) Returns lResult`
+
+---
+
+## Karte 10 | Mittel
+**Q:** Was ist `$cols` bei einer Liste?
+**A:** Die Spalten-Sammlung einer Liste. `lList.$cols.$add('name', kCharacter, kSimplechar, 255)` fuegt eine Spalte hinzu.
+
+---
+
+## Karte 11 | Mittel
+**Q:** Wie zaehlt man die Zeilen einer Liste?
+**A:** `Calculate lCount as lList.$linecount`
+
+---
+
+## Karte 12 | Mittel
+**Q:** Was macht `$line`?
+**A:** Gibt die aktuelle Zeilennummer einer Liste zurueck oder setzt sie. `Do lList.$line.$assign(5)` springt zu Zeile 5.
+
+---
+
+## Karte 13 | Mittel
+**Q:** Wie greifst du auf den Wert einer Spalte "price" in der aktuellen Zeile einer Liste zu?
+**A:** `Calculate lPrice as lList.price` (wenn `$line` gesetzt ist) oder `Calculate lPrice as lList.[lRow].price` fuer eine bestimmte Zeile.
+
+---
+
+## Karte 14 | Fortgeschritten
+**Q:** Was ist `$sendall()`?
+**A:** Fuehrt einen Befehl fuer ALLE Zeilen einer Liste aus - wie `forEach` in JS. Beispiel: `Do lList.$sendall($ref.price.$assign($ref.price * 1.19))` (19% MwSt auf alle Preise)
+
+---
+
+## Karte 15 | Fortgeschritten
+**Q:** Was bedeutet `$ref` in `$sendall`?
+**A:** `$ref` ist die Referenz auf die aktuelle Zeile innerhalb von `$sendall` - wie der Callback-Parameter in `Array.forEach(item => ...)`.
+
+---
+
+## Karte 16 | Fortgeschritten
+**Q:** Wie findest du eine Zeile in einer Liste per Suche?
+**A:** `Do lList.$search(lList.id = lSearchID)` - Setzt `$line` auf die gefundene Zeile. `#F` (Flag) zeigt Erfolg.
+
+---
+
+## Karte 17 | Fortgeschritten
+**Q:** Was ist `$getAllCols()`?
+**A:** Laedt alle Spalten einer Datenbankabfrage in die Liste, ohne sie einzeln zu benennen.
+
+---
+
+## Karte 18 | Basis
+**Q:** Was macht `kTrue` vs `kFalse`?
+**A:** Omnis-Konstanten fuer Boolean true/false. Immer mit `k`-Praefix (k = Konstante).
+
+---
+
+## Karte 19 | Mittel
+**Q:** Wie oeffnest du ein Window (Fenster)?
+**A:** `Do $clib.$windows.wMyWindow.$open('*') Returns lWinRef`
+
+---
+
+## Karte 20 | Mittel
+**Q:** Was bedeutet der `'*'` Parameter bei `$open`?
+**A:** Oeffnet das Fenster als neue Instanz. Ohne `'*'` wird eine bestehende Instanz wiederverwendet.
+
+---
+
+## Karte 21 | Fortgeschritten
+**Q:** Wie iterierst du ueber alle Objekte auf einem Window?
+**A:** `For each $cinst.$objs as lObj` - wie `for (const el of document.querySelectorAll('*'))` in JS.
+
+---
+
+## Karte 22 | Fortgeschritten
+**Q:** Was ist der Unterschied zwischen `$cclass` und `$cinst`?
+**A:** `$cclass` = die Klasse (Bauplan/Template), `$cinst` = die konkrete Instanz (laufendes Objekt). Wie `MyClass` vs `myInstance` in JS.
+
+---
+
+## Karte 23 | Basis
+**Q:** Wie gibt eine Methode einen Wert zurueck?
+**A:** `Quit method lReturnValue` - aequivalent zu `return lReturnValue` in JS.
+
+---
+
+## Karte 24 | Mittel
+**Q:** Was ist `$event`?
+**A:** Die Standard-Event-Handler-Methode einer Klasse. Wird bei UI-Events aufgerufen (wie `onClick`). Parameter `pEventCode` enthaelt den Event-Typ.
+
+---
+
+## Karte 25 | Fortgeschritten
+**Q:** Unterschied `$event` vs `$eventclient` bei Remote Forms?
+**A:** `$event` = laeuft auf dem SERVER. `$eventclient` = laeuft im BROWSER (JavaScript). Fuer schnelle UI-Reaktionen `$eventclient` verwenden.
+
+---
+
+## Karte 26 | Fortgeschritten
+**Q:** Wie greifst du von einer Instanz auf die Instanz-Variable eines anderen offenen Windows zu?
+**A:** `Calculate lVal as $iwindows.wOtherWindow.$ivar.iMyVariable`
+
+---
+
+## Karte 27 | Mittel
+**Q:** Was macht `$add()` bei einer Liste?
+**A:** Fuegt eine neue leere Zeile am Ende hinzu. `Do lList.$add()`. Danach Werte setzen mit `Calculate lList.colName as "wert"`.
+
+---
+
+## Karte 28 | Mittel
+**Q:** Wie loeschst du eine Zeile aus einer Liste?
+**A:** `Do lList.$remove(lList.$line)` - entfernt die aktuelle Zeile. Oder `Do lList.$remove(5)` fuer Zeile 5.
+
+---
+
+## Karte 29 | Fortgeschritten
+**Q:** Was ist `$define()` bei einem Schema/Query?
+**A:** Definiert die Spaltenstruktur. `Do lSchema.$define(col1, col2, ...)` - wie ein CREATE TABLE fuer eine Omnis-Datenstruktur.
+
+---
+
+## Karte 30 | Fortgeschritten
+**Q:** Wie navigierst du durch die gesamte Omnis-Objekthierarchie?
+**A:** Von oben: `$root` -> `$libs` -> Library -> `$classes` -> Class -> `$methods` / `$objs` / `$ivars`. Alles ist ein Baum mit `$` Pfaden!
+
+---
+
+## Spaced Repetition Tracking
+
+| Karte | Zuletzt | Naechste | Schwierigkeit |
+|-------|---------|----------|---------------|
+| (wird beim Ueben gefuellt) | | | |
diff --git a/edu/content/flashcards/deck-solution2.md b/edu/content/flashcards/deck-solution2.md
new file mode 100644
index 0000000..4631208
--- /dev/null
+++ b/edu/content/flashcards/deck-solution2.md
@@ -0,0 +1,132 @@
+# Flashcard Deck: Solution2 Architektur & Konventionen
+
+**Thema**: Solution2 ERP-System kennenlernen
+**Karten**: 20
+
+---
+
+## Karte 1 | Basis
+**Q:** Was ist Solution2?
+**A:** Ein vollstaendiges deutsches ERP-System (Enterprise Resource Planning) gebaut auf Omnis Studio. Abdeckung: Verkauf, Einkauf, Fertigung, Lager, Qualitaet, Buchhaltung, HR, Mobile.
+
+---
+
+## Karte 2 | Basis
+**Q:** Welche Datenbank nutzt Solution2?
+**A:** PostgreSQL. Datenbank `masterdemo`, Schema `soluser`. Verbindung ueber den PostgreSQL-DAM (`PGSQLDAM`).
+
+---
+
+## Karte 3 | Basis
+**Q:** Was bedeutet das Praefix `w` bei einer Solution2-Klasse?
+**A:** Window-Klasse = UI-Formular. z.B. `wOrders` = das Auftragsformular, `wLogin` = der Login-Dialog.
+
+---
+
+## Karte 4 | Basis
+**Q:** Was bedeutet das Praefix `o`?
+**A:** Object-Klasse = Business-Logik. z.B. `oCreateInvoice` = Logik fuer Rechnungserstellung, `oLogin` = Login-Validierung.
+
+---
+
+## Karte 5 | Basis
+**Q:** Was bedeutet `T_` als Praefix?
+**A:** Table-Klasse = Daten-Cursor/Datenliste. z.B. `T_Orders` = Auftragsliste, `T_Super` = Basis-Table-Klasse fuer alle.
+
+---
+
+## Karte 6 | Basis
+**Q:** Welche 4 Audit-Spalten hat JEDE Solution2-Tabelle?
+**A:** `created` (TIMESTAMP), `modtime` (TIMESTAMP), `revisor` (INTEGER = Employee-ID), `valid` (BOOLEAN = Soft-Delete). Immer pruefen und setzen!
+
+---
+
+## Karte 7 | Mittel
+**Q:** Was ist das Dual-Signature-Pattern?
+**A:** Viele Geschaeftsdokumente benoetigen Vier-Augen-Prinzip: `firstsigned` (1. Unterschrift), `needsecondsignature` (braucht 2.?), `secondrevisor` (wer prueft), `secondsigned` (2. Unterschrift erteilt).
+
+---
+
+## Karte 8 | Mittel
+**Q:** Was ist `oSuperData`?
+**A:** Die Basis-Superklasse fuer fast alle Business-Logik-Objekte (`o`-Klassen). Enthaelt gemeinsame Methoden fuer Datenzugriff, Validierung, Navigation.
+
+---
+
+## Karte 9 | Mittel
+**Q:** Wie funktioniert das RBAC (Berechtigungssystem) in Solution2?
+**A:** Form-Path-basiert! Berechtigungsgruppen werden Navigation-Pfaden zugewiesen. `T_ModuleAccess2Methods` steuert den Zugriff auf Modul-Methoden. Nicht per Screen, sondern per Modul.
+
+---
+
+## Karte 10 | Mittel
+**Q:** Welche Library enthaelt den Login und die Navigation?
+**A:** `solution2` (die Haupt-Library). Klassen: `wLogin`, `oLogin`, `oLogon`, `oNavigate`, `wEnterprise`.
+
+---
+
+## Karte 11 | Mittel
+**Q:** Was macht die `solution2Services`-Library?
+**A:** Hintergrund-/Service-Layer: Push-Notifications, Warehouse-Success-Handler, Order-Callbacks, Container-Management, Zugriffskontrolle.
+
+---
+
+## Karte 12 | Mittel
+**Q:** Wie heisst der Auftrags-zu-Rechnung-Prozess?
+**A:** Orders -> Lieferschein (deliverynoteitems) -> Rechnungsfreigabe (customerinvoicerequirement mit invoicingallowed=true) -> Rechnung (customerinvoices) -> Buchhaltungsexport.
+
+---
+
+## Karte 13 | Fortgeschritten
+**Q:** Was ist die `MobileWork`-Library?
+**A:** Der Web/Mobile-Client von Solution2, gebaut mit Omnis JS Remote Forms. Klassen: `jsMainForm`, `jsMainFrame`, `jsMainMenu`. Laeuft im Browser.
+
+---
+
+## Karte 14 | Fortgeschritten
+**Q:** Was sind die wichtigsten Tabellen im Verkauf?
+**A:** `orders` (Auftraege, 103 Spalten!), `orderitems` (Positionen), `deliverynoteitems` (Lieferschein), `customerinvoices` (Rechnungen, 90 Spalten), `customerinvoiceitems` (Rechnungspositionen).
+
+---
+
+## Karte 15 | Fortgeschritten
+**Q:** Was ist ZUGFeRD/Factur-X in Solution2?
+**A:** E-Rechnungsformate. Solution2 erzeugt und verarbeitet elektronische Rechnungen ueber `oOmnis2Zugferd` und `oXML_eInvoice` in der AdminAccounting-Library. Pflicht in EU seit 2025!
+
+---
+
+## Karte 16 | Fortgeschritten
+**Q:** Was ist die `BasicTools`-Library?
+**A:** Shared Utilities: HTML-Generierung (`cHTML`), Datei-Import (`oImportFile`), Edit-Utilities (`oEdit`), Fehleranzeige.
+
+---
+
+## Karte 17 | Mittel
+**Q:** In welcher Library ist der Einkauf?
+**A:** `Purchase` - Bestellungen, Lieferanten, Wareneingaenge, E-Rechnungen. Klassen: `wSupplierInvoices`, `wPurchaseOrderStockReceipt`, `T_SupplierInvoices`.
+
+---
+
+## Karte 18 | Mittel
+**Q:** Wie heissen die Klassen fuer die Fertigung?
+**A:** In der `Manufacturing`-Library: `oPartList` (Stueckliste), `rCalculateProductCosts` (Kalkulation), `scResourceSpecification` (Ressourcen).
+
+---
+
+## Karte 19 | Fortgeschritten
+**Q:** Was ist `T_SuperVersion`?
+**A:** Erweiterte Basis-Table-Klasse mit Versionierung. Fuer Tabellen die eine Aenderungshistorie benoetigen.
+
+---
+
+## Karte 20 | Fortgeschritten
+**Q:** Wie werden Custom Fields (Zusatzfelder) in Solution2 umgesetzt?
+**A:** Ueber die Tabellen `additionalfields` (Felddefinition: Typ, Zuordnung, Druck-Flags) und `additionalfieldsgroups` (Gruppierung). Koennen an verschiedene Entitaeten gehaengt werden (Auftraege, Teile, Fertigungsschritte...).
+
+---
+
+## Spaced Repetition Tracking
+
+| Karte | Zuletzt | Naechste | Schwierigkeit |
+|-------|---------|----------|---------------|
+| (wird beim Ueben gefuellt) | | | |
diff --git a/edu/content/flashcards/deck-sql.md b/edu/content/flashcards/deck-sql.md
new file mode 100644
index 0000000..aa2489b
--- /dev/null
+++ b/edu/content/flashcards/deck-sql.md
@@ -0,0 +1,136 @@
+# Flashcard Deck: SQL & Datenbankzugriff in Omnis
+
+**Thema**: SQL-Zugriff ueber DAMs (Database Access Modules)
+**Karten**: 15
+
+---
+
+## Karte 1 | Basis
+**Q:** Wie verbindet sich Omnis mit einer Datenbank?
+**A:** Ueber Session-Objekte und DAMs (Database Access Modules). Erst Session erstellen, dann `$logon()`:
+```omnis
+Do lSess.$logon('host', 'user', 'pass', 'sessName') Returns #F
+```
+
+---
+
+## Karte 2 | Basis
+**Q:** Was ist der Unterschied zwischen `$prepare/$execute` und `$execdirect`?
+**A:** `$prepare`+`$execute` = vorbereitete Abfrage (parameterisiert, wiederverwendbar, sicher). `$execdirect` = direkte Ausfuehrung (einmalig). Fuer SELECT immer `$prepare/$execute` bevorzugen!
+
+---
+
+## Karte 3 | Basis
+**Q:** Wie holst du Daten aus einer SQL-Abfrage in eine Omnis-Liste?
+**A:**
+```omnis
+Do lStmt.$prepare('SELECT * FROM contact WHERE valid = :1') Returns #F
+Do lStmt.$execute(kTrue) Returns #F
+Do lStmt.$fetch(lResultList, kFetchAll) Returns #F
+```
+
+---
+
+## Karte 4 | Basis
+**Q:** Was bedeutet `:1`, `:2` etc. in SQL-Statements?
+**A:** Bind-Variablen (Parameter). `:1` = erster Parameter, `:2` = zweiter etc. Werden bei `$execute` der Reihe nach uebergeben. **SICHER** gegen SQL-Injection!
+
+---
+
+## Karte 5 | Mittel
+**Q:** Was ist der Unterschied zwischen `@[lVar]` und `[lVar]` in Omnis-SQL?
+**A:** `@[lVar]` = Bind-Variable (sicher, parametrisiert). `[lVar]` = direkte Text-Substitution (UNSICHER, SQL-Injection moeglich!). **Immer `@[lVar]` verwenden!**
+
+---
+
+## Karte 6 | Mittel
+**Q:** Was macht `kFetchAll` vs `kFetchOne`?
+**A:** `kFetchAll` holt ALLE Ergebniszeilen in eine Liste. `kFetchOne` holt nur EINE Zeile (in eine Row oder die naechste Zeile einer Liste). Fuer grosse Ergebnismengen: `kFetchOne` in Schleife fuer bessere Speicher-Kontrolle.
+
+---
+
+## Karte 7 | Mittel
+**Q:** Wie fuehrst du ein INSERT aus?
+**A:**
+```omnis
+Do lStmt.$execdirect(con('INSERT INTO mytable (name, created) VALUES (@[lName], @[#D])')) Returns #F
+```
+`#D` = aktuelles Datum. Oder mit prepare: `$prepare('INSERT INTO mytable (name) VALUES (:1)')` -> `$execute(lName)`
+
+---
+
+## Karte 8 | Mittel
+**Q:** Wie machst du ein UPDATE?
+**A:**
+```omnis
+Do lStmt.$prepare('UPDATE orders SET status = :1, modtime = :2 WHERE orderid = :3') Returns #F
+Do lStmt.$execute('completed', #T, lOrderID) Returns #F
+```
+`#T` = aktueller Timestamp.
+
+---
+
+## Karte 9 | Mittel
+**Q:** Wie pruefst du ob eine SQL-Operation erfolgreich war?
+**A:** Jede SQL-Methode setzt `#F` (den Flag). `Returns #F` faengt den Wert auf:
+```omnis
+Do lStmt.$execute(lID) Returns #F
+If flag true
+ # Erfolg
+Else
+ # Fehler - lSess.$lasterror fuer Details
+End If
+```
+
+---
+
+## Karte 10 | Fortgeschritten
+**Q:** Was ist `$lasterror` und `$lasterrortext`?
+**A:** Session-Properties die den letzten SQL-Fehler enthalten. `lSess.$lasterror` = Fehlercode, `lSess.$lasterrortext` = Fehlerbeschreibung. Immer bei `#F = false` pruefen!
+
+---
+
+## Karte 11 | Fortgeschritten
+**Q:** Wie machst du eine Transaktion in Omnis?
+**A:**
+```omnis
+Do lSess.$begin() Returns #F ;; BEGIN TRANSACTION
+# ... SQL Operationen ...
+If lAllesOK
+ Do lSess.$commit() Returns #F ;; COMMIT
+Else
+ Do lSess.$rollback() Returns #F ;; ROLLBACK
+End If
+```
+
+---
+
+## Karte 12 | Fortgeschritten
+**Q:** Was ist ein "DAM" in Omnis?
+**A:** Database Access Module - ein Treiber fuer verschiedene DB-Systeme. Wichtige DAMs: `PGSQLDAM` (PostgreSQL), `OABORDAM` (Oracle), `MYSQLDAM` (MySQL), `ODBCDAM` (ODBC). Solution2 nutzt `PGSQLDAM`.
+
+---
+
+## Karte 13 | Fortgeschritten
+**Q:** Wie definierst du die Spalten einer Liste aus einem SQL-Ergebnis automatisch?
+**A:** Entweder `$getAllCols()` nach dem Fetch, oder `$definefromsqlclass('scMySchema')` vor dem Fetch. Automatisch: Omnis erkennt die Spalten beim Fetch von selbst, wenn die Liste leer ist.
+
+---
+
+## Karte 14 | Fortgeschritten
+**Q:** Was ist der Unterschied zwischen Server-Side SQL und Client-Side SQL in Remote Forms?
+**A:** Server-Side: SQL laeuft auf dem Omnis-Server (normal). Client-Side: Geht NICHT - der JS-Client im Browser hat keinen DB-Zugriff. Alle Daten muessen ueber Server-Methoden geladen werden.
+
+---
+
+## Karte 15 | Fortgeschritten
+**Q:** Wie wird in Solution2 die DB-Verbindung verwaltet?
+**A:** Ueber die `DBAccess`-Library. Sie verwaltet Sessions, Berechtigungen und Connection-Pooling. Die Session wird beim Login erstellt (`oLogin`/`oLogon`) und ueber `ioDBAccessStartup` im Navigator initialisiert.
+
+---
+
+## Spaced Repetition Tracking
+
+| Karte | Zuletzt | Naechste | Schwierigkeit |
+|-------|---------|----------|---------------|
+| (wird beim Ueben gefuellt) | | | |
diff --git a/edu/content/flashcards/deck-windows.md b/edu/content/flashcards/deck-windows.md
new file mode 100644
index 0000000..b09758f
--- /dev/null
+++ b/edu/content/flashcards/deck-windows.md
@@ -0,0 +1,170 @@
+# Flashcard Deck: Fensterprogrammierung & SubWindows
+
+**Thema**: Window-Hierarchie, SubWindow-Muster, Command-Delegation in Solution2
+**Karten**: 25
+
+---
+
+## Karte 1 | Basis
+**Q:** Welche 3 Fenster-Superklassen hat Solution2?
+**A:** `wBaseWindow` (Wurzel), `wMainWindow` (Hauptfenster, erbt von wBaseWindow), `wSubWindow` (eingebettete Panels, erbt auch von wBaseWindow). Alle in `solution2/SuperClasses/`.
+
+---
+
+## Karte 2 | Basis
+**Q:** Was ist `wBaseWindow`?
+**A:** Die Wurzel-Superklasse ALLER Solution2-Fenster (hat KEINE eigene Superklasse). Definiert gemeinsame Infrastruktur: `ioCode`, `iiRef2SubWindow`, `ibControl2SubWindow`, `ilList`, `irRow`, `inEDMode`, `icCurrTabPane`.
+
+---
+
+## Karte 3 | Basis
+**Q:** Was ist `wMainWindow`?
+**A:** Superklasse fuer eigenstaendige Dateneingabe-Fenster (z.B. wOrders, wParts). Erbt von `wBaseWindow`. Fuegt hinzu: `irRowCount`, `irRowMax`, `irStatusBarPane`. Ruft bei Commands immer `$closeRelatedWindow()` auf.
+
+---
+
+## Karte 4 | Basis
+**Q:** Was ist `wSubWindow`?
+**A:** Superklasse fuer eingebettete Fenster-Panels (in Tabs/Paged Panes). Erbt von `wBaseWindow`. Fuegt hinzu: `iiRef2Parent` (Rueck-Referenz), `icFormWindow` (Rechte), `icSubWindow`, `iiRef2Window`. Ermoeglicht rekursive Verschachtelung.
+
+---
+
+## Karte 5 | Kritisch
+**Q:** Was macht `ibControl2SubWindow`?
+**A:** DER zentrale Schalter! Wenn `kTrue`: Commands gehen an `iiRef2SubWindow`. Wenn `kFalse`: Commands gehen an `ioCode` (Business-Logik). Wird von `$setControl2SubWindow()` gesetzt/zurueckgesetzt.
+
+---
+
+## Karte 6 | Kritisch
+**Q:** Was passiert in `wSubWindow.$construct`?
+**A:** 1) `Do inherited` (wBaseWindow-Init), 2) `Set reference iiRef2SubWindow` auf eigenes SubWindow-Feld, 3) `Set reference iiRef2Parent` auf pRef2Parent, 4) `$translateObjects`, 5) `icFormWindow` merken, 6) **`iiRef2Parent.$setControl2SubWindow(kTrue)`** = Kontrolle uebernehmen!, 7+8) Row/List-Namen setzen.
+
+---
+
+## Karte 7 | Kritisch
+**Q:** Was passiert in `wSubWindow.$destruct`?
+**A:** 1) `iiRef2Parent.$setControl2SubWindow(kFalse)` = Kontrolle ZURUECKGEBEN!, 2) `ilList.$define()` = Liste leeren, 3) `irRow.$define()` = Row leeren, 4) `irRowSearch.$define()` = Such-Row leeren. **Ohne diesen Code denkt der Parent das SubWindow existiert noch!**
+
+---
+
+## Karte 8 | Kritisch
+**Q:** Wie sieht das Command-Delegation-Pattern aus?
+**A:** Auf JEDEM Fenster identisch:
+```omnis
+If $cinst.ibControl2SubWindow
+ Do iiRef2SubWindow.$cXxx() ## -> SubWindow
+Else
+ Do $cinst.ioCode.$cXxx() ## -> Business-Logik
+End If
+```
+Gilt fuer: $cEdit, $cInsert, $cSave, $cCancel, $cDelete, $cPrint, $cAudit, $cNextVersion.
+
+---
+
+## Karte 9 | Wichtig
+**Q:** Was ist die bidirektionale Referenz-Kette?
+**A:** Jedes SubWindow hat `iiRef2Parent` (zeigt auf den Elter) UND der Elter hat `iiRef2SubWindow` (zeigt auf das Kind). So koennen Commands abwaerts und TabPane-Sperren aufwaerts fliessen.
+
+---
+
+## Karte 10 | Wichtig
+**Q:** Wie fliesst ein Edit-Command durch die Hierarchie?
+**A:** `wMainWindow.$cEdit` -> prueft `ibControl2SubWindow=kTrue` -> `wSubWindow.$cEdit` (Ebene 1) -> prueft `ibControl2SubWindow` -> entweder an Sub-SubWindow ODER an `ioCode.$cEdit()`. Der Command "sinkt" bis zur tiefsten aktiven Ebene.
+
+---
+
+## Karte 11 | Wichtig
+**Q:** Was macht `$closeRelatedWindow()` und wann wird es aufgerufen?
+**A:** Schliesst alle zugehoerigen Popup-Fenster. Wird auf `wMainWindow` IMMER VOR der Command-Delegation aufgerufen. Auf `wSubWindow` wird es NICHT aufgerufen - der Unterschied zwischen Main und Sub!
+
+---
+
+## Karte 12 | Wichtig
+**Q:** Wie funktioniert `$EnableTabPane`?
+**A:** SubWindow ruft `iiRef2Parent.$EnableTabPane($cinst.inEDMode)` auf -> Parent ruft `$cwind.$objs.TP.$enableTabPanes(pEDMode)` auf -> Tab-Reiter werden gesperrt/freigegeben. Die Sperre fliesst AUFWAERTS in der Hierarchie!
+
+---
+
+## Karte 13 | Wichtig
+**Q:** Wie heisst das TabPane-Objekt in Solution2 standardmaessig?
+**A:** `TP` - das ist Konvention! Zugriff ueber `$cwind.$objs.TP.$enableTabPanes(pEDMode)`.
+
+---
+
+## Karte 14 | Wichtig
+**Q:** Wie schliesst sich ein SubWindow?
+**A:** Es schliesst sich NICHT selbst! `wSubWindow.$closeWindow` delegiert an: `Do iiRef2Parent.$closeWindow()`. Der Parent entscheidet ueber das Schliessen des gesamten Fensters.
+
+---
+
+## Karte 15 | Mittel
+**Q:** Was ist `ioCode`?
+**A:** Das Business-Logik-Objekt (z.B. `oOrders`, `oOrderItems`). Es ist der "letzte Stopp" fuer Commands wenn kein aktives SubWindow existiert. Wird als Instanzvariable im Fenster definiert und enthaelt Methoden wie `$cEdit()`, `$cSave()`, `$cDelete()`.
+
+---
+
+## Karte 16 | Mittel
+**Q:** Was ist `icFormWindow`?
+**A:** Der Name des Formulars, der im `$construct` aus `pFormWiindow` (ja, mit Tippfehler im Original!) gesetzt wird. Wird fuer Pfad- und Rechtebestimmung (RBAC) verwendet.
+
+---
+
+## Karte 17 | Mittel
+**Q:** Wie weit kann die SubWindow-Verschachtelung gehen?
+**A:** Theoretisch unbegrenzt (rekursiv). Jedes SubWindow kann ein eigenes SubWindow-Feld haben. Praktisches Beispiel: `wOrders` (Main) -> `wOrder` (Sub, Details) -> `wOrderItems` (SubSub, Positionen). Typisch: 2-3 Ebenen.
+
+---
+
+## Karte 18 | Mittel
+**Q:** Was macht `$setControl2SubWindow(pBool)`?
+**A:** Setzt `ibControl2SubWindow` auf den uebergebenen Wert. Wird von SubWindows aufgerufen: `kTrue` in `$construct` (Kontrolle uebernehmen), `kFalse` in `$destruct` (Kontrolle zurueckgeben).
+
+---
+
+## Karte 19 | Mittel
+**Q:** Was macht `wOrder.$SubWindowSavesAttachments`?
+**A:** Prueft welches SubWindow aktiv ist (`iiRef2SubWindow.$classname`) und ruft je nach Typ verschiedene Save-Methoden auf. Zeigt: Man kann per `$classname` auf dem SubWindow verschiedene Aktionen ausloesen.
+
+---
+
+## Karte 20 | Mittel
+**Q:** Was ist der Unterschied bei Cross-Library-Vererbung?
+**A:** Innerhalb der `solution2`-Library: `wSubWindow` reicht. Aus anderen Libraries (Sales, Manufacturing, etc.): `solution2.wSubWindow` mit Library-Prefix noetig! Beispiel: `wOrder extends solution2.wSubWindow`.
+
+---
+
+## Karte 21 | Fortgeschritten
+**Q:** Was passiert wenn ein SubWindow die Kontrolle ABLEHNT?
+**A:** Es ruft sofort `$setControl2SubWindow(kFalse)` im `$construct` auf. Beispiel: `wWebPortal.$construct` macht `Do pRef2Parent.$setControl2SubWindow(kFalse)`. Das Fenster ist dann nur ein Anzeige-Panel ohne Command-Routing.
+
+---
+
+## Karte 22 | Fortgeschritten
+**Q:** Was macht `wOrderItems.$CallParentDoCommand`?
+**A:** Delegiert einen Command AUFWAERTS: `Do iiRef2Parent.$CallDoCommand`. Damit kann ein SubWindow seinen Parent bitten, einen bestimmten Command auszufuehren - die umgekehrte Richtung vom normalen Fluss!
+
+---
+
+## Karte 23 | Fortgeschritten
+**Q:** Was passiert wenn $destruct das `$setControl2SubWindow(kFalse)` vergisst?
+**A:** Das Elternfenster denkt, das SubWindow existiert noch (`ibControl2SubWindow = kTrue`). Alle Commands werden an eine tote Referenz geschickt -> "Method not found"-Fehler. Der haefigste SubWindow-Bug!
+
+---
+
+## Karte 24 | Fortgeschritten
+**Q:** Wie debuggt man Command-Routing-Probleme?
+**A:** 1) `Calculate lDebug as $cinst.ibControl2SubWindow` pruefen, 2) `iiRef2SubWindow` auf NULL pruefen, 3) Breakpoint in `$cEdit`/`$cCancel` des SubWindows setzen, 4) Pruefen ob `$construct` mit `Do inherited` aufgerufen wird, 5) Pruefen ob Superklasse korrekt ist (`wSubWindow` vs `wBaseWindow`).
+
+---
+
+## Karte 25 | Fortgeschritten
+**Q:** Was sind die 7 goldenen Regeln der Fensterprogrammierung?
+**A:** 1) `ibControl2SubWindow` bestimmt ALLES, 2) $construct baut die Kette auf (`inherited`!), 3) $destruct baut sie ab (Kontrolle zurueckgeben!), 4) Commands fliessen ABWAERTS, 5) TabPane-Sperren fliessen AUFWAERTS, 6) $closeWindow delegiert AUFWAERTS, 7) `ioCode` ist der letzte Stopp.
+
+---
+
+## Spaced Repetition Tracking
+
+| Karte | Zuletzt | Naechste | Schwierigkeit |
+|-------|---------|----------|---------------|
+| (wird beim Ueben gefuellt) | | | |
diff --git a/edu/content/tutorials/01-omnis-kickstart.md b/edu/content/tutorials/01-omnis-kickstart.md
new file mode 100644
index 0000000..0d5e4e3
--- /dev/null
+++ b/edu/content/tutorials/01-omnis-kickstart.md
@@ -0,0 +1,289 @@
+# Tutorial 1: Omnis Kickstart - Von Null zum ersten Verstaendnis
+
+**Dauer**: ca. 30 Minuten
+**Voraussetzung**: Grundkenntnisse in irgendeiner Programmiersprache (JS, Python, VB, C#, Java...)
+**Ziel**: Du verstehst die Grundstruktur von Omnis und kannst einfachen Code lesen
+
+---
+
+## 1. Was ist Omnis Studio?
+
+Stell dir vor: Visual Studio + Access + Node.js in einem Tool. Das ist Omnis.
+
+| Was du kennst | Omnis-Aequivalent |
+|---------------|-------------------|
+| VS Code / IDE | Omnis Studio IDE (integriert) |
+| .sln / Projekt | Library (.lbs Datei) |
+| Klassen / Dateien | Classes (in der Library) |
+| Funktionen | Methods |
+| npm packages | Externe Libraries |
+| HTML/React | Windows (Desktop) oder Remote Forms (Web) |
+| SQL Workbench | Eingebauter SQL Browser |
+
+**Der groesste Unterschied:** Omnis ist eine **Kommando-basierte Sprache**, keine Expression-basierte. Jede Zeile beginnt mit einem Befehl.
+
+---
+
+## 2. Die Sprache: Befehle statt Ausdruecke
+
+### In JavaScript:
+```javascript
+let name = "Harry"; // Deklaration + Zuweisung
+let total = price * quantity; // Ausdruck
+console.log(name); // Funktionsaufruf
+```
+
+### In Omnis:
+```omnis
+Calculate lName as "Harry" ;; Zuweisung mit dem Befehl "Calculate"
+Calculate lTotal as lPrice * lQty ;; Berechnung mit "Calculate"
+OK message {[lName]} ;; Nachricht anzeigen (wie alert/console.log)
+```
+
+**Merke:**
+- `Calculate` = "Berechne und weise zu"
+- Variablen haben Praefix: `l` = lokal, `i` = Instanz, `t` = Task, `p` = Parameter
+- `;;` oder `#` fuer Kommentare
+- `{[variable]}` fuer String-Interpolation in Texten
+
+---
+
+## 3. Variablen: Das Praefix-System
+
+In Omnis hat JEDE Variable einen Praefix, der ihren **Scope** anzeigt:
+
+| Praefix | Scope | Wie in JS/Python |
+|---------|-------|-----------------|
+| `l` | Lokal (nur diese Methode) | `let x` / lokale Variable |
+| `i` | Instanz (dieses Objekt) | `this.x` / `self.x` |
+| `t` | Task (globale Session) | Globale Variable |
+| `p` | Parameter (Eingabe) | Funktionsparameter |
+| `c` | Klassen-Variable (statisch) | `static` / Klassenvariable |
+
+```omnis
+# Beispiel: Methode mit Parameter und lokaler Variable
+Calculate lFullName as con(pFirstName, " ", pLastName)
+Calculate iCustomerName as lFullName ;; In Instanz-Var speichern
+```
+
+**Uebung 1:** Was ist der Scope dieser Variablen?
+- `lCounter` -> ???
+- `iOrderList` -> ???
+- `tCurrentUser` -> ???
+- `pAmount` -> ???
+
+Loesung
+
+- `lCounter` -> Lokal (nur in der aktuellen Methode)
+- `iOrderList` -> Instanz (gehoert zum Objekt)
+- `tCurrentUser` -> Task (global in der Session)
+- `pAmount` -> Parameter (wurde der Methode uebergeben)
+
+Loesung
+
+```omnis
+Calculate lSum as 0
+For lI from 1 to 100 step 1
+ If mod(lI, 2) = 0
+ Calculate lSum as lSum + lI
+ End If
+End For
+OK message {Summe: [lSum]}
+```
+
+Antworten
+
+1. Holt den Datennamen (= gebundene Variable) des UI-Elements "orderGrid" auf der aktuellen Instanz.
+2. `Calculate lVisible as $cinst.$objs.btnSave.$visible`
+3. `Calculate` = Berechnung/Zuweisung, `Do` = Methodenaufruf/Befehlsausfuehrung
+
+
' + Markdown.esc(code.trim()) + '';
+ });
+
+ // Inline code
+ html = html.replace(/`([^`]+)`/g, '$1');
+
+ // Bold
+ html = html.replace(/\*\*(.+?)\*\*/g, '$1');
+
+ // Italic
+ html = html.replace(/\*(.+?)\*/g, '$1');
+
+ // Headings
+ html = html.replace(/^### (.+)$/gm, '