/* ═══════════════════════════════════════════════════════════════════════
   funci.css
   Ausgelagertes Stylesheet aus funci.html (siehe Code-Review /
   Refactoring: Massnahme 5 - Modul-Split). Enthaelt die komplette
   Praesentationsschicht (Layout, Themes, Responsive/Mobile-Regeln).
   Muss im selben Ordner wie funci.html liegen (wird dort per
   <link rel="stylesheet" href="funci.css"> eingebunden).
═══════════════════════════════════════════════════════════════════════ */

        /**
         * BUGFIX (Code-Review Maßnahme 1 - technische Priorisierung):
         * über 400 var(--...)-Verwendungen im gesamten Stylesheet, aber
         * BISHER KEIN EINZIGES :root definierte diese Variablen
         * tatsächlich - viele (u.a. --clr-accent, --clr-surface,
         * --clr-text, --box-bg, --radius, --shadow*, --transition-fast,
         * --success) hatten dabei nicht einmal einen Fallback-Wert. Mit
         * echtem Chromium-Rendering bestätigt: das führte zu SCHWARZEM
         * TEXT AUF WEISSEM HINTERGRUND statt der beabsichtigten dunklen
         * Oberfläche mit grünen Akzenten - kein rein kosmetischer
         * Schönheitsfehler, sondern eine tatsächlich kaputte Darstellung
         * bei jedem frischen Laden der Seite. Die hier eingetragenen
         * Werte wurden aus bereits vorhandenen, konsistent im ganzen
         * Stylesheet wiederkehrenden rgba()-Mustern abgeleitet (z.B.
         * rgba(var(--success-rgb), ...) für --clr-accent) sowie aus dem
         * bereits korrekt funktionierenden :root in admin_backend.html
         * (dieselbe Anwendung, dieselbe beabsichtigte Farbpalette).
         * html[data-theme="light"] überschreibt dieselben Variablen für
         * den Hellmodus - vorher gab es dafür ebenfalls keine zentrale
         * Stelle, weshalb effektiv NUR der (durch das Fehlen jeglicher
         * Definition zufällig "hell" wirkende) Browser-Standard sichtbar
         * war, nicht ein bewusst gestalteter Hellmodus.
         */
        :root {
            --clr-bg: #1a2332;
            --clr-surface: #212c40;
            --box-bg: #161f2e;
            --clr-text: #e2e8f0;
            /**
             * BUGFIX (Code-Review Maßnahme 7 - Barrierefreiheits-Audit):
             * mit echtem axe-core-Scan des DARKMODE-Zustands bestätigt
             * (vorheriger Durchgang prüfte nur den Lightmode-Start-
             * zustand) - #64748b war auch im Darkmode gegen mehrere
             * seiner tatsächlichen Hintergründe zu dunkel für den WCAG-
             * AA-Mindestkontrast.
             */
            --clr-muted: #8995a6;
            --clr-accent: #34d399;
            --clr-accent2: #38bdf8;
            --clr-on-accent: #06231a;
            --border-color: #33415c;
            --danger: #f87171;
            --warning: #fbbf24;
            --success: #34d399;
            /**
             * NEU (Code-Review Maßnahme 7 - Barrierefreiheits-Audit):
             * RGB-Einzelwerte (ohne "#", kommagetrennt) derselben Farben
             * wie oben - ermöglichen rgba(var(--warning-rgb), 0.4)-artige
             * Verwendung für TEILWEISE transparente Ränder/Hintergründe,
             * OHNE dabei (wie bei einem pauschalen CSS opacity auf das
             * ganze Element) auch den eigentlichen TEXT mit abzudunkeln -
             * genau das war die Ursache mehrerer mit echtem axe-core-Scan
             * gefundener Kontrast-Verstöße bei den nicht ausgewählten
             * Schwierigkeits-Buttons (siehe .diff-btn weiter unten).
             */
            --danger-rgb: 248, 113, 113;
            --warning-rgb: 251, 191, 36;
            --success-rgb: 52, 211, 153;
            --clr-accent2-rgb: 56, 189, 248;
            /**
             * NEU (Code-Review Maßnahme 7 - Barrierefreiheits-Audit):
             * die Tipp-Button-Farbe war bisher als fester Hex-Wert
             * hartkodiert (unabhängig vom Theme) - #a78bfa erfüllt den
             * WCAG-AA-Mindestkontrast im Darkmode (4.80:1), NICHT aber
             * im Lightmode (2.35:1). Jetzt als eigene, themenabhängige
             * Variable statt eines universellen Werts - der Darkmode
             * behält die ursprüngliche, bereits passende Farbe.
             */
            --hint-purple: #a78bfa;
            --radius: 8px;
            --shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
            --shadow: 0 1px 3px rgba(0, 0, 0, .45);
            --shadow-lg: 0 4px 20px rgba(0, 0, 0, .3);
            --transition-fast: .15s ease;
        }

        html[data-theme="light"] {
            --clr-bg: #f8fafc;
            --clr-surface: #ffffff;
            --box-bg: #f1f5f9;
            --clr-text: #1e293b;
            /**
             * BUGFIX (Code-Review Maßnahme 7 - Barrierefreiheits-Audit):
             * mit echtem axe-core-Scan gegen die tatsächlich gerenderte
             * Seite bestätigt - --clr-muted/--clr-accent/--clr-accent2/
             * --warning/--danger waren im Lightmode zu hell für den
             * WCAG-AA-Mindestkontrast (4.5:1) auf ihren jeweiligen
             * Hintergründen (u.a. Sterne-/Abzeichen-Anzeige, Schwer-
             * igkeits-Buttons, Ausführen-/Debug-Buttons, Struktogramm/
             * Flussdiagramm). Neue Werte rechnerisch ermittelt (WCAG-
             * Kontrastformel), jeweils mit spürbarem Sicherheitsabstand
             * über der 4.5:1-Grenze statt nur knapp darüber. Der
             * Darkmode ist von dieser Änderung nicht betroffen (eigene,
             * bereits ausreichend kontrastreiche Werte, unverändert).
             */
            --clr-muted: #485364;
            /* v14.91: #047f59/#0176b3 erreichten auf getönten Flächen
               (Codes, aktive Aufgabe, Konsole) nur ~4:1 - gefunden mit der
               a11y-Prüfung aller Fenster (test/run.js, "a11y v14.91"). */
            --clr-accent: #036b4b;
            --clr-accent2: #01649a;
            --clr-on-accent: #ffffff;
            --border-color: #cbd5e1;
            --danger: #ce2323;
            --warning: #a45a04;
            --success: #036b4b;
            --danger-rgb: 206, 35, 35;
            --warning-rgb: 164, 90, 4;
            --success-rgb: 3, 107, 75;
            --clr-accent2-rgb: 1, 100, 154;
            --hint-purple: #715eaa;
            --shadow-sm: 0 1px 2px rgba(0, 0, 0, .06);
            --shadow: 0 1px 3px rgba(0, 0, 0, .1);
            --shadow-lg: 0 4px 20px rgba(0, 0, 0, .08);
        }

        /* ── Vollbild-Layout ── */
        /**
         * BUGFIX (live gemeldet: "kommt man durch Hochscrollen manchmal
         * nicht mehr an die oberste Leiste"): body allein mit
         * overflow:hidden reicht auf mobilen Browsern (insbesondere iOS
         * Safari) NICHT zuverlässig aus, um das äußere Dokument/den
         * Viewport am Scrollen zu hindern - ein bekanntes, oft
         * übersehenes Verhalten: mobile Browser erlauben trotz
         * body{overflow:hidden} weiterhin ein leichtes "Bounce"/
         * Rubber-Banding des GESAMTEN Dokuments (nicht nur innerhalb
         * scrollbarer Bereiche wie #task-panel-body/#mob-desc-body), vor
         * allem nach schnellem Scrollen innerhalb eines verschachtelten
         * scrollbaren Bereichs bis an dessen Ende. Die Seite blieb dann
         * gelegentlich sichtbar leicht nach oben verschoben stehen - die
         * oberste Kopfzeile (mit dem Zurück-zum-Klassenraum-Knopf) ragte
         * dadurch teilweise oder ganz aus dem sichtbaren Bereich heraus,
         * OHNE dass sich durch weiteres Hochscrollen zuverlässig wieder
         * dorthin zurückkehren ließ. html BRAUCHT dieselbe Einschränkung
         * wie body - beide zusammen unterbinden das Bouncing des
         * Dokuments selbst zuverlässig, während die bewusst gewollten,
         * einzelnen scrollbaren Bereiche (task-panel-body, mob-desc-body,
         * task-list usw.) davon unberührt bleiben.
         */
        html {
            overflow: hidden;
            height: 100%;
            height: 100dvh;
            /* v14.45: tatsächlich sichtbare Höhe, per JS gemessen (siehe _syncAppHeight() in funci.html) */
            height: var(--app-height, 100dvh);
        }

        /**
         * BUGFIX (01.10.2026, live gemeldet: "meist am Tablet sieht man die
         * Buttonleiste mit Ausführen, Geschwindigkeitsregler, Debug, ...
         * nicht mehr (ganz) und kann nicht darauf zugreifen"): body ist
         * exakt 100dvh hoch, trug aber den Browser-Standardrand (8px) und
         * darüber hinaus nahm der Sprunglink "Zum Inhalt springen" als
         * normales Flex-Kind 18px Höhe ein (für ihn gab es gar keine
         * Regel) - zusammen ragte das Layout 26px unter den sichtbaren
         * Bereich (overflow:hidden, also nicht erreichbar). Am Desktop
         * fielen nur die untersten 8px der Steuerleiste weg; hochkant am
         * Tablet bricht die Steuerleiste zweizeilig um und mit den
         * Browser-Leisten verschwand sie sichtbar. Jetzt: kein Rand, der
         * Sprunglink erscheint (Barrierefreiheit) nur bei Tastaturfokus
         * und nimmt sonst keinen Platz ein.
         */
        .skip-link {
            position: absolute;
            left: 8px;
            top: -100px;
            z-index: 100000;
            padding: 8px 14px;
            border-radius: 6px;
            background: var(--clr-accent);
            color: var(--clr-on-accent);
            font-weight: 700;
            text-decoration: none;
        }

        .skip-link:focus,
        .skip-link:focus-visible {
            top: 8px;
        }

        body {
            margin: 0; /* siehe BUGFIX-Kommentar bei .skip-link oben */
            /* v15.42 (PO-Auftrag): bisher ohne eigene Schrift = Browser-Standard (meist eine
               Serifenschrift wie Times), während Knöpfe und Blöcke serifenlos waren. Jetzt
               derselbe Systemschrift-Stapel wie im Aufgaben-Editor. Code/Konsole behalten
               ihre eigene Festbreitenschrift, Welt-Symbole ihren Emoji-Stapel. */
            font-family: 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
            overflow: hidden;
            height: 100vh;
            /* 100dvh berücksichtigt auf Smartphones die ein-/ausblendende
               Adressleiste korrekt – 100vh allein führt sonst dazu, dass
               am unteren Rand liegende Elemente (z.B. die mobile
               Steuerleiste) teilweise hinter der Browser-UI verschwinden.
               Browser ohne dvh-Unterstützung ignorieren die Zeile und
               bleiben beim 100vh-Wert darüber. */
            height: 100dvh;
            /* BUGFIX (v14.45, live gemeldet, iPad Safari: Ausführen-Leiste
               unten halb abgeschnitten, in Brave nicht): Safari meldet für
               100dvh auf dem iPad in manchen Fenster-Situationen (Stage
               Manager/Split View/nach Drehen) ~40px mehr als sichtbar.
               --app-height ist die per JS gemessene, wirklich sichtbare
               Höhe (siehe _syncAppHeight() in funci.html); ohne JS bleibt
               100dvh. */
            height: var(--app-height, 100dvh);
            /* BUGFIX (Tablet-Ansicht, browserabhängig): body war bisher
               KEIN Flex-Container - #layout berechnete seine Höhe
               stattdessen über eine fest einprogrammierte Annahme
               ("calc(100dvh - 54px)", siehe dort), wie hoch #app-header
               angeblich IMMER ist. Rendert ein Browser/Betriebssystem
               den Header (z.B. durch andere Schriftmetriken, Zoomstufe
               oder mehrzeilig umbrechenden Inhalt bei mittleren
               Bildschirmbreiten) auch nur wenige Pixel höher als diese
               54px, ragt #layout entsprechend über den sichtbaren
               Bereich hinaus - die STEUERLEISTE am unteren Ende der
               Arbeitsfläche (Ausführen/Debug/...) verschwindet dadurch
               unten aus dem Bild, ohne dass sich (wegen "overflow:
               hidden" oben) dorthin scrollen ließe. Mit display:flex hier
               UND flex:1 bei #layout (statt der festen calc()-Höhe,
               siehe dort) berechnet der Browser die verbleibende Höhe
               jetzt automatisch anhand der TATSÄCHLICHEN Header-Höhe,
               unabhängig davon, wie hoch die im Einzelfall ausfällt. */
            display: flex;
            flex-direction: column;
            /* BUGFIX (v14.66, live gemeldet, iPad Safari: "Wenn man im
               Browser die Symbolleiste ausblendet, dann sieht man den
               Bereich, sonst ist er zum Teil oder ganz abgeschnitten"):
               neuere Safari-Versionen legen ihre Leisten ÜBER die Seite,
               melden innerHeight/visualViewport/100dvh aber unverändert -
               die Messung in _syncAppHeight() kann das nicht erkennen.
               Mit viewport-fit=cover (Meta-Viewport in funci.html) meldet
               Safari den verdeckten Bereich als Safe-Area; der Innenabstand
               hält den Inhalt davon frei. border-box, damit die Gesamthöhe
               --app-height bleibt. Ohne verdeckte Leisten sind alle
               Werte 0 (keine Änderung). */
            box-sizing: border-box;
            padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
            /* BUGFIX v15.49 (live gemeldet, iPad/Safari 26.5 mit Tab-Leiste: Ausführen-Leiste
               bis auf wenige Pixel abgeschnitten; ?vp=1 am Gerät: innerHeight/visualViewport/
               --app-height stimmten, die Leiste endete aber unabhängig von der Fensterhöhe
               immer bei der Höhe OHNE Tab-Leiste (quer 717 bei 640/688/734, hoch 1077 bei
               1048/1094), das Dokument war zudem um 29 px verschoben): body hängt jetzt
               nicht mehr im Dokumentfluss, sondern ist fest an den sichtbaren Bereich
               gebunden (position: fixed + oben/unten 0) - die Höhe ergibt sich direkt aus
               Safaris Viewport, höchstens --app-height; das Dokument kann nicht mehr
               verrutschen (es gibt nichts mehr zu scrollen). In Playwright-WebKit nicht
               nachstellbar (dort passte es schon vorher). */
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            height: auto;
            max-height: var(--app-height, 100%);
        }

        .blocklyWidgetDiv,
        .blocklyDropDownDiv,
        .blocklyTooltipDiv {
            z-index: 99990 !important;
        }

        .blocklyHtmlInput {
            z-index: 99991 !important;
        }

        /* ── App-Header ── */
        #app-header {
            background: linear-gradient(135deg, var(--clr-surface), var(--clr-bg));
            border-bottom: 2px solid var(--clr-accent);
            padding: 0 20px;
            height: 54px;
            display: flex;
            align-items: center;
            gap: 14px;
            flex-shrink: 0;
            position: relative;
            z-index: 50;
        }

        #app-header .logo {
            display: flex;
            align-items: center;
            padding: 0;
            border: none;
            background: none;
            cursor: pointer;
            border-radius: 8px;
            transition: transform .15s, filter .15s;
        }

        #app-header .logo:hover,
        #app-header .logo:focus-visible {
            transform: scale(1.06);
            filter: drop-shadow(0 0 6px var(--clr-accent));
            outline: none;
        }

        #app-header .logo img.header-logo-img {
            display: block;
            height: 50px;
            width: auto;
        }

        /**
         * NEU (live gemeldet: "auch das kleine logo soll sich am theme
         * anpassen hell/dunkel"): dasselbe Prinzip wie beim Popup-Logo
         * (siehe #logo-modal-img-light/html[data-theme="light"] weiter
         * unten) - Standard (kein/anderes data-theme) zeigt das dunkle
         * Logo, "light" schaltet auf das helle um.
         */
        #app-header .logo img.header-logo-img-light {
            display: none;
        }

        html[data-theme="light"] #app-header .logo img.header-logo-img-dark {
            display: none;
        }

        html[data-theme="light"] #app-header .logo img.header-logo-img-light {
            display: block;
        }

        .sr-only {
            position: absolute;
            width: 1px; height: 1px;
            padding: 0; margin: -1px;
            overflow: hidden;
            clip: rect(0, 0, 0, 0);
            white-space: nowrap;
            border: 0;
        }

        #logo-modal-box {
            max-width: 92vw;
            text-align: center;
            padding: 22px;
        }

        .logo-modal-img {
            max-width: 100%;
            max-height: 86vh;
            border-radius: 12px;
            display: block;
            margin: 0 auto;
        }

        /**
         * NEU (live gemeldet: theme-abhängiges Logo im Modal): Standard
         * (kein data-theme-Attribut bzw. jeder Wert außer "light") ist
         * laut bestehender Konvention im ganzen Projekt immer Dunkel-
         * modus (siehe z.B. drawGridCanvas()/_drawGridWorld() weiter
         * oben: "isDark = ... !== 'light'") - deshalb hier bewusst
         * ebenso: das DUNKLE Logo ist der Normalfall, das HELLE wird nur
         * bei explizit gesetztem data-theme="light" gezeigt.
         */
        #logo-modal-img-light {
            display: none;
        }

        html[data-theme="light"] #logo-modal-img-dark {
            display: none;
        }

        html[data-theme="light"] #logo-modal-img-light {
            display: block;
        }

        /**
         * BUGFIX (live gemeldet: "der Text 'FUN·C·I ...' zeigt zum Teil
         * über den oberen Bereich hinaus, wenn man das Fenster in der
         * Breite verkleinert"): .header-title-wrap war bisher ein
         * unbenannter Flex-Kind-<div> OHNE min-width:0 - eine bekannte
         * Flexbox-Falle: Flex-Kinder haben standardmäßig
         * min-width:auto, können also nicht kleiner werden als ihr
         * Inhalt, selbst wenn der umgebende Header eng wird. Titel/
         * Untertitel bekommen zusätzlich eine harte Ein-Zeilen-Kürzung
         * (nowrap+ellipsis) statt eines mehrzeiligen Umbruchs, der die
         * feste Header-Höhe gesprengt hätte.
         */
        #app-header .header-title-wrap {
            min-width: 0;
            overflow: hidden;
        }

        #app-header h1 {
            font-size: 1rem;
            margin: 0;
            font-weight: 800;
            background: linear-gradient(90deg, var(--clr-accent), var(--clr-accent2));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        #app-header .sub {
            font-size: .68rem;
            color: var(--clr-muted);
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        #app-header .stats {
            margin-left: auto;
            display: flex;
            gap: 10px;
            align-items: center;
        }

        /**
         * NEU (live gemeldet: "Der Sprachen-Dropdown-Button soll als
         * Icon die Fahne der ausgewählten Sprache haben. ... reicht das
         * Menü über die Browserbreite hinaus"): eigenes Dropdown statt
         * nativem <select> (siehe HTML-Kommentar bei
         * #lang-switcher-wrap für die ausführliche Begründung).
         * position:relative auf dem Wrapper + position:absolute mit
         * right:0 auf dem Menü verankert das Menü an seiner RECHTEN
         * Kante am Button, statt an der linken - dadurch wächst es beim
         * Aufklappen nach LINKS in den vorhandenen Platz hinein, statt
         * potenziell nach rechts über den Fensterrand hinaus (die
         * Fahnen-Schaltfläche sitzt im Header eher rechts).
         * max-width mit calc() begrenzt die Breite zusätzlich hart auf
         * die tatsächlich verfügbare Fensterbreite (abzüglich etwas
         * Rand), damit selbst bei einem sehr schmalen Fenster nichts
         * über den linken Rand hinausragen kann.
         */
        .lang-switcher-wrap {
            position: relative;
        }

        .lang-switcher-menu {
            position: absolute;
            top: calc(100% + 6px);
            right: 0;
            background: var(--clr-surface);
            border: 1px solid var(--border-color);
            border-radius: 10px;
            box-shadow: var(--shadow-lg);
            padding: 4px;
            display: flex;
            flex-direction: column;
            gap: 2px;
            z-index: 200;
            min-width: 150px;
            max-width: calc(100vw - 24px);
            max-width: calc(100dvw - 24px);
            box-sizing: border-box;
        }

        .lang-switcher-item {
            display: block;
            width: 100%;
            text-align: left;
            padding: 7px 10px;
            border: none;
            background: none;
            color: var(--clr-text);
            font-size: .82rem;
            border-radius: 6px;
            cursor: pointer;
            white-space: nowrap;
        }

        .lang-switcher-item:hover,
        .lang-switcher-item:focus-visible {
            background: var(--clr-bg2, rgba(255, 255, 255, .06));
            outline: none;
        }

        .lang-switcher-item.active {
            color: var(--clr-accent);
            font-weight: 700;
        }

        /**
         * NEU (live gemeldet: "ergänze in der Titelleiste zwischen
         * 'FUN·C·I ...' und dem Sterne-Button als Text immer den
         * aktuell gewählten Aufgabensatz und den gewählten
         * Aufgabentitel"): sitzt im Markup bewusst zwischen dem Titel-
         * Block und .stats - da .stats bereits über margin-left:auto
         * selbst an den rechten Rand rückt (siehe oben), landet dieser
         * Text dadurch automatisch GENAU im geforderten Zwischenraum,
         * ohne eigene Positionierung. min-width:0 zusammen mit
         * text-overflow:ellipsis sorgt dafür, dass ein sehr langer
         * Aufgabensatz-/Aufgabentitel den restlichen Kopfbereich
         * (insbesondere die Sterne/Abzeichen-Anzeigen) nicht verdrängt,
         * sondern sauber abgeschnitten wird.
         */
        #hdr-task-info {
            font-size: .74rem;
            font-weight: 700;
            color: var(--clr-muted);
            background: var(--box-bg);
            border: 1px solid var(--border-color);
            border-radius: 999px;
            padding: 3px 12px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            min-width: 0;
            flex-shrink: 1;
        }

        /**
         * NEU (live gemeldet): die Kopfbereich-Anzeigen (Sterne/Erledigt/
         * Abzeichen/Serie) sind jetzt klickbare <button>-Elemente statt
         * reiner <div>s (öffnen showStatsInfo(), siehe dort) - font/
         * border-Reset nötig, da Buttons sonst browserabhängige
         * Standardoptik mitbringen; das übrige Erscheinungsbild bleibt
         * bewusst identisch zur vorherigen, nicht klickbaren Fassung.
         */
        .stat-pill {
            background: var(--box-bg);
            border: 1px solid var(--border-color);
            padding: 3px 11px;
            border-radius: 999px;
            font-size: .78rem;
            color: var(--clr-muted);
            font-family: inherit;
            cursor: pointer;
            transition: var(--transition-fast);
        }

        .stat-pill:hover,
        .stat-pill:focus-visible {
            border-color: var(--clr-accent);
            color: var(--clr-text);
        }

        .stat-pill:focus-visible {
            outline: 2px solid var(--clr-accent);
            outline-offset: 1px;
        }

        .stat-pill strong {
            color: var(--clr-accent);
        }

        .theme-toggle {
            position: static;
            width: 36px;
            height: 36px;
            font-size: 1rem;
        }

        /* Nur das jeweils passende Symbol zeigen: Mond im Dunkelmodus
           (Standard, kein data-theme-Attribut gesetzt), Sonne im Hellmodus.
           Vorher wurden beide Emojis gleichzeitig angezeigt, da es dafür
           bisher keine CSS-Regel gab. */
        .theme-toggle .sun {
            display: none;
        }

        .theme-toggle .moon {
            display: inline;
        }

        html[data-theme="light"] .theme-toggle .sun {
            display: inline;
        }

        html[data-theme="light"] .theme-toggle .moon {
            display: none;
        }

        /**
         * NEU (live gemeldet): Basis-/Standardansicht-Umschalter -
         * dasselbe Icon-Tausch-Muster wie beim Sonne/Mond-Symbol direkt
         * darüber. Zeigt im aktuellen Zustand jeweils das Symbol für den
         * ZIEL-Zustand (Klick wechselt DORTHIN) - 🎓 (Standard) ist
         * sichtbar, solange man sich in der Basisansicht befindet (Klick
         * führt zur vollständigen/"fortgeschritteneren" Ansicht), 🔰
         * (Basis) ist sichtbar in der Standardansicht.
         */
        #view-mode-toggle .view-mode-basic {
            display: inline;
        }

        #view-mode-toggle .view-mode-standard {
            display: none;
        }

        html[data-view-mode="basic"] #view-mode-toggle .view-mode-basic {
            display: none;
        }

        html[data-view-mode="basic"] #view-mode-toggle .view-mode-standard {
            display: inline;
        }

        /**
         * ═══════════════════════════════════════════════════════════════
         * NEU (live gemeldet): Basisansicht - blendet Abzeichen, Serie,
         * Code-Reiter und Visualisierungs-Reiter aus, lässt die
         * Blockauswahl dauerhaft aufgeklappt (siehe zugehörige JS-Logik
         * in toggleViewMode()/_restoreFlyoutAutoCloseState()).
         * ═══════════════════════════════════════════════════════════════
         * [data-viz-type]/[data-tab]-Selektoren treffen bewusst sowohl
         * die Desktop- als auch die mobilen Gegenstücke (beide nutzen
         * dieselben Attribute/Klassen, siehe HTML) - eine einzige Regel
         * genügt dadurch für beide Ansichten gleichzeitig.
         */
        html[data-view-mode="basic"] .stat-pill-badges,
        html[data-view-mode="basic"] .stat-pill-streak,
        html[data-view-mode="basic"] .stat-pill-solved /* v15.54: Punkt 2 – nur noch ⭐, Antippen = Übersicht */ {
            display: none;
        }

        html[data-view-mode="basic"] .output-tab-btn[data-tab="code"],
        html[data-view-mode="basic"] .output-tab-btn[data-tab="viz"] {
            display: none;
        }

        /**
         * NEU (live gemeldet: "Blende in der Basis-Ansicht die Reiter
         * Listen und Aufruf-Stack aus"): dieselbe Technik wie bei den
         * Ausgabe-Reitern oben (Code/Visualisierung), jetzt für die
         * Variablen-Unterreiter. .var-tab-btn statt .output-tab-btn,
         * data-vartab statt data-tab - ansonsten identisches Muster.
         */
        html[data-view-mode="basic"] .var-tab-btn[data-vartab="lists"],
        html[data-view-mode="basic"] .var-tab-btn[data-vartab="callstack"] {
            display: none;
        }

        /**
         * NEU (01.10.2026, live angefordert: "Da in der Basisansicht im
         * Konsolenbereich nur die Konsole und im Variablenbereich nur die
         * Variablen angezeigt werden, kann man auf das farbliche
         * Hinterlegen der Kategorie Variablen und Konsole verzichten"):
         * der jeweils einzige verbliebene Reiter wirkt wie eine normale
         * Bereichsüberschrift (gleiche Optik wie z.B. "Inventar").
         */
        html[data-view-mode="basic"] .output-tab-btn.active,
        html[data-view-mode="basic"] .panel-head .output-tab-btn.active:hover,
        html[data-view-mode="basic"] #output-panel-head .output-tab-btn.active:hover {
            background: none;
            border-color: transparent;
            color: var(--clr-muted);
            padding-left: 0;
            text-transform: uppercase;
            letter-spacing: 1px;
            cursor: default;
        }

        html[data-view-mode="basic"] #ws-menu {
            display: none;
        }

        html[data-view-mode="basic"] #block-counter {
            right: 10px;
        }

        /**
         * NEU (live gemeldet: "Verzichte in der Basisansicht zusätzlich
         * auf die Debugging-Buttons"): .btn-debug/.btn-step treffen
         * wieder sowohl die Desktop- (#btn-debug/#btn-step) als auch die
         * mobilen Gegenstücke (#mob-btn-debug/#mob-btn-step), siehe
         * Kommentar oben bei den anderen [data-tab]-Regeln - dieselbe
         * Technik gilt auch hier.
         */
        html[data-view-mode="basic"] .btn-debug,
        html[data-view-mode="basic"] .btn-step {
            display: none;
        }

        /* Vollbild-Toggle: nur das passende Symbol zeigen (Erweitern-Pfeile
           wenn NICHT im Vollbildmodus, Verkleinern-Symbol wenn schon aktiv).
           body.is-fullscreen wird per JS bei jedem fullscreenchange-Ereignis
           gesetzt (auch wenn per Esc/System-Geste beendet wird).
           WICHTIG: für "exit" wird bewusst dasselbe ⛶-Zeichen wie für
           "enter" verwendet (nur um 45° gedreht) statt eines eigenen
           Verkleinern-Symbols (🗗 o.ä.) – solche selteneren Symbole werden
           von manchen Emoji-Schriften auf Tablet/Smartphone nicht
           dargestellt (leeres/fehlendes Zeichen), ⛶ dagegen zuverlässig.
           #primm-fullscreen-toggle (live gemeldet: "im PRIMM-Zyklus eine
           Möglichkeit, das PRIMM-Modal auf Vollbildschirm zu toggeln"):
           zweiter, gleichwertiger Button direkt im PRIMM-Modal, ruft
           dieselbe toggleFullscreen()-Funktion auf wie der Header-Button -
           dient nur der Erreichbarkeit, ohne das Modal verlassen zu
           müssen (Vollbild gilt ohnehin für die ganze Seite). */
        #fullscreen-toggle .fs-exit,
        #primm-fullscreen-toggle .fs-exit,
        #teacher-fullscreen-toggle .fs-exit {
            display: none;
            transform: rotate(45deg);
        }

        #fullscreen-toggle .fs-enter,
        #primm-fullscreen-toggle .fs-enter,
        #teacher-fullscreen-toggle .fs-enter {
            display: inline;
        }

        body.is-fullscreen #fullscreen-toggle .fs-exit,
        body.is-fullscreen #primm-fullscreen-toggle .fs-exit,
        body.is-fullscreen #teacher-fullscreen-toggle .fs-exit {
            display: inline;
        }

        body.is-fullscreen #fullscreen-toggle .fs-enter,
        body.is-fullscreen #primm-fullscreen-toggle .fs-enter,
        body.is-fullscreen #teacher-fullscreen-toggle .fs-enter {
            display: none;
        }

        /* ── Haupt-Layout ── */
        #layout {
            display: flex;
            flex: 1;
            min-height: 0;
            /* BUGFIX (Tablet-Ansicht): die vorherige feste "height:
               calc(100dvh - 54px)" hier überschrieb das oben bereits
               vorhandene "flex: 1" faktisch komplett (eine explizite
               height-Angabe hat Vorrang vor der flexiblen Bemessung über
               flex-basis) - siehe ausführlichen Kommentar bei body oben.
               min-height:0 ist zusätzlich nötig, damit Flex-Kinder
               innerhalb eines Spalten-Layouts wie hier überhaupt aktiv
               schrumpfen dürfen (Flexbox-Standardverhalten wäre sonst
               min-height:auto, was das Element NICHT unter die Höhe
               seines Inhalts schrumpfen ließe - dasselbe Problem in
               anderer Form). */
            overflow: hidden;
            position: relative;
        }

        /* ── Ein-/Ausklapp-Griffe für Aufgaben- und Ausgabebereich ──
           Direkte Kinder von #layout (nicht von #sidebar/#right-panel
           selbst!), damit sie beim Zuklappen des jeweiligen Bereichs
           (width:0 + overflow:hidden) nicht mit verschwinden – sie "kleben"
           stattdessen per CSS-Transition an der jeweiligen Außenkante und
           wandern so sichtbar bis an den Bildschirmrand. */
        .panel-collapse-btn {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            width: 16px;
            height: 48px;
            padding: 0;
            border: 1px solid var(--border-color);
            background: var(--clr-surface);
            color: var(--clr-muted);
            cursor: pointer;
            font-size: .65rem;
            display: flex;
            align-items: center;
            justify-content: center;
            /* Hoher z-index nötig: Blocklys eigener Werkzeugkasten
               (.blocklyToolbox) liegt am linken Rand von #center genau
               an derselben Stelle wie dieser Button und würde Klicks sonst
               abfangen ("intercepts pointer events" beim Testen). */
            z-index: 500;
            transition: left .25s ease, right .25s ease, background .15s, height .25s ease;
        }

        .panel-collapse-btn:hover {
            background: var(--clr-accent);
            color: var(--clr-on-accent);
        }

        .panel-collapse-btn .pc-arrow-right {
            display: none;
        }

        #sidebar-collapse-btn {
            left: 240px;
            border-radius: 0 6px 6px 0;
            border-left: none;
        }

        #layout.sidebar-collapsed #sidebar-collapse-btn {
            left: 0;
        }

        #layout.sidebar-collapsed #sidebar-collapse-btn .pc-arrow-left {
            display: none;
        }

        #layout.sidebar-collapsed #sidebar-collapse-btn .pc-arrow-right {
            display: flex;
        }

        /**
         * NEU (live gemeldet: "Die Beschriftung 'Aufgaben' beim Button
         * zum Ein-/Ausklappen der Aufgabenliste kann man nicht komplett
         * lesen. Beschrifte vielleicht vertikal statt horizontal"): der
         * Button ist mit Absicht nur 16px schmal (siehe Kommentar bei
         * .panel-collapse-btn oben) - "Aufgaben" horizontal in normaler
         * Schriftrichtung ließ sich darin nie vollständig darstellen,
         * das Wort wurde abgeschnitten. writing-mode:vertical-rl dreht
         * den Text stattdessen um 90° (liest von oben nach unten,
         * einzelne Buchstaben bleiben aufrecht lesbar durch
         * text-orientation:mixed) - passt dadurch bequem in die
         * vorhandene Breite. Die Höhe des eingeklappten Buttons wird
         * dafür (NUR im eingeklappten Zustand, siehe
         * #layout.sidebar-collapsed .../-btn-Regel unten) von 48px auf
         * 108px vergrößert, da "Aufgaben" (8 Buchstaben) selbst
         * senkrecht mehr als 48px Höhe braucht - im normalen
         * (ausgeklappten) Zustand, wo nur der Pfeil ohne Text gezeigt
         * wird, bleibt die ursprüngliche, kompaktere 48px-Höhe
         * unverändert. flex-direction:column + gap stapelt Pfeil und
         * Text sauber übereinander.
         */
        #layout.sidebar-collapsed #sidebar-collapse-btn {
            height: 108px;
            flex-direction: column;
            gap: 6px;
        }

        #sidebar-collapse-expand-label {
            writing-mode: vertical-rl;
            text-orientation: mixed;
            font-size: .68rem;
            font-weight: 700;
            letter-spacing: .02em;
            white-space: nowrap;
        }

        /* ── Sidebar ── */
        #sidebar {
            width: 240px;
            background: var(--clr-surface);
            border-right: 1px solid var(--border-color);
            display: flex;
            flex-direction: column;
            flex-shrink: 0;
            overflow: hidden;
            transition: width .25s ease, min-width .25s ease, border-right-width .25s ease;
        }

        /* Klick auf "← Aufgabe wählen" bei bereits offener Aufgabenleiste:
           kurzes Aufleuchten zeigt, wo gewählt wird (siehe
           _onChooseTaskHintSelected()). Nur ein innerer Rahmen, kein
           Layout-Sprung. */
        #sidebar.sidebar-attention {
            animation: sidebarAttention 1.2s ease-out;
        }

        @keyframes sidebarAttention {
            0%,
            60% {
                box-shadow: inset 0 0 0 3px var(--clr-accent);
            }

            100% {
                box-shadow: inset 0 0 0 3px transparent;
            }
        }

        @media (prefers-reduced-motion: reduce) {
            #sidebar.sidebar-attention {
                animation: none;
                box-shadow: inset 0 0 0 3px var(--clr-accent);
            }
        }

        #layout.sidebar-collapsed #sidebar {
            width: 0;
            min-width: 0;
            border-right-width: 0;
        }

        #sidebar-head {
            margin: 0;
            padding: 10px 14px;
            background: var(--box-bg);
            font-weight: 700;
            font-size: .8rem;
            flex-shrink: 0;
            color: var(--clr-muted);
            text-transform: uppercase;
            letter-spacing: .06em;
            border-bottom: 1px solid var(--border-color);
        }

        #task-set-bar {
            padding: 8px 10px;
            background: var(--box-bg);
            border-bottom: 1px solid var(--border-color);
            flex-shrink: 0;
        }

        #task-set-bar select {
            width: 100%;
            padding: 7px 8px;
            border-radius: 6px;
            border: 1px solid var(--border-color);
            background: var(--clr-bg);
            color: var(--clr-text);
            font-size: .78rem;
            font-family: inherit;
            cursor: pointer;
        }

        #task-set-bar select:focus-visible {
            outline: 2px solid var(--clr-accent);
            outline-offset: 1px;
        }

        #task-set-bar.mob-variant {
            padding: 8px 12px;
        }

        #task-list {
            overflow-y: auto;
            flex: 1;
        }

        .cat-header {
            padding: 6px 14px;
            background: var(--clr-bg);
            font-size: .65rem;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: 1px;
            color: var(--clr-accent2);
            border-top: 1px solid var(--border-color);
            border-bottom: 1px solid var(--border-color);
        }

        /**
         * BUGFIX (live gemeldet: Daumen-Buttons quetschten Titel/
         * Kategorie ab; Kategorie war selbst in einer eigenen Zeile mit
         * den Daumen noch teils abgeschnitten): .task-item ist jetzt eine
         * Spalte aus DREI Zeilen - .task-row1 (Nummer + Titel + Sterne),
         * .task-row2 (nur Kategorie, volle Breite) und .task-row3 (nur
         * Daumen, rechtsbündig). Jede Zeile enthält dadurch höchstens
         * EIN Element, das um Platz konkurrieren könnte - Titel UND
         * Kategorie bleiben so garantiert vollständig lesbar.
         */
        .task-item {
            padding: 7px 12px;
            border-bottom: 1px solid var(--border-color);
            display: flex;
            flex-direction: column;
            gap: 3px;
            transition: var(--transition-fast);
            position: relative;
        }

        /**
         * BUGFIX (Barrierefreiheits-Audit, echter axe-core-Fund
         * "nested-interactive"): role="button"/onclick lagen vorher auf
         * .task-item selbst - dadurch waren die echten <button>-Elemente
         * darin (Daumen-Bewertung, PRIMM-Badge) als interaktives Element
         * in einem interaktiven Element verschachtelt. .task-item-open
         * umschließt jetzt NUR noch Zeile 1+2 (Titel/Kategorie/Sterne,
         * der eigentliche "Aufgabe öffnen"-Bereich) und trägt Rolle/
         * Cursor/Klick-Semantik allein - dieselbe Spalten-Anordnung wie
         * vorher bei .task-item, damit sich am Erscheinungsbild nichts
         * ändert.
         */
        .task-item-open {
            cursor: pointer;
            display: flex;
            flex-direction: column;
            gap: 3px;
        }

        /**
         * NEU (live gemeldet: "Ergänze an jeder Aufgabe im Aufgabenbereich,
         * die einen PRIMM-Auslösepunkt hat, einen PRIMM-Badge oben
         * rechts"): kleiner, runder Knopf oben rechts in der
         * Aufgabenkarte - klickbar unabhängig vom Rest der Karte (siehe
         * event.stopPropagation() in _primmBadgeClick()), daher mit
         * eigenem z-index über dem Karten-Inhalt.
         */
        .task-primm-badge {
            position: absolute;
            top: 4px;
            right: 4px;
            z-index: 2;
            width: 22px;
            height: 22px;
            border-radius: 50%;
            border: none;
            background: var(--clr-accent);
            color: #fff;
            font-size: .78rem;
            line-height: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            padding: 0;
            box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
        }

        .task-primm-badge:hover {
            filter: brightness(1.15);
            transform: scale(1.08);
        }

        .task-item:hover {
            background: var(--box-bg);
        }

        .task-item.active {
            background: rgba(var(--success-rgb), 0.18);
            border-left: 4px solid var(--clr-accent);
            box-shadow: inset 0 0 0 1px rgba(var(--success-rgb), 0.25);
        }

        .task-item.active .task-title {
            font-weight: 700;
            color: var(--clr-accent);
        }

        /* v14.91: --clr-muted auf der grün getönten aktiven Zeile nur 3,2:1 */
        .task-item.active .task-cat {
            color: var(--clr-text);
        }

        .task-row1 {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .task-num {
            width: 22px;
            height: 22px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: .7rem;
            font-weight: 700;
            color: var(--clr-on-accent);
            flex-shrink: 0;
        }

        .task-title {
            flex: 1;
            min-width: 0;
            margin: 0;
            font-size: .78rem;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            color: var(--clr-text);
            font-weight: 400;
        }

        .task-stars {
            font-size: .72rem;
            flex-shrink: 0;
        }

        /**
         * Um dieselbe Breite wie .task-num (22px) + dessen gap (8px)
         * eingerückt, damit die Kategorie optisch bündig unter dem
         * Titel in .task-row1 beginnt, statt am linken Rand der Zeile.
         * Enthält NUR die Kategorie (volle Restbreite, keine Konkurrenz
         * mit den Daumen-Buttons mehr - die stehen jetzt in einer
         * eigenen dritten Zeile, siehe .task-row3) - bei sehr langen
         * Kategorienamen wurde sonst trotz eigener Zeile noch abgeschnitten.
         */
        .task-row2 {
            padding-left: 30px;
        }

        .task-cat {
            margin: 0;
            font-size: .65rem;
            color: var(--clr-muted);
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        /**
         * Bewertungssystem: eigene dritte Zeile für die Daumen-Buttons
         * (live gemeldet: sowohl Titel als auch Kategorie sollen
         * vollständig lesbar bleiben - in derselben Zeile wie die
         * Kategorie wurden längere Kategorienamen trotz eigener
         * .task-row2 noch abgeschnitten). Rechtsbündig, mit demselben
         * Einzug wie .task-row2, damit alle drei Zeilen optisch bündig
         * beginnen.
         */
        .task-row3 {
            display: flex;
            justify-content: flex-end;
            padding-left: 30px;
        }

        .task-thumbs {
            display: flex;
            gap: 3px;
            flex-shrink: 0;
        }

        .thumb-btn {
            display: flex;
            align-items: center;
            gap: 2px;
            padding: 2px 5px;
            border: 1px solid transparent;
            border-radius: 5px;
            background: transparent;
            cursor: pointer;
            font-size: .68rem;
            line-height: 1;
            color: var(--clr-muted);
            transition: var(--transition-fast);
        }

        .thumb-btn:hover {
            background: var(--box-bg);
            border-color: var(--border-color);
        }

        .thumb-btn.thumb-up.voted {
            background: rgba(var(--success-rgb), 0.18);
            border-color: var(--clr-accent);
            color: var(--clr-accent);
        }

        .thumb-btn.thumb-down.voted {
            background: rgba(var(--danger-rgb), 0.14);
            border-color: var(--danger, #f87171);
            color: var(--danger, #f87171);
        }

        .thumb-count {
            font-size: .66rem;
            font-variant-numeric: tabular-nums;
            min-width: 9px;
            text-align: left;
        }

        /* ── Center ── */
        #center {
            flex: 1;
            display: flex;
            flex-direction: column;
            overflow: hidden;
            min-width: 0;
        }

        /* ── Aufgaben-Panel ── */
        #task-panel {
            background: var(--clr-surface);
            border-bottom: 1px solid var(--border-color);
            padding: 8px 14px;
            flex-shrink: 0;
        }

        #task-panel-top {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 6px;
            flex-wrap: wrap;
        }

        #task-panel h2 {
            font-size: .9rem;
            color: var(--clr-text);
            flex: 1;
            margin: 0;
            font-weight: 700;
        }

        /**
         * BUGFIX (Code-Review Maßnahme 7 - Barrierefreiheits-Audit): mit
         * echtem axe-core-Scan bestätigt - das pauschale opacity:.6 auf
         * dem gesamten Button dunkelte auch den TEXT ab (nicht nur den
         * Rahmen), wodurch "Mittel"/"Schwer" im nicht ausgewählten
         * Zustand deutlich unter dem WCAG-AA-Mindestkontrast lagen
         * (teils nur 2.5:1 statt der geforderten 4.5:1). Die optische
         * Zurückhaltung nicht ausgewählter Buttons bleibt erhalten,
         * wirkt jetzt aber NUR noch über einen transparenteren Rahmen
         * (relevant für WCAG nur mit der niedrigeren 3:1-Schwelle für
         * Nicht-Text-Elemente) - der Text selbst bleibt durchgehend in
         * voller, zugänglicher Farbe.
         */
        .diff-btn {
            position: relative;
            overflow: visible;
            padding: 4px 12px;
            border: 2px solid transparent;
            border-radius: 999px;
            cursor: pointer;
            font-size: .72rem;
            font-weight: 700;
            background: transparent;
            transition: var(--transition-fast);
        }

        .diff-btn:hover {
            background: rgba(255, 255, 255, .06);
        }

        /* BUGFIX (Absturz "lv is undefined"): Aufgaben ohne diese Stufe -
           siehe syncDiffButtons() - werden hier sichtbar ausgegraut und
           nicht mehr anklickbar, statt einen Absturz zu riskieren. */
        .diff-btn:disabled,
        .diff-btn.diff-btn-unavailable {
            opacity: .25;
            cursor: not-allowed;
            pointer-events: none;
        }

        .diff-btn.easy {
            border-color: rgba(var(--success-rgb), .55);
            color: var(--success);
        }

        .diff-btn.medium {
            border-color: rgba(var(--warning-rgb), .55);
            color: var(--warning);
        }

        .diff-btn.hard {
            border-color: rgba(var(--danger-rgb), .55);
            color: var(--danger);
        }

        /* Bereits gelöst (mit Stern): auch wenn NICHT gerade ausgewählt,
           soll man das auf einen Blick sehen – gefüllte, farbige Fläche
           in voller Deckkraft plus ein Häkchen-Badge oben rechts am Button. */
        .diff-btn.done {
            opacity: 1;
        }

        .diff-btn.easy.done {
            background: rgba(var(--success-rgb), .25);
        }

        .diff-btn.medium.done {
            background: rgba(var(--warning-rgb), .25);
        }

        .diff-btn.hard.done {
            background: rgba(var(--danger-rgb), .25);
        }

        /* Gelöst, aber OHNE Stern (Musterlösung genutzt oder vom Lehrer
           gelöst): bewusst neutral/grau statt in der Ampelfarbe, damit der
           Unterschied zu "richtig mit Stern gelöst" sofort erkennbar ist. */
        .diff-btn.done-nostar {
            opacity: 1;
            background: rgba(148, 163, 184, .25);
            border-color: rgba(148, 163, 184, .7);
            color: var(--clr-muted);
        }

        .diff-btn.done::after,
        .diff-btn.done-nostar::after {
            content: '✓';
            position: absolute;
            top: -7px;
            right: -7px;
            width: 16px;
            height: 16px;
            line-height: 16px;
            border-radius: 50%;
            font-size: .62rem;
            font-weight: 900;
            text-align: center;
            color: #fff;
            box-shadow: 0 1px 3px rgba(0, 0, 0, .45);
        }

        .diff-btn.easy.done::after {
            background: var(--success);
        }

        .diff-btn.medium.done::after {
            background: var(--warning);
        }

        .diff-btn.hard.done::after {
            background: var(--danger);
        }

        .diff-btn.done-nostar::after {
            background: #94a3b8;
            content: '✓';
        }

        /* Aktive Schwierigkeit deutlich hervorgehoben: volle Deckkraft,
           gefüllter Hintergrund, Leucht-Ring UND leichte Vergrößerung –
           mehrere sich verstärkende Signale statt nur der Füllfarbe,
           damit auf einen Blick klar ist, welche Stufe gerade bearbeitet wird.
           Diese Regeln stehen bewusst NACH .done/.done-nostar, damit bei
           gleichzeitig aktiver UND gelöster Stufe die Auswahl-Füllung
           gewinnt – das Häkchen-Badge bleibt trotzdem sichtbar. */
        .diff-btn.easy.active {
            background: var(--success);
            color: var(--clr-on-accent);
            opacity: 1;
            transform: scale(1.1);
            box-shadow: 0 0 0 3px rgba(var(--success-rgb), .3), 0 2px 10px rgba(var(--success-rgb), .45);
        }

        .diff-btn.medium.active {
            background: var(--warning);
            color: var(--clr-on-accent);
            opacity: 1;
            transform: scale(1.1);
            box-shadow: 0 0 0 3px rgba(var(--warning-rgb), .3), 0 2px 10px rgba(var(--warning-rgb), .45);
        }

        .diff-btn.hard.active {
            background: var(--danger);
            color: #fff;
            opacity: 1;
            transform: scale(1.1);
            box-shadow: 0 0 0 3px rgba(var(--danger-rgb), .3), 0 2px 10px rgba(var(--danger-rgb), .45);
        }

        #task-desc {
            background: var(--box-bg);
            border-left: 3px solid var(--clr-accent);
            padding: 6px 10px;
            border-radius: 0 8px 8px 0;
            font-size: .8rem;
            line-height: 1.5;
            color: var(--clr-muted);
        }

        #task-desc code {
            background: rgba(var(--success-rgb), 0.12);
            padding: 1px 4px;
            border-radius: 3px;
            font-family: monospace;
            color: var(--clr-accent);
        }

        #task-meta {
            display: flex;
            gap: 8px;
            margin-top: 5px;
            flex-wrap: wrap;
        }

        .meta-pill {
            background: var(--box-bg);
            border: 1px solid var(--border-color);
            padding: 2px 8px;
            border-radius: 999px;
            font-size: .69rem;
            color: var(--clr-muted);
        }

        #hint-box {
            margin-top: 5px;
            background: rgba(var(--warning-rgb), 0.08);
            border: 1px solid var(--warning);
            border-radius: 8px;
            padding: 5px 10px;
            font-size: .78rem;
            color: var(--warning);
            display: none;
        }

        /**
         * NEU (live gemeldet: "gestufte Hilfe"): Kopfzeile mit dem
         * Zähler ("Hilfe 2/4") sowie die Vor-/Zurück-Navigation
         * darunter. Bewusst schlicht gehalten, in derselben Warnfarbe
         * wie der Rest von #hint-box, damit es sich optisch weiterhin
         * als EIN zusammenhängender Block liest statt wie mehrere
         * getrennte Elemente.
         */
        #hint-header {
            font-weight: 700;
            margin-bottom: 3px;
        }

        #hint-nav {
            display: flex;
            gap: 8px;
            margin-top: 6px;
        }

        #hint-nav button {
            flex: 1;
            background: rgba(var(--warning-rgb), 0.15);
            border: 1px solid var(--warning);
            color: var(--warning);
            border-radius: 6px;
            padding: 4px 8px;
            font-size: .72rem;
            font-weight: 600;
            cursor: pointer;
        }

        #hint-nav button:hover:not(:disabled) {
            background: rgba(var(--warning-rgb), 0.28);
        }

        #hint-nav button:disabled {
            opacity: .35;
            cursor: default;
        }

        /* Mobile Variante von #hint-header/#hint-nav (siehe dort) -
           #mob-hint-box selbst bleibt bewusst inline gestylt (siehe
           HTML), nur die neuen Navigations-Elemente hier. */
        #mob-hint-header {
            font-weight: 700;
            margin-bottom: 2px;
        }

        #mob-hint-nav {
            display: flex;
            gap: 6px;
            margin-top: 5px;
        }

        #mob-hint-nav button {
            flex: 1;
            background: rgba(var(--warning-rgb), 0.15);
            border: 1px solid var(--warning);
            color: var(--warning);
            border-radius: 6px;
            padding: 4px 6px;
            font-size: .7rem;
            font-weight: 600;
            cursor: pointer;
        }

        #mob-hint-nav button:disabled {
            opacity: .35;
            cursor: default;
        }

        /* ── Blockly-Bereich ── */
        #blockly-wrap {
            flex: 1;
            position: relative;
            overflow: hidden;
        }

        /**
         * BUGFIX (bei echtem Test gefunden: der Togglebutton lag GENAU
         * dort, wo auch die Werkzeugkasten-Kategorien beginnen - je nach
         * Länge der längsten Kategoriebezeichnung reicht deren Breite
         * unterschiedlich weit nach rechts, weshalb eine feste
         * "daneben"-Positionierung unzuverlässig gewesen wäre. Stattdessen
         * bekommt #blocklyDiv oben einen eigenen, reservierten Streifen
         * (top:38px statt inset:0) - Blockly füllt dadurch nur noch den
         * verbleibenden Bereich darunter, der Werkzeugkasten beginnt
         * automatisch entsprechend weiter unten, und der Button hat seine
         * eigene, garantiert überlappungsfreie Zeile.
         */
        /* Nur eine Kategorie im Werkzeugkasten (siehe
           _updateSingleCategoryMode()): Kategorie-Spalte ausblenden - die
           Blockauswahl dieser Kategorie ist dauerhaft offen. Der Umschalter
           "Blockauswahl immer sichtbar" wäre dann wirkungslos. */
        #blockly-wrap.single-category-toolbox .blocklyToolbox,
        #blockly-wrap.single-category-toolbox #ws-menu,
        #primm-modal .single-category-toolbox .blocklyToolbox {
            display: none !important;
        }

        #blockly-wrap.single-category-toolbox #block-counter {
            right: 10px;
        }

        /* GEÄNDERT (v14.57, live angefordert): Blockanzahl und ☰-Menü
           schweben jetzt oben rechts ÜBER dem Programmierbereich - der
           reservierte Streifen (top:38px) entfällt. Der frühere Grund
           (Umschalt-Knopf links über den Werkzeugkasten-Kategorien) gibt
           es seit v14.55 nicht mehr; rechts oben liegen keine Blockly-
           Bedienelemente (Zoom/Papierkorb sitzen unten rechts). */
        #blocklyDiv {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: #111827;
            /* Dark: leichtes Grau statt tiefschwarz */
        }

        /* Blockly zeichnet den sichtbaren Canvas-Hintergrund als eigenes
           <rect class="blocklyMainBackground"> INNERHALB des SVG – die
           CSS-background-Farbe von #blocklyDiv (oder des <svg> selbst)
           wird davon komplett verdeckt und ist unsichtbar. Blocklys
           Theme-Manager setzt den fill-Wert dieses Rects zudem laufend
           per Inline-Style neu (Standard: weiß) – deshalb hier bewusst
           mit !important, damit unsere Farbe zuverlässig gewinnt, auch
           nach Zoom/Resize/Theme-Neuberechnungen. */
        .blocklyMainBackground {
            fill: #111827 !important;
        }

        html[data-theme="light"] .blocklyMainBackground {
            fill: #e8edf4 !important;
        }

        /**
         * BUGFIX (live gemeldet: Scrollleisten im Programmierbereich zu
         * breit im Vergleich zum Rest der App): die BREITE wird bereits
         * über Blockly.Scrollbar.scrollbarThickness in initBlockly()
         * gesetzt (SVG-Geometrie, nicht per CSS beeinflussbar) - hier
         * geht es nur noch um die FARBE, damit die schmaleren Leisten
         * in beiden Modi ausreichend Kontrast zum Arbeitsbereich-
         * Hintergrund haben (siehe .blocklyMainBackground direkt
         * darüber). !important wie beim Hintergrund auch nötig, da
         * Blocklys Theme-Manager den fill-Wert sonst laufend per
         * Inline-Style überschreibt.
         */
        .blocklyScrollbarHandle {
            fill: #475569 !important;
        }

        .blocklyScrollbarBackground:hover + .blocklyScrollbarHandle,
        .blocklyScrollbarHandle:hover {
            fill: #64748b !important;
        }

        html[data-theme="light"] .blocklyScrollbarHandle {
            fill: #94a3b8 !important;
        }

        html[data-theme="light"] .blocklyScrollbarBackground:hover + .blocklyScrollbarHandle,
        html[data-theme="light"] .blocklyScrollbarHandle:hover {
            fill: #64748b !important;
        }

        /**
         * Die Blockauswahl-Ausklappliste (.blocklyFlyout) hat einen
         * fest dunklen Hintergrund UNABHÄNGIG vom App-Theme (siehe
         * .blocklyFlyout weiter unten, rgba(9,14,26,...) ohne
         * Lightmode-Variante) - ihre Scrollleiste braucht deshalb
         * IMMER die helle Variante, auch wenn die restliche Seite
         * gerade im Lightmode ist. html[data-theme="light"] davor
         * sorgt zusätzlich für ausreichend Spezifität, damit diese
         * Regel die allgemeine Lightmode-Regel oben zuverlässig
         * überschreibt (beide nutzen !important - ohne das würde bei
         * gleicher Spezifität sonst die spätere/allgemeinere Regel
         * gewinnen).
         */
        .blocklyFlyout .blocklyScrollbarHandle,
        html[data-theme="light"] .blocklyFlyout .blocklyScrollbarHandle {
            fill: #94a3b8 !important;
        }

        .blocklyFlyout .blocklyScrollbarBackground:hover + .blocklyScrollbarHandle,
        .blocklyFlyout .blocklyScrollbarHandle:hover,
        html[data-theme="light"] .blocklyFlyout .blocklyScrollbarBackground:hover + .blocklyScrollbarHandle,
        html[data-theme="light"] .blocklyFlyout .blocklyScrollbarHandle:hover {
            fill: #cbd5e1 !important;
        }

        /**
         * BUGFIX (live gemeldet: "die Anzeigeleiste für die Anzahl der
         * verwendeten Blöcke ... soll auf gleicher Höhe (y-Richtung) wie
         * der Blockauswahl-Toggle-Button sein"): top (vorher 10px) und
         * das vertikale Padding (vorher 5px) wichen bisher leicht von
         * #flyout-autoclose-toggle (top:6px, Padding 4px) ab - beide
         * Werte hier jetzt exakt angeglichen.
         */
        #block-counter {
            position: absolute;
            top: 6px;
            right: 10px;
            background: var(--clr-surface);
            color: var(--clr-text);
            padding: 4px 12px;
            border-radius: 999px;
            font-size: .76rem;
            z-index: 10;
            display: flex;
            align-items: center;
            gap: 8px;
            backdrop-filter: blur(6px);
            border: 1px solid var(--border-color);
            box-shadow: var(--shadow);
            pointer-events: none;
        }

        /**
         * GEÄNDERT (v14.55, live angefordert): der frühere Umschalter
         * "Blockauswahl immer sichtbar/verschwindet" links über dem
         * Werkzeugkasten ist jetzt eine Checkbox in einem ☰-Menü rechts
         * neben der Blockanzahl (#ws-menu, gleiche Höhe wie #block-counter,
         * Mechanik wie das ☰-Menü des Ausgabebereichs). #block-counter
         * rückt dafür nach links; wo das Menü ausgeblendet ist
         * (Basisansicht, nur eine Kategorie, Smartphone), steht der
         * Zähler wieder ganz rechts.
         */
        #block-counter {
            right: 44px;
        }

        #ws-menu {
            position: absolute;
            top: 6px;
            right: 10px;
            z-index: 11;
            margin-left: 0;
        }

        #counter-bar {
            width: 60px;
            height: 4px;
            background: var(--border-color);
            border-radius: 2px;
            overflow: hidden;
        }

        #counter-fill {
            height: 100%;
            background: var(--success);
            border-radius: 2px;
            transition: width .3s, background .3s;
        }

        #counter-label {
            font-size: .66rem;
            min-width: 56px;
        }

        #debug-overlay {
            position: absolute;
            bottom: 10px;
            left: 10px;
            right: 10px;
            background: rgba(15, 23, 42, 0.97);
            color: #f1f5f9;
            padding: 10px 14px;
            border-radius: 10px;
            font-size: .82rem;
            /* WICHTIG (Bugfix): Blocklys eigener Werkzeugkasten
               (.blocklyToolbox) liegt links am Rand der Werkstatt und
               hat einen eigenen z-index von 70 - schwebt dadurch über
               dem hier verwendeten z-index:10 und deckte das Overlay am
               unteren linken Rand teilweise ab ("nicht voll sichtbar").
               Gleiches Problem wie zuvor bei .panel-collapse-btn (siehe
               dortigen Kommentar) - hier mit demselben, ausreichend
               hohen z-index gelöst. */
            z-index: 500;
            display: none;
            gap: 4px;
            flex-direction: column;
            border: 2px solid var(--warning);
            backdrop-filter: blur(6px);
            box-shadow: 0 4px 18px rgba(0, 0, 0, .45);
            max-width: 560px;
        }

        #debug-overlay.active {
            display: flex;
        }

        #debug-overlay .dbg-top-row {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .dbg-info {
            color: var(--warning);
            font-weight: 800;
            font-size: .88rem;
        }

        #dbg-next {
            color: #f1f5f9 !important;
            font-size: .82rem !important;
            font-weight: 600;
            max-width: none !important;
            overflow: visible !important;
            white-space: normal !important;
            text-overflow: unset !important;
            line-height: 1.4;
        }

        /* ── Control-Bar ── */
        #control-bar {
            background: var(--clr-bg);
            padding: 6px 12px;
            display: flex;
            align-items: center;
            gap: 6px;
            border-top: 1px solid var(--border-color);
            flex-shrink: 0;
            flex-wrap: wrap;
        }

        .ctrl-btn {
            padding: 5px 13px;
            height: 32px;
            box-sizing: border-box;
            line-height: 1;
            border: 1px solid var(--border-color);
            border-radius: 999px;
            cursor: pointer;
            font-size: .78rem;
            font-weight: 700;
            display: flex;
            align-items: center;
            gap: 4px;
            background: var(--box-bg);
            color: var(--clr-text);
            transition: var(--transition-fast);
        }

        .ctrl-btn:hover {
            transform: translateY(-1px);
            box-shadow: var(--shadow-sm);
        }

        .ctrl-btn:active {
            transform: scale(.97);
        }

        .ctrl-btn:disabled {
            opacity: .35;
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
        }

        /* Geschwindigkeitsregler neben "▶ Ausführen" (siehe setExecSpeed()/
           _speedToStepDelayMs() im Skript) - steuert das Tempo der
           animierten Wiedergabe von Turtle-Bewegung UND Konsolen-
           Textausgabe gemeinsam. */
        .exec-speed-wrap {
            display: flex;
            align-items: center;
            gap: 5px;
            height: 32px;
            padding: 0 10px;
            border: 1px solid var(--border-color);
            border-radius: 999px;
            background: var(--box-bg);
        }

        .exec-speed-icon {
            font-size: .78rem;
            line-height: 1;
            opacity: .8;
        }

        .exec-speed-wrap input[type="range"] {
            width: 74px;
            accent-color: var(--clr-accent);
            cursor: pointer;
        }

        .exec-speed-wrap-mob {
            padding: 0 6px;
            height: 30px;
        }

        .exec-speed-wrap-mob input[type="range"] {
            width: 52px;
        }

        .btn-run {
            background: rgba(var(--success-rgb), 0.03);
            border-color: var(--success);
            color: var(--success);
        }

        /* v14.58: Ausführen-Knopf im Zurücksetzen-Modus (nach dem Start
           eines Programms, siehe _setRunResetMode()) - neutral statt grün,
           damit klar ist, dass jetzt NICHT erneut ausgeführt wird. */
        .btn-run.reset-mode,
        .mob-ctrl-btn.btn-run.reset-mode {
            background: rgba(127, 127, 127, 0.08);
            border-color: var(--clr-text);
            color: var(--clr-text);
        }

        .btn-debug {
            background: rgba(var(--warning-rgb), 0.05);
            border-color: var(--warning);
            color: var(--warning);
        }

        .btn-step {
            background: rgba(var(--clr-accent2-rgb), 0.12);
            border-color: var(--clr-accent2);
            color: var(--clr-accent2);
        }

        .btn-stop {
            background: rgba(var(--danger-rgb), 0.12);
            border-color: var(--danger);
            color: var(--danger);
        }

        .btn-clear {
            background: var(--box-bg);
            border-color: var(--border-color);
            color: var(--clr-muted);
        }

        .btn-hint {
            background: rgba(167, 139, 250, 0.12);
            border-color: var(--hint-purple);
            color: var(--hint-purple);
        }

        .btn-code {
            background: rgba(var(--clr-accent2-rgb), 0.08);
            border-color: rgba(var(--clr-accent2-rgb), 0.3);
            color: var(--clr-accent2);
        }

        .btn-solution {
            background: rgba(var(--danger-rgb), 0.07);
            border-color: var(--danger);
            color: var(--danger);
        }

        /* ── Right Panel ── */
        #right-panel {
            width: var(--right-panel-width, 255px);
            background: var(--clr-bg);
            border-left: 1px solid var(--border-color);
            display: flex;
            flex-direction: column;
            flex-shrink: 0;
            overflow: hidden;
        }

        .panel-head {
            padding: 6px 11px;
            background: var(--clr-surface);
            color: var(--clr-muted);
            font-size: .68rem;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: 1px;
            border-bottom: 1px solid var(--border-color);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .panel-head button {
            background: none;
            border: none;
            color: var(--clr-muted);
            cursor: pointer;
            font-size: .76rem;
            transition: color .2s;
            padding: 0;
        }

        .panel-head button:hover {
            color: var(--clr-accent);
        }

        /* ── Schriftgrößen-Regler (Variablen/Konsole/Code) ──
           Kleine, unaufdringliche +/- Buttons im jeweiligen Panel-Kopf -
           die eingestellte Größe wird pro Bereich als CSS-Variable
           gesetzt (--var-font-size/--console-font-size/--code-font-size,
           siehe adjustPanelFontSize() im Skript) und in localStorage
           gemerkt, bleibt also über einen Seiten-Neustart hinweg
           erhalten. */
        .font-size-ctrl {
            display: flex;
            align-items: center;
            gap: 2px;
            margin-left: 6px;
        }

        .font-size-ctrl button {
            width: 16px;
            height: 16px;
            display: flex;
            align-items: center;
            justify-content: center;
            border: 1px solid var(--border-color);
            border-radius: 3px;
            font-size: .68rem !important;
            line-height: 1;
        }

        .font-size-ctrl button:hover {
            border-color: var(--clr-accent);
        }

        /* ── Reiter-Kopf Konsole/Code/Visualisierung (01.10.2026) ──
           Rechte Gruppe: [✕ − +] bzw. im Visualisierungs-Reiter
           [− 100% +], dahinter [⛶] - alles im Stil der
           Schriftgrößen-Knöpfe oben. */
        .output-head-actions {
            display: flex;
            align-items: center;
            gap: 3px;
            margin-left: 4px;
            flex-shrink: 0;
        }

        .output-head-actions .font-size-ctrl {
            margin-left: 0;
        }

        .font-size-ctrl-label {
            font-size: .6rem;
            min-width: 28px;
            text-align: center;
            color: var(--clr-muted);
            text-transform: none;
            letter-spacing: normal;
        }

        /* ── ☰-Menü im Simulationskopf (01.10.2026) ──
           Außerhalb des Vollbilds stecken alle Simulations-Knöpfe in einem
           aufklappbaren Menü; die Beschriftung jedes Eintrags kommt aus
           seinem title-Attribut (bleibt bei Umschaltern mit wechselndem
           Titel automatisch aktuell). Im Simulations-Vollbild (weiter
           unten) bleibt die große Knopfleiste wie bisher direkt sichtbar. */
        .sim-menu {
            position: relative;
            display: flex;
            align-items: center;
            margin-left: auto;
        }

        .panel-head .sim-menu-toggle {
            width: 22px;
            height: 20px;
            display: flex;
            align-items: center;
            justify-content: center;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            font-size: .82rem;
            line-height: 1;
        }

        .panel-head .sim-menu-toggle:hover,
        .sim-menu.open .sim-menu-toggle {
            border-color: var(--clr-accent);
            color: var(--clr-accent);
        }

        /* .sim-menu als Präfix: muss die (weiter unten stehende, gleich
           spezifische) Regel .panel-head-btn-group { display: flex }
           schlagen, sonst wäre das Menü dauerhaft aufgeklappt. */
        .sim-menu .sim-menu-panel {
            display: none;
            position: absolute;
            top: calc(100% + 6px);
            right: 0;
            z-index: 400;
            flex-direction: column;
            align-items: stretch;
            gap: 1px;
            min-width: 240px;
            padding: 4px;
            background: var(--clr-surface);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            box-shadow: var(--shadow-lg);
            text-transform: none;
            letter-spacing: normal;
        }

        .sim-menu.open .sim-menu-panel {
            display: flex;
        }

        /* v15.59 (iPad-Screenshot 05.11.2026): #right-panel schneidet ab (overflow: hidden) - das
           rechtsbündige, mind. 240 px breite Menü ragte bei schmalem Ausgabebereich links hinaus.
           Breite höchstens Bereichsbreite (#right-panel ist ein Container -> cqw); die Höhe begrenzt
           _fitSimMenuPanel() beim Öffnen (dann scrollt das Menü). */
        #right-panel .sim-menu .sim-menu-panel {
            box-sizing: border-box;
            min-width: min(240px, calc(100cqw - 16px));
            max-width: calc(100cqw - 16px);
        }

        /* Konsolen-Fenster: linksbündig wie die eingebettete Konsole
           (.modal-box zentriert Text standardmäßig). */
        #console-modal-output {
            text-align: left;
        }

        .panel-head .sim-menu-panel > button {
            /* feste Symbolspalte: Beschriftungen beginnen bündig, egal wie breit das Emoji ist */
            display: grid;
            grid-template-columns: 22px 1fr;
            justify-items: start;
            align-items: center;
            gap: 8px;
            width: 100%;
            padding: 6px 8px;
            border-radius: 6px;
            text-align: left;
            color: var(--clr-text);
            font-size: .82rem;
        }

        .sim-menu-panel > button::after {
            content: attr(title);
            font-size: .74rem;
            font-weight: 600;
        }

        .panel-head .sim-menu-panel > button:hover {
            background: var(--clr-bg2, rgba(127, 127, 127, .12));
            color: var(--clr-text);
        }

        .panel-head .sim-menu-panel > button[aria-pressed="true"] {
            color: var(--clr-accent);
        }

        /**
         * NEU (04.10.2026): Bereiche unter der Simulation per Checkbox im
         * ☰-Menü ein-/ausblenden (siehe setOutputAreaVisible() in
         * funci.html). Abschnitts-Überschrift + Checkbox-Zeilen im Stil der
         * übrigen Menüeinträge (gleiche Symbolspalte).
         */
        /* gleiche Schrift wie die Menü-Knöpfe (Browser-Standard für <button>, sonst erbt das Label die Serifenschrift der Seite) */
        .sim-menu-panel .sim-menu-section,
        .sim-menu-panel .sim-menu-check {
            font-family: Arial, Helvetica, sans-serif;
        }

        .sim-menu-panel .sim-menu-section {
            margin: 4px 0 0;
            padding: 6px 8px 2px;
            border-top: 1px solid var(--border-color);
            font-size: .66rem;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: .04em;
            color: var(--clr-muted);
        }

        .sim-menu-panel .sim-menu-check {
            display: grid;
            grid-template-columns: 22px 1fr;
            align-items: center;
            gap: 8px;
            padding: 5px 8px;
            border-radius: 6px;
            color: var(--clr-text);
            font-size: .74rem;
            font-weight: 600;
            cursor: pointer;
        }

        .sim-menu-panel .sim-menu-check:hover {
            background: var(--clr-bg2, rgba(127, 127, 127, .12));
        }

        .sim-menu-panel .sim-menu-check input {
            margin: 0;
            justify-self: center;
            accent-color: var(--clr-accent);
            cursor: pointer;
        }

        /* im Simulations-Vollbild (Knopfleiste) ohne Bedeutung */
        #sim-focus-wrap:fullscreen .sim-menu-out,
        body.hide-out-console .sim-menu-out,
        #sim-focus-wrap:fullscreen .sim-menu-section,
        #sim-focus-wrap:fullscreen .sim-menu-check,
        #mob-sim-focus-wrap:fullscreen .sim-menu-section,
        #mob-sim-focus-wrap:fullscreen .sim-menu-check {
            display: none !important;
        }

        body.hide-out-inventory #grid-inventory-wrap,
        body.hide-out-inventory #mob-grid-inventory-wrap,
        body.hide-out-vars .out-area-vars,
        body.hide-out-console .out-area-console {
            display: none !important;
        }

        /* ohne Konsolen-Bereich füllt die Simulation den frei werdenden Platz (Desktop) */
        body.hide-out-console #sim-focus-wrap {
            flex: 1;
            min-height: 0;
        }

        body.hide-out-console #sim-canvas-wrap,
        body.hide-out-console #grid-canvas-wrap {
            flex: 1;
            height: auto;
            min-height: 0;
        }

        body.hide-out-console #sim-resize-handle {
            display: none;
        }

        /**
         * NEU (05.10.2026, v14.37): Nur-Text-Aufgaben zeigen statt der
         * Simulation nur deren Kopfzeile ("Textausgabe") - das ☰-Menü dort
         * enthält nur die Bereichs-Checkboxen (siehe _applyTaskTypeUI() in
         * funci.html). Die Kopfzeile wächst nie mit, auch nicht bei
         * ausgeblendetem Konsolen-Bereich.
         */
        /* v14.55: auf die beiden Simulations-Menüs beschränkt - die
           ☰-Menüs im Aufgabenkopf/Programmierbereich nutzen dieselben
           Klassen, ihre Einträge (z.B. "Einklappen") müssen bei
           Text-Aufgaben sichtbar bleiben. */
        body.task-type-text #sim-menu-panel > :not(.sim-menu-section):not(.sim-menu-check):not(.sim-menu-out),
        body.task-type-text #mob-sim-menu-panel > :not(.sim-menu-section):not(.sim-menu-check):not(.sim-menu-out) {
            display: none !important;
        }

        body.task-type-text .sim-menu-panel .sim-menu-section {
            margin-top: 0;
            border-top: none;
        }

        body.task-type-text #sim-focus-wrap,
        body.task-type-text #mob-sim-focus-wrap {
            flex: none !important;
        }

        #sim-focus-wrap:fullscreen .sim-menu-toggle,
        #mob-sim-focus-wrap:fullscreen .sim-menu-toggle {
            display: none;
        }

        #sim-focus-wrap:-webkit-full-screen .sim-menu-toggle,
        #mob-sim-focus-wrap:-webkit-full-screen .sim-menu-toggle {
            display: none;
        }

        #sim-focus-wrap:fullscreen .sim-menu-panel,
        #mob-sim-focus-wrap:fullscreen .sim-menu-panel {
            display: flex;
            position: static;
            flex-direction: row;
            align-items: center;
            min-width: 0;
            padding: 0;
            background: none;
            border: none;
            box-shadow: none;
        }

        #sim-focus-wrap:-webkit-full-screen .sim-menu-panel,
        #mob-sim-focus-wrap:-webkit-full-screen .sim-menu-panel {
            display: flex;
            position: static;
            flex-direction: row;
            align-items: center;
            min-width: 0;
            padding: 0;
            background: none;
            border: none;
            box-shadow: none;
        }

        #sim-focus-wrap:fullscreen .sim-menu-panel > button,
        #mob-sim-focus-wrap:fullscreen .sim-menu-panel > button {
            display: flex;
            width: auto;
            justify-content: center;
        }

        #sim-focus-wrap:-webkit-full-screen .sim-menu-panel > button,
        #mob-sim-focus-wrap:-webkit-full-screen .sim-menu-panel > button {
            display: flex;
            width: auto;
            justify-content: center;
        }

        #sim-focus-wrap:fullscreen .sim-menu-panel > button::after,
        #mob-sim-focus-wrap:fullscreen .sim-menu-panel > button::after {
            content: none;
        }

        #sim-focus-wrap:-webkit-full-screen .sim-menu-panel > button::after,
        #mob-sim-focus-wrap:-webkit-full-screen .sim-menu-panel > button::after {
            content: none;
        }

        .panel-head button:disabled {
            opacity: .35;
            cursor: not-allowed;
            color: var(--clr-muted);
        }

        .panel-head button:disabled:hover {
            color: var(--clr-muted);
        }

        .panel-head-btn-group {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        /**
         * NEU (live gemeldet: "Drop-Down-Menü ... im Simulationsbereich"):
         * ersetzt bei Grid-Aufgaben MIT Testwelten den bisherigen reinen
         * Text-Titel (#sim-panel-title/#mob-sim-panel-title) - erbt
         * bewusst dieselbe Groß-/Kleinschreibung/Größe von .panel-head
         * (siehe oben), damit sie sich NICHT vom übrigen Panel-Kopf
         * abhebt, wirkt aber optisch trotzdem klar als Auswahlfeld
         * (dezenter Rahmen statt reinem Fließtext).
         */
        .sim-world-select {
            background: var(--clr-surface);
            color: inherit;
            font: inherit;
            text-transform: inherit;
            letter-spacing: inherit;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            padding: 2px 6px;
            cursor: pointer;
            /* GEÄNDERT (v14.41, live gemeldet: "Das Dropdown-Menü ist nicht
               breit genug, wenn die Standard-Welt einen Haken bekommt.
               Passe das Menü dynamisch der Breite an"): statt fester
               Obergrenze (55%, schnitt "🗺️ Standard-Welt ✅" ab) richtet
               sich die Breite nach dem gewählten Eintrag (field-sizing,
               ohne Unterstützung: nach dem breitesten Eintrag) und darf
               den Kopf bis auf das ☰-Menü ausfüllen; nur wenn auch das
               nicht reicht, schrumpft es (min-width: 0). */
            field-sizing: content;
            flex: 0 1 auto;
            min-width: 0;
            max-width: 100%;
        }

        .sim-world-select:hover,
        .sim-world-select:focus-visible {
            border-color: var(--clr-accent);
        }

        /* NEU (v14.38): während eines "Alle Welten"-Durchlaufs zeigt das
           Dropdown die gerade geprüfte Welt farbig hinterlegt (siehe
           _allWorldsRunActive in funci.html) */
        .sim-world-select.sim-world-select-running {
            background: var(--clr-accent);
            border-color: var(--clr-accent);
            color: var(--clr-on-accent);
            font-weight: 700;
            animation: sim-world-running-pulse 1.2s ease-in-out infinite;
        }

        .sim-world-select.sim-world-select-running option {
            background: var(--clr-surface);
            color: var(--clr-text);
            font-weight: normal;
        }

        @keyframes sim-world-running-pulse {
            50% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--clr-accent) 35%, transparent); }
        }

        /* NEU (v14.38): Weltwechsel im Grid-Canvas - alte Welt (Kopie)
           gleitet nach links hinaus, neue Welt von rechts herein (siehe
           _slideGridWorlds() in funci.html) */
        .grid-world-sliding {
            overflow: hidden;
        }

        .grid-world-slide-out {
            position: absolute;
            pointer-events: none;
            z-index: 1;
        }

        @media (prefers-reduced-motion: reduce) {
            .sim-world-select.sim-world-select-running {
                animation: none;
            }
        }

        /* ── Simulation-Fokus-Modus (Vollbild nur für die Simulation) ──
           Nutzt dieselbe Fullscreen-API wie der bestehende Vollbild-
           Knopf für die ganze Seite (siehe toggleFullscreen()), hier
           aber gezielt auf #sim-focus-wrap/#mob-sim-focus-wrap
           angewendet - dadurch bleibt der Rest der Seite (Blöcke,
           Aufgabenbereich) unverändert im Hintergrund, nur die
           Simulation selbst wird bildschirmfüllend dargestellt.
           Funktioniert dadurch identisch auf Desktop UND Mobile, ohne
           Popup-Fenster (siehe Begründung in der Konzept-Besprechung). */
        #sim-focus-wrap,
        #mob-sim-focus-wrap {
            display: flex;
            flex-direction: column;
            min-height: 0;
        }

        #sim-focus-wrap:fullscreen,
        #mob-sim-focus-wrap:fullscreen {
            background: #0e1a2b;
            padding: 0;
            /**
             * BUGFIX (live gemeldet: "in Firefox wird nur das obere
             * Bildschirmviertel genutzt, in Chromium der volle
             * Bildschirm"): hier stand bisher NUR eine explizite Höhe,
             * keine explizite Breite. Chromiums User-Agent-Stylesheet
             * lässt ein :fullscreen-Element automatisch auf 100% Breite
             * UND Höhe wachsen, auch ohne dass die Seite selbst eine
             * Breite vorgibt - Firefox verlässt sich dagegen stärker auf
             * das Autor-CSS und behält ohne explizite Breite die
             * ursprüngliche (normale Panel-)Breite bei, während nur die
             * Höhe wächst. Jetzt beides explizit gesetzt, wie es die
             * Fullscreen-API ohnehin erwarten lässt.
             */
            width: 100vw;
            width: 100dvw;
            height: 100vh;
            height: 100dvh;
            margin: 0;
            max-width: none;
        }

        /**
         * BUGFIX (live gemeldet: "Chromium respektiert im Vollbild nicht
         * das gewählte Light-Theme"): der Hintergrund oben war fest auf
         * die dunkle Farbe codiert, unabhängig vom Theme - dieselbe
         * helle Farbe wie beim Canvas-Inhalt selbst (siehe
         * drawCanvas()/drawMobCanvas(): "isDark ? '#0e1a2b' : '#f5f7fa'")
         * jetzt auch hier für das Light-Theme ergänzt.
         */
        html[data-theme="light"] #sim-focus-wrap:fullscreen,
        html[data-theme="light"] #mob-sim-focus-wrap:fullscreen {
            background: #f5f7fa;
        }
        #sim-focus-wrap:-webkit-full-screen,
        #mob-sim-focus-wrap:-webkit-full-screen {
            background: #0e1a2b;
            padding: 0;
            /**
             * BUGFIX (live gemeldet: "in Firefox wird nur das obere
             * Bildschirmviertel genutzt, in Chromium der volle
             * Bildschirm"): hier stand bisher NUR eine explizite Höhe,
             * keine explizite Breite. Chromiums User-Agent-Stylesheet
             * lässt ein :fullscreen-Element automatisch auf 100% Breite
             * UND Höhe wachsen, auch ohne dass die Seite selbst eine
             * Breite vorgibt - Firefox verlässt sich dagegen stärker auf
             * das Autor-CSS und behält ohne explizite Breite die
             * ursprüngliche (normale Panel-)Breite bei, während nur die
             * Höhe wächst. Jetzt beides explizit gesetzt, wie es die
             * Fullscreen-API ohnehin erwarten lässt.
             */
            width: 100vw;
            width: 100dvw;
            height: 100vh;
            height: 100dvh;
            margin: 0;
            max-width: none;
        }

        html[data-theme="light"] #sim-focus-wrap:-webkit-full-screen,
        html[data-theme="light"] #mob-sim-focus-wrap:-webkit-full-screen {
            background: #f5f7fa;
        }

        #sim-focus-wrap:fullscreen #sim-canvas-wrap,
        #mob-sim-focus-wrap:fullscreen #mob-sim-wrap {
            flex: 1;
            height: auto;
            min-height: 0;
        }

        #sim-focus-wrap:-webkit-full-screen #sim-canvas-wrap,
        #mob-sim-focus-wrap:-webkit-full-screen #mob-sim-wrap {
            flex: 1;
            height: auto;
            min-height: 0;
        }/* BUGFIX: dieselbe Höhenanpassung fehlte bisher komplett für den
           Grid-Canvas - er behielt dadurch im Vollbild-Modus seine feste
           185px/160px-Grundhöhe (siehe #grid-canvas-wrap weiter unten),
        während die Breite sich (über die volle Fenstergröße von
           #sim-focus-wrap) trotzdem streckte - daraus resultierte genau
           das gemeldete "füllt die Breite,
        aber nicht die Höhe". */
        #sim-focus-wrap:fullscreen #grid-canvas-wrap,
        #mob-sim-focus-wrap:fullscreen #mob-grid-canvas-wrap {
            flex: 1;
            height: auto;
            min-height: 0;
        }

        #sim-focus-wrap:-webkit-full-screen #grid-canvas-wrap,
        #mob-sim-focus-wrap:-webkit-full-screen #mob-grid-canvas-wrap {
            flex: 1;
            height: auto;
            min-height: 0;
        }

        #sim-focus-wrap:fullscreen .panel-head,
        #mob-sim-focus-wrap:fullscreen .panel-head {
            background: rgba(14, 26, 43, .9);
            flex-shrink: 0;
        }

        #sim-focus-wrap:-webkit-full-screen .panel-head,
        #mob-sim-focus-wrap:-webkit-full-screen .panel-head {
            background: rgba(14, 26, 43, .9);
            flex-shrink: 0;
        }

        html[data-theme="light"] #sim-focus-wrap:fullscreen .panel-head,
        html[data-theme="light"] #mob-sim-focus-wrap:fullscreen .panel-head {
            background: rgba(245, 247, 250, .92);
        }

        html[data-theme="light"] #sim-focus-wrap:-webkit-full-screen .panel-head,
        html[data-theme="light"] #mob-sim-focus-wrap:-webkit-full-screen .panel-head {
            background: rgba(245, 247, 250, .92);
        }

        /* Steuerleiste (Ausführen, Koordinatensystem, ...) im Vollbild-
           Modus spürbar größer - auf dem großen Bildschirm/aus größerer
           Entfernung (Beamer im Klassenzimmer!) sonst schwer zu treffen/
           zu lesen. Gilt für alle Simulationsarten (Turtle UND Grid),
        da
           beide dieselbe #sim-focus-wrap/#mob-sim-focus-wrap-Hülle und
           denselben .panel-head-btn-group nutzen. */
        #sim-focus-wrap:fullscreen .panel-head,
        #mob-sim-focus-wrap:fullscreen .panel-head {
            padding: 14px 18px;
        }

        #sim-focus-wrap:-webkit-full-screen .panel-head,
        #mob-sim-focus-wrap:-webkit-full-screen .panel-head {
            padding: 14px 18px;
        }

        #sim-focus-wrap:fullscreen .panel-head span:first-child,
        #mob-sim-focus-wrap:fullscreen .panel-head span:first-child {
            font-size: 1.15rem;
        }

        #sim-focus-wrap:-webkit-full-screen .panel-head span:first-child,
        #mob-sim-focus-wrap:-webkit-full-screen .panel-head span:first-child {
            font-size: 1.15rem;
        }

        #sim-focus-wrap:fullscreen .panel-head-btn-group,
        #mob-sim-focus-wrap:fullscreen .panel-head-btn-group {
            gap: 12px;
        }

        #sim-focus-wrap:-webkit-full-screen .panel-head-btn-group,
        #mob-sim-focus-wrap:-webkit-full-screen .panel-head-btn-group {
            gap: 12px;
        }

        #sim-focus-wrap:fullscreen .panel-head-btn-group button,
        #mob-sim-focus-wrap:fullscreen .panel-head-btn-group button {
            font-size: 1.5rem;
            min-width: 44px;
            min-height: 44px;
            padding: 8px 10px;
        }

        #sim-focus-wrap:-webkit-full-screen .panel-head-btn-group button,
        #mob-sim-focus-wrap:-webkit-full-screen .panel-head-btn-group button {
            font-size: 1.5rem;
            min-width: 44px;
            min-height: 44px;
            padding: 8px 10px;
        }

        /* Geschwindigkeitsregler neben dem Ausführen-Knopf: existiert nur
           EIN zusätzliches Exemplar pro Ansicht (Desktop/Mobile), extra
           für den Vollbildmodus - dort ist die reguläre Steuerleiste
           (#control-bar) ja nicht sichtbar (liegt außerhalb von
           #sim-focus-wrap). Bleibt außerhalb des Vollbilds unsichtbar,
           um die ohnehin schon kompakte normale Kopfzeile nicht zu
           überladen - der Regler in #control-bar deckt diesen Fall
           weiterhin ab. setExecSpeed() hält beide Exemplare synchron. */
        .sim-focus-only-control {
            display: none;
        }

        #sim-focus-wrap:fullscreen .sim-focus-only-control,
        #mob-sim-focus-wrap:fullscreen .sim-focus-only-control {
            display: flex;
            height: 44px;
            padding: 0 14px;
        }

        #sim-focus-wrap:-webkit-full-screen .sim-focus-only-control,
        #mob-sim-focus-wrap:-webkit-full-screen .sim-focus-only-control {
            display: flex;
            height: 44px;
            padding: 0 14px;
        }

        #sim-focus-wrap:fullscreen .sim-focus-only-control input[type="range"],
        #mob-sim-focus-wrap:fullscreen .sim-focus-only-control input[type="range"] {
            width: 110px;
        }

        #sim-focus-wrap:-webkit-full-screen .sim-focus-only-control input[type="range"],
        #mob-sim-focus-wrap:-webkit-full-screen .sim-focus-only-control input[type="range"] {
            width: 110px;
        }

        #sim-focus-wrap:fullscreen .sim-focus-only-control .exec-speed-icon,
        #mob-sim-focus-wrap:fullscreen .sim-focus-only-control .exec-speed-icon {
            font-size: 1.1rem;
        }

        #sim-focus-wrap:-webkit-full-screen .sim-focus-only-control .exec-speed-icon,
        #mob-sim-focus-wrap:-webkit-full-screen .sim-focus-only-control .exec-speed-icon {
            font-size: 1.1rem;
        }

        .sim-focus-btn .fs-exit {
            display: none;
        }

        #sim-focus-wrap:fullscreen .sim-focus-btn .fs-enter,
        #mob-sim-focus-wrap:fullscreen .sim-focus-btn .fs-enter {
            display: none;
        }

        #sim-focus-wrap:-webkit-full-screen .sim-focus-btn .fs-enter,
        #mob-sim-focus-wrap:-webkit-full-screen .sim-focus-btn .fs-enter {
            display: none;
        }

        #sim-focus-wrap:fullscreen .sim-focus-btn .fs-exit,
        #mob-sim-focus-wrap:fullscreen .sim-focus-btn .fs-exit {
            display: inline;
        }

        #sim-focus-wrap:-webkit-full-screen .sim-focus-btn .fs-exit,
        #mob-sim-focus-wrap:-webkit-full-screen .sim-focus-btn .fs-exit {
            display: inline;
        }

        #sim-canvas-wrap {
            height: var(--sim-area-height, 185px);
            background: #0e1a2b;
            /* Dark-Fallback – passt zu drawCanvas() */
            border-bottom: 1px solid var(--border-color);
            flex-shrink: 0;
            cursor: grab;
            position: relative;
        }

        /* Grid-Welt-Renderer (Aufgabentyp "grid", Phase 1) - dieselben
           Maße/Eigenschaften wie #sim-canvas-wrap, aber ein eigenes,
           separates Element statt denselben Canvas wiederzuverwenden -
           hält drawCanvas() (Turtle) und drawGridCanvas() (Grid) sauber
           getrennt, statt eine Fallunterscheidung in eine gemeinsame
           Zeichenfunktion einzubauen. Sichtbarkeit wird von
           _applyTaskTypeUI() gesteuert. */
        #grid-canvas-wrap {
            height: var(--sim-area-height, 185px);
            background: #0e1a2b;
            border-bottom: 1px solid var(--border-color);
            flex-shrink: 0;
            cursor: grab;
            position: relative;
        }

        /**
         * BUGFIX (live gemeldet: "sobald man ... die Zeilen- und
         * Spaltenkoordinaten anschaltet, werden die Grid-Welt und die
         * Objekte darauf unschärfer"): fehlte hier bisher - im
         * Gegensatz zu #simCanvas (Turtle) und
         * #mob-grid-canvas-wrap canvas (mobile Grid-Welt), die BEIDE
         * bereits width:100%/height:100%/display:block haben, war die
         * CSS-ANZEIGEGRÖSSE des Desktop-Grid-Canvas bisher NICHT fest
         * an seinen Wrapper gebunden - sie ergab sich stattdessen indirekt
         * aus canvas.width/height (den für die interne Auflösung
         * gedachten HTML-Attributen, siehe _fitCanvasToContainer()),
         * ein fragiler, selbstbezüglicher Kreislauf: canvas.width
         * bestimmt (mangels Gegen-CSS) zugleich die sichtbare Größe,
         * canvas.width wird aber wiederum aus der GELESENEN sichtbaren
         * Größe berechnet. Auf Geräten mit devicePixelRatio > 1 (die
         * Mehrheit heute) driftete das bei jedem erneuten Einpass-
         * Vorgang (siehe toggleCoordSystem() -> _fitAndCenterGridWorld())
         * leicht auseinander - die interne Auflösung passte danach
         * nicht mehr exakt zur tatsächlichen Anzeigegröße, wodurch der
         * Browser das bereits fertige Canvas-Bild sichtbar weichzeichnend
         * skalieren musste. Jetzt fest auf 100%/100% seines Wrappers
         * gebunden, exakt wie bei den beiden bereits korrekten Vorbildern
         * oben - die interne Auflösung bleibt dadurch unabhängig von der
         * Anzeigegröße immer exakt synchron.
         */
        #gridCanvas {
            width: 100%;
            height: 100%;
            display: block;
        }

        /* Zeigt "(x|y)" oben rechts über der Simulation, solange das
           Koordinatensystem eingeschaltet ist (siehe toggleCoordSystem()/
           _updateCoordReadout()). Als eigenständiges HTML-Element statt
           direkt auf den Canvas gezeichnet, damit es unabhängig vom
           Zoom-/Pan-Transform des Canvas (siehe drawCanvas()) immer an
           derselben Bildschirm-Position und -Größe bleibt. */
        .sim-coord-readout {
            position: absolute;
            top: 6px;
            right: 8px;
            font: 700 .72rem/1 'Consolas', monospace;
            color: rgba(255, 255, 255, .82);
            background: rgba(14, 26, 43, .55);
            padding: 3px 7px;
            border-radius: 5px;
            pointer-events: none;
            display: none;
            z-index: 2;
        }

        .sim-coord-readout.show {
            display: block;
        }

        /* Verbreiterter Ausgabebereich (510px statt 255px, siehe
           toggleRightPanelWidth()): die Simulation wird nicht einfach
           in die Breite gezogen (verzerrtes Bild), sondern behält ihr
           Höhen-/Breiten-Verhältnis bei - die Höhe verdoppelt sich
           exakt im selben Maß wie die Breite (185px -> 370px, genau wie
           255px -> 510px). Variablen/Ausgabe darunter rutschen dadurch
           automatisch weiter nach unten und bleiben dank ihrer bereits
           vorhandenen eigenen Scroll-Bereiche (siehe #var-display/
           #console-wrap) bedienbar, auch wenn dadurch weniger Höhe für
           sie übrig bleibt. */
        /* #layout.rightpanel-widened wurde entfernt (siehe
           _initRightPanelResizeHandle() in funci.html) - #sim-canvas-wrap
           nutzt bereits regulär height:var(--sim-area-height, 185px), das
           reicht jetzt aus. */

        #sim-canvas-wrap.sim-panning,
        #mob-sim-wrap.sim-panning {
            cursor: grabbing;
        }

        #coord-toggle-btn[aria-pressed="true"],
        #mob-coord-toggle-btn[aria-pressed="true"] {
            background: var(--clr-accent);
            color: var(--clr-on-accent);
        }

        #simCanvas {
            width: 100%;
            height: 100%;
            display: block;
        }

        /**
         * Verschiebbare VERTIKALE Leiste zwischen Programmierbereich
         * (#center, Blockly-Werkstatt) und Ausgabebereich (#right-panel)
         * - analog zu #sim-resize-handle oben, nur horizontal statt
         * vertikal ziehbar. Ersetzt die früheren separaten Knöpfe
         * "Ausgabebereich einklappen" und "Ausgabebereich verbreitern/
         * normal breit" (siehe _initRightPanelResizeHandle() in
         * funci.html) - #right-panel nutzt dafür jetzt
         * width:var(--right-panel-width, 255px) statt einer festen
         * Breite mit nur drei möglichen Zuständen.
         */
        #rightpanel-resize-handle {
            flex-shrink: 0;
            width: 9px;
            margin: 0 -1px;
            /* überlappt die 1px-Trennlinie knapp, damit kein Doppelrand entsteht */
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: col-resize;
            background: var(--clr-bg, #1a2332);
            border-left: 1px solid var(--border-color, #33415c);
            touch-action: none;
            z-index: 400;
            /* unter .panel-collapse-btn (z-index:500), aber über normalem Seiteninhalt */
        }

        #rightpanel-resize-handle:hover,
        #rightpanel-resize-handle:focus-visible,
        #rightpanel-resize-handle.dragging {
            background: rgba(var(--success-rgb), 0.12);
            outline: none;
        }

        .rightpanel-resize-grip {
            width: 4px;
            height: 36px;
            border-radius: 3px;
            background: var(--clr-muted, #64748b);
            opacity: .5;
            transition: opacity .15s, background .15s;
        }

        #rightpanel-resize-handle:hover .rightpanel-resize-grip,
        #rightpanel-resize-handle:focus-visible .rightpanel-resize-grip,
        #rightpanel-resize-handle.dragging .rightpanel-resize-grip {
            opacity: 1;
            background: var(--clr-accent);
        }

        /* Eingeklappter Ausgabebereich (siehe RIGHT_PANEL_MIN_WIDTH in
           funci.html): #right-panel selbst schrumpft auf 0, die Leiste
           bleibt als schmaler, weiterhin klar erkennbarer/ziehbarer
           Streifen am rechten Rand stehen, um wieder ausklappen zu
           können. */
        #layout.rightpanel-collapsed-drag #right-panel {
            width: 0 !important;
            min-width: 0;
            border-left-width: 0;
        }


        /**
         * live gemeldet: die Simulation soll auf Kosten von Konsole/Code
         * vergrößerbar sein (und umgekehrt), statt fest bei 185px/370px
         * zu bleiben. #sim-canvas-wrap/#grid-canvas-wrap nutzen dafür
         * jetzt height:var(--sim-area-height, 185px) statt eines festen
         * Werts (siehe dort) - #console-wrap/#embedded-code-wrap sind
         * bereits flex:1 und füllen dadurch automatisch den jeweils
         * verbleibenden Rest, ganz ohne eigene Anpassung.
         */
        #sim-resize-handle {
            flex-shrink: 0;
            height: 9px;
            margin: -1px 0;
            /* überlappt die 1px-Trennlinien knapp, damit kein Doppelrand entsteht */
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: row-resize;
            /**
             * BUGFIX (live gemeldet: "Leiste weder zu sehen noch per
             * Maus verschiebbar"): --clr-bg/--clr-muted/--border-color/
             * --clr-accent waren zum Zeitpunkt dieses Fixes in der
             * GESAMTEN App an KEINER Stelle definiert (kein :root-Block
             * vorhanden) - var(...) OHNE Rückfallwert wurde dadurch zum
             * ungültigen Wert und fiel auf den Anfangswert der
             * Eigenschaft zurück (bei background-color: transparent).
             * NACHTRAG (Code-Review Maßnahme 1): der fehlende :root-Block
             * wurde inzwischen ergänzt (siehe Dateianfang) - der hier
             * gesetzte explizite Rückfallwert ist dadurch nicht mehr
             * zwingend nötig, bleibt aber als zusätzliches Sicherheitsnetz
             * bewusst stehen (schadet nicht, greift automatisch nur dann,
             * wenn die jetzt definierte Variable aus irgendeinem Grund
             * doch einmal nicht verfügbar wäre).
             */
            background: var(--clr-bg, #1a2332);
            border-bottom: 1px solid var(--border-color, #33415c);
            touch-action: none;
            /* verhindert Scrollen der Seite während des Ziehens auf Touch-Geräten */
        }

        #sim-resize-handle:hover,
        #sim-resize-handle:focus-visible,
        #sim-resize-handle.dragging {
            background: rgba(var(--success-rgb), 0.12);
            outline: none;
        }

        .sim-resize-grip {
            width: 36px;
            height: 4px;
            border-radius: 3px;
            background: var(--clr-muted, #64748b);
            opacity: .5;
            transition: opacity .15s, background .15s;
        }

        #sim-resize-handle:hover .sim-resize-grip,
        #sim-resize-handle:focus-visible .sim-resize-grip,
        #sim-resize-handle.dragging .sim-resize-grip {
            opacity: 1;
            background: var(--clr-accent);
        }

        /**
         * GEÄNDERT (live gemeldet: "der Inventar-Bereich und der
         * Variablen-Bereich hat einen grauen Hintergrund in Light-View-
         * Style. Passe den Hintergrund an den Konsolen-Hintergrund an."):
         * rgba(0,0,0,0.2) (ein fixer, dunkel-transparenter Überzug) sah
         * im Dunkelmodus unauffällig aus, verursachte im Hellmodus aber
         * genau diesen unpassenden Grauton - #console-wrap hat GAR
         * keinen eigenen Hintergrund und zeigt dadurch direkt die
         * Grundfarbe von #right-panel (var(--clr-bg)) - genau diese
         * Variable jetzt auch hier, damit beide Bereiche optisch
         * einheitlich wirken.
         */
        #var-display {
            padding: 5px 8px;
            border-bottom: 1px solid var(--border-color);
            font-family: 'Courier New', monospace;
            font-size: var(--var-font-size, .8rem);
            min-height: 28px;
            max-height: 72px;
            overflow-y: auto;
            flex-shrink: 0;
            background: var(--clr-bg);
        }

        /* ── Grid-Welt-Inventar (Aufgabentyp "grid") ──
           Zeigt die vom Akteur aufgehobenen Gegenstände als Symbol-Reihe
           unterhalb des Simulationsbereichs, analog zur Variablenanzeige.
           Sichtbarkeit wird wie beim Grid-Canvas selbst von
           _applyTaskTypeUI() gesteuert. */
        #grid-inventory-wrap,
        #mob-grid-inventory-wrap {
            flex-shrink: 0;
        }

        #grid-inventory-list,
        #mob-grid-inventory-list {
            padding: 6px 8px;
            border-bottom: 1px solid var(--border-color);
            min-height: 28px;
            max-height: 72px;
            overflow-y: auto;
            flex-shrink: 0;
            background: var(--clr-bg);
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            align-items: center;
        }

        .grid-inventory-empty {
            color: var(--clr-muted);
            font-size: .68rem;
        }

        .grid-inventory-item {
            width: 30px;
            height: 30px;
            border-radius: 8px;
            border: 1px solid var(--border-color);
            background: var(--clr-bg2, rgba(255, 255, 255, .05));
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.1rem;
            /* v15.35: dieselbe eingebettete Emoji-Schrift wie die Grid-Welt
               (GRID_EMOJI_FONT_STACK in turtle_check_shared.js) */
            font-family: "FunciEmoji", "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
            cursor: pointer;
            transition: transform .12s, border-color .12s;
            flex-shrink: 0;
            position: relative;
        }

        /**
         * NEU (live gemeldet: "Damit die Inventarliste in funci nicht zu
         * lang wird, soll bei gleichen Gegenständen die Anzahl als
         * Stapel angezeigt werden"): kleine Zahl unten rechts auf der
         * Inventar-Kachel, analog zur bestehenden Stapel-Anzeige auf
         * Gegenstands-Feldern im Grid selbst.
         */
        .grid-inventory-count {
            position: absolute;
            bottom: -4px;
            right: -4px;
            background: #dc2626;
            color: #fff;
            /* BUGFIX v15.37 (live gemeldet, iPad: Anzahl schlecht lesbar): die
               Kachel nutzt seit v15.35 die Emoji-Schrift; die Ziffern fielen auf
               "Apple Color Emoji" zurück (Tasten-Ziffern: breit, nach unten
               versetzt, abgeschnitten) - Zahl daher immer in Textschrift. */
            font-family: Arial, Helvetica, sans-serif;
            font-size: .68rem;
            font-weight: 700;
            line-height: 1;
            letter-spacing: 0;
            font-variant-numeric: tabular-nums;
            min-width: 16px;
            height: 16px;
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 0 3px;
            pointer-events: none;
        }

        .grid-inventory-item:hover {
            transform: scale(1.15);
            border-color: var(--clr-accent);
        }

        .var-row {
            display: flex;
            gap: 4px;
            margin-bottom: 2px;
            align-items: center;
            flex-wrap: wrap;
        }

        .var-name {
            color: var(--clr-accent2);
        }

        .var-eq {
            color: var(--clr-muted);
        }

        .var-val {
            color: var(--clr-accent);
            font-weight: 700;
        }

        .var-new {
            color: var(--warning);
            font-size: .6rem;
            margin-left: 2px;
        }

        /**
         * NEU (live gemeldet: "Wenn man in funci eine Liste verwendet,
         * so soll diese im Ausgabebereich visualisiert werden [...]
         * Beim Programmablauf und beim Debugging sollen neue
         * Listeneinträge oder Wertänderungen visualisiert werden"):
         * Darstellung als Kette nummerierter "Chips" (1-basiert, wie
         * die Listen-Blöcke selbst), damit auf einen Blick sowohl die
         * Reihenfolge als auch die Anzahl der Einträge erkennbar ist -
         * ähnlich der bereits bestehenden Inventar-Kachel-Darstellung,
         * aber horizontal als Kette statt als Raster, da Listen-
         * Reihenfolge pädagogisch relevant ist (anders als das
         * ungeordnete Inventar).
         */
        #list-display {
            font-family: 'Courier New', monospace;
            font-size: var(--var-font-size, .78rem);
        }

        .list-row {
            display: block;
            margin-bottom: 6px;
        }

        .list-len {
            color: var(--clr-muted);
            font-size: .68rem;
        }

        .list-items-row {
            display: flex;
            flex-wrap: wrap;
            gap: 4px;
            margin-top: 3px;
            margin-left: 4px;
        }

        .list-item {
            display: inline-flex;
            align-items: center;
            gap: 3px;
            background: var(--clr-bg2, rgba(255, 255, 255, .05));
            border: 1px solid var(--border-color);
            border-radius: 6px;
            padding: 2px 6px;
            font-size: .72rem;
            transition: background .5s, border-color .5s;
        }

        .list-item-idx {
            color: var(--clr-muted);
            font-size: .6rem;
        }

        .list-item-empty {
            color: var(--clr-muted);
            font-size: .68rem;
            font-style: italic;
        }

        /**
         * Zuletzt veränderte Liste kurz hervorheben (die GESAMTE Liste,
         * siehe Begründung im JS-Kommentar bei updateListDisplay()) -
         * dieselbe Warnfarbe wie .var-new, aber als dezenter
         * Hintergrund-Rahmen statt reiner Textfarbe, da hier mehrere
         * Chips gleichzeitig markiert werden, nicht nur ein einzelner
         * Wert.
         */
        .list-row-changed .list-item {
            border-color: var(--warning);
            background: color-mix(in srgb, var(--warning) 18%, transparent);
        }

        /**
         * NEU (live gemeldet: "Ergänze im Ausgabebereich neben dem
         * Listen-Reiter einen Reiter 'Aufruf-Stack', der den Call-Stack
         * visualisiert"): jeder Rahmen als eigene Karte, mit dem
         * aktuellsten (obersten) Rahmen optisch hervorgehoben - macht
         * bei Rekursion die wachsende/schrumpfende Tiefe auf einen
         * Blick erkennbar.
         */
        #callstack-display {
            font-family: 'Courier New', monospace;
            font-size: var(--var-font-size, .78rem);
        }

        .cs-frame {
            border: 1px solid var(--border-color);
            border-radius: 6px;
            padding: 5px 8px;
            margin-bottom: 5px;
            background: var(--clr-bg2, rgba(255, 255, 255, .05));
        }

        .cs-frame-top {
            border-color: var(--clr-accent);
            background: color-mix(in srgb, var(--clr-accent) 12%, transparent);
        }

        .cs-frame-head {
            display: flex;
            align-items: center;
            gap: 6px;
            font-weight: 700;
        }

        .cs-frame-depth {
            color: var(--clr-muted);
            font-size: .68rem;
        }

        .cs-frame-name {
            color: var(--clr-accent2);
        }

        .cs-frame-current {
            color: var(--clr-accent);
            font-size: .6rem;
            font-weight: 400;
            margin-left: auto;
        }

        .cs-frame-params {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            margin-top: 3px;
        }

        .cs-param {
            font-size: .72rem;
        }

        .cs-param-name {
            color: var(--clr-muted);
        }

        .cs-param-val {
            color: var(--var-val, var(--clr-accent));
            font-weight: 700;
        }

        .cs-param-none {
            color: var(--clr-muted);
            font-size: .68rem;
            font-style: italic;
        }

        .cs-frame-caller {
            color: var(--clr-muted);
            font-size: .64rem;
            margin-top: 3px;
        }

        #console-wrap {
            flex: 1;
            overflow-y: auto;
            padding: 5px;
            font-family: 'Courier New', monospace;
            font-size: var(--console-font-size, .82rem);
        }

        /* ── Ausgabe-Tabs (Konsole / Code) ── */
        #output-panel-head {
            justify-content: space-between;
        }

        .output-tabs {
            display: flex;
            gap: 4px;
        }

        .output-tab-btn {
            background: none;
            border: 1px solid transparent;
            color: var(--clr-muted);
            font-size: .68rem;
            font-weight: 700;
            padding: 3px 8px;
            border-radius: 999px;
            cursor: pointer;
            font-family: inherit;
            text-transform: none;
            letter-spacing: normal;
        }

        .output-tab-btn.active {
            background: var(--clr-accent);
            color: var(--clr-on-accent);
        }

        /* v15.43 (PO-Wunsch „Reiter kompakter“): Reiter brechen nie um (vorher stand das Symbol bei
           schmalem Ausgabebereich über dem Text, die aktive Pille wurde angeschnitten). Ist der
           Ausgabebereich schmal, zeigen nicht aktive Reiter nur ihr Symbol (Name per title/aria-label,
           s. _splitTabLabel() in funci.html). */
        .output-tab-btn {
            white-space: nowrap;
        }

        #right-panel {
            container-type: inline-size;
            container-name: rightpanel;
        }

        @container rightpanel (max-width: 380px) {
            .output-tabs {
                gap: 2px;
            }

            .panel-head .output-tab-btn {
                padding: 3px 6px;
            }

            .panel-head .output-tab-btn:not(.active) .tab-txt {
                display: none;
            }
        }

        /**
         * BUGFIX (live gemeldet: grüner Text auf grünem Hintergrund beim
         * Hover über Konsole/Code/Visualisierung): die generische Regel
         * ".panel-head button:hover { color: var(--clr-accent); }"
         * (weiter unten) greift auch hier, da #output-panel-head
         * ebenfalls die Klasse "panel-head" trägt - beim Hovern über den
         * AKTIVEN Tab (bereits grüner Hintergrund über .active) wurde
         * der Text dadurch AUF DIESELBE grüne Farbe umgeschaltet wie der
         * Hintergrund selbst. #output-panel-head als zusätzlicher
         * ID-Selektor stellt sicher, dass diese gezielte Regel Vorrang
         * vor der generischen hat (höhere Spezifität), unabhängig von
         * der Reihenfolge im Stylesheet.
         */
        #output-panel-head .output-tab-btn:hover {
            color: var(--clr-text);
        }

        #output-panel-head .output-tab-btn.active:hover {
            color: var(--clr-on-accent);
        }

        /**
         * NEU (live gemeldet: "wenn einer der Reiter Variablen, Listen
         * oder Aufruf-Stack ausgewählt ist und man mit der Maus darüber
         * geht, kann man den text nicht mehr lesen"): dieselbe Ursache
         * wie beim oben dokumentierten Bugfix für die Konsole/Code/
         * Visualisierung-Reiter - die generische Regel ".panel-head
         * button:hover { color: var(--clr-accent); }" greift auch hier,
         * da die Variablen-Unterreiter (.var-tab-btn) in einem eigenen,
         * aber ebenfalls ".panel-head"-Container liegen. Dieser Container
         * hat keine eigene ID wie #output-panel-head - .var-tab-btn ist
         * als Klasse aber bereits spezifisch genug für eine direkte
         * Regel, ganz ohne zusätzlichen ID-Selektor.
         */
        .var-tab-btn:hover {
            color: var(--clr-text);
        }

        .var-tab-btn.active:hover {
            color: var(--clr-on-accent);
        }

        /**
         * BUGFIX (live gemeldet: Struktogramm/Flussdiagramm sichtbar im
         * Konsole- UND Code-Reiter): setOutputTab()/setMobOutputTab()
         * togglen die Klasse "output-hidden" korrekt auf ALLEN
         * betroffenen Containern (siehe dortige JS-Funktionen) - diese
         * CSS-Regel deckte davon bisher aber NUR #console-wrap und
         * #embedded-code-wrap ab. #embedded-viz-wrap (Desktop) FEHLTE
         * komplett, wodurch der Visualisierungs-Bereich unabhängig vom
         * gewählten Reiter IMMER sichtbar blieb - das Umschalten der
         * Klasse hatte dort schlicht keine Wirkung. Dieselbe Lücke
         * bestand zusätzlich für ALLE DREI mobilen Container
         * (#mob-console-wrap/#mob-code-wrap/#mob-viz-wrap), die bisher
         * gar keine eigene .output-hidden-Regel hatten.
         */
        #console-wrap.output-hidden,
        #embedded-code-wrap.output-hidden,
        #embedded-viz-wrap.output-hidden,
        #mob-console-wrap.output-hidden,
        #mob-code-wrap.output-hidden,
        #mob-viz-wrap.output-hidden {
            display: none;
        }

        #embedded-code-wrap {
            flex: 1;
            display: flex;
            flex-direction: column;
            min-height: 0;
        }

        #embedded-code-toolbar {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 4px 6px;
            border-bottom: 1px solid var(--border-color);
            flex-shrink: 0;
        }

        #embedded-code-lang-tabs {
            display: flex;
            gap: 4px;
        }

        .embedded-code-lang-btn {
            background: none;
            border: 1px solid var(--border-color);
            color: var(--clr-muted);
            font-size: .64rem;
            font-weight: 700;
            padding: 2px 7px;
            border-radius: 6px;
            cursor: pointer;
            font-family: inherit;
        }

        .embedded-code-lang-btn.active {
            background: var(--clr-accent2);
            color: #fff;
            border-color: var(--clr-accent2);
        }

        #embedded-code-toolbar>button {
            background: none;
            border: 1px solid var(--border-color);
            color: var(--clr-muted);
            border-radius: 6px;
            width: 22px;
            height: 22px;
            cursor: pointer;
            font-size: .8rem;
            line-height: 1;
        }

        #embedded-code-toolbar>button:hover {
            background: var(--clr-accent);
            color: var(--clr-on-accent);
        }

        #embedded-code-output {
            flex: 1;
            overflow: auto;
            background: var(--box-bg);
            color: var(--clr-text);
            font-family: 'Cascadia Code', 'Fira Code', 'Courier New', monospace;
            font-size: var(--code-font-size, .78rem);
            line-height: 1.55;
            white-space: pre;
            tab-size: 2;
        }

        #embedded-code-output .code-line {
            padding: 0 8px;
        }

        #embedded-code-output .code-line-num {
            width: 1.8em;
            margin-right: 8px;
        }

        /**
         * NEU (live gemeldet): Struktogramm (Nassi-Shneiderman-Diagramm)
         * im Reiter "Visualisierung" - Kästchen werden als verschachtelte
         * div-Blöcke aufgebaut (siehe _renderStructNode()), die
         * Verschachtelungstiefe entsteht rein durch die HTML-Struktur
         * selbst (Innen-Ränder/Einrückung), nicht durch eine feste
         * Pixel-Berechnung, damit beliebig tief verschachtelte Programme
         * funktionieren.
         */
        #embedded-viz-wrap {
            flex: 1;
            display: flex;
            flex-direction: column;
            min-height: 0;
        }

        #embedded-viz-output {
            flex: 1;
            overflow: auto;
            background: var(--box-bg);
            padding: 10px;
            font-size: .76rem;
        }

        .struct-empty {
            padding: 10px 12px;
            color: var(--clr-muted);
            font-style: italic;
        }

        /**
         * GEÄNDERT v15.19: Struktogramm als SVG (_generateStruktogrammSvg())
         * nach DIN 66261 - Schleife in L-Form (Kopf + linker Balken, Farbe je
         * Schleifentiefe .struct-depth-0…3), Verzweigung mit Dreieck und
         * „ja“/„nein“. Export-Farben stehen fest in VIZ_EXPORT_STYLE.
         */
        .struktogramm-svg {
            display: block;
            --struct-line: var(--clr-muted);
        }

        .struct-label {
            fill: var(--clr-text);
            font-size: 13px;
            font-family: inherit;
        }

        .struct-head-label {
            font-weight: 700;
        }

        .struct-branch-label {
            fill: var(--clr-muted);
            font-size: 11px;
            font-style: italic;
        }

        .struct-stmt rect,
        .struct-col,
        .struct-empty-cell rect {
            fill: var(--clr-surface);
            stroke: var(--struct-line);
            stroke-width: 1.2;
        }

        .struct-empty-cell .struct-label {
            fill: var(--clr-muted);
        }

        .struct-if rect {
            fill: color-mix(in srgb, rgb(var(--warning-rgb)) 16%, var(--clr-surface));
            stroke: var(--struct-line);
            stroke-width: 1.2;
        }

        .struct-if line {
            stroke: var(--struct-line);
            stroke-width: 1.2;
        }

        .struct-loop polygon {
            stroke: var(--struct-line);
            stroke-width: 1.2;
        }

        /* deckend gemischt (nicht halbtransparent), sonst hängt der Farbton
           vom jeweiligen Untergrund ab (verschachtelte Verzweigung heller) */
        .struct-loop polygon { fill: var(--clr-surface); }
        .struct-depth-0 polygon { fill: color-mix(in srgb, rgb(var(--clr-accent2-rgb)) 26%, var(--clr-surface)); }
        .struct-depth-1 polygon { fill: color-mix(in srgb, rgb(139, 92, 246) 26%, var(--clr-surface)); }
        .struct-depth-2 polygon { fill: color-mix(in srgb, rgb(20, 184, 166) 26%, var(--clr-surface)); }
        .struct-depth-3 polygon { fill: color-mix(in srgb, rgb(236, 72, 153) 24%, var(--clr-surface)); }

        .struct-frame {
            fill: none;
            stroke: var(--struct-line);
            stroke-width: 1.5;
        }

        /* Hervorhebung beim schrittweisen Debuggen (siehe
           _highlightVizNode()) - dieselbe Akzentfarbe wie die
           Zeilen-Hervorhebung in der Code-Ansicht, damit beide optisch
           als "derselbe aktuelle Schritt" erkennbar sind. */
        .struct-box.struct-active rect,
        .struct-box.struct-active polygon {
            stroke: var(--clr-accent);
            stroke-width: 3;
        }

        .struct-stmt.struct-active rect,
        .struct-if.struct-active rect,
        .struct-loop.struct-active polygon {
            fill: rgba(var(--success-rgb), 0.3);
        }

        /**
         * NEU: Umschalter zwischen den beiden Visualisierungsformen
         * (Struktogramm/Flussdiagramm) oberhalb der eigentlichen
         * Darstellung - dieselbe Pillenform/derselbe Grundstil wie
         * andere Umschalter der App (z.B. der Code-Sprache-Umschalter),
         * damit sich das Bedienkonzept konsistent anfühlt.
         */
        /**
         * BUGFIX (live gemeldet): Struktogramm-/Flussdiagramm-/PNG-/
         * Vergrößern-Buttons hatten bisher ein eigenes, deutlich
         * größeres Pillen-Design (padding 5px 14px, border-radius 999px,
         * font .76rem) - sichtbar inkonsistent zum kompakten Code-Reiter
         * direkt daneben (padding 2px 7px, border-radius 6px,
         * font .64rem). Jetzt strukturell UND optisch an dasselbe Muster
         * angeglichen: #viz-type-toggle entspricht #embedded-code-
         * toolbar (gruppierte Umschalter-Buttons links, einzelne
         * Aktions-Buttons rechtsbündig über justify-content:
         * space-between statt der vorherigen margin-left:auto-Krücke),
         * #embedded-viz-type-tabs entspricht #embedded-code-lang-tabs.
         */
        #viz-type-toggle {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 4px 6px;
            border-bottom: 1px solid var(--border-color);
            flex-shrink: 0;
            gap: 4px;
        }

        #embedded-viz-type-tabs {
            display: flex;
            gap: 4px;
        }

        .viz-type-btn {
            background: none;
            border: 1px solid var(--border-color);
            color: var(--clr-muted);
            font-size: .64rem;
            font-weight: 700;
            padding: 2px 7px;
            border-radius: 6px;
            cursor: pointer;
            font-family: inherit;
        }

        .viz-type-btn:hover {
            border-color: var(--clr-accent);
        }

        .viz-type-btn.active {
            background: var(--clr-accent2);
            color: var(--clr-on-accent); /* v14.91: #fff auf #38bdf8 = 2,1:1 im Darkmode */
            border-color: var(--clr-accent2);
        }

        /* v15.27: #viz-download-btn (PNG im Reiter) entfallen - PNG im ☰-Menü (#viz-png-menu-btn) und im Zoom-Fenster */

        /**
         * NEU: Flussdiagramm (Programmablaufplan) - klassische Symbole
         * (Stadion-Form für Start/Ende, Rechteck für Anweisungen, Raute
         * für Verzweigungen/Schleifenbedingungen), erzeugt von
         * _generateFlowchartSvg(). Die einzelnen Symbole liegen als
         * <g class="flow-node" data-block-id="..."> vor - dieselbe
         * data-block-id wie im Struktogramm, damit _highlightVizNode()
         * beide Darstellungen über denselben Mechanismus hervorheben
         * kann.
         */
        .flowchart-svg {
            display: block;
            margin: 0 auto;
        }

        .flow-stmt rect {
            fill: var(--clr-surface);
            stroke: var(--border-color);
            stroke-width: 1.5;
        }

        .flow-decision polygon {
            fill: rgba(var(--warning-rgb), 0.1);
            stroke: #f59e0b;
            stroke-width: 1.5;
        }

        /* DIN 66001: Parallelogramm für Ein-/Ausgabe */
        .flow-io polygon {
            fill: rgba(20, 184, 166, 0.12);
            stroke: #14b8a6;
            stroke-width: 1.5;
        }

        /* DIN 66001: Rechteck mit doppelten Vertikallinien für
           Unterprogrammaufrufe */
        .flow-subroutine rect {
            fill: var(--clr-surface);
            stroke: var(--clr-accent2);
            stroke-width: 1.5;
        }
        .flow-subroutine line {
            stroke: var(--clr-accent2);
            stroke-width: 1.5;
        }

        .flow-startend rect {
            fill: rgba(var(--clr-accent2-rgb), 0.14);
            stroke: var(--clr-accent2);
            stroke-width: 1.5;
        }

        /* v15.17: 13/12 px statt 11/10 px (Flussdiagramm war kaum lesbar) */
        .flow-label {
            fill: var(--clr-text);
            font-size: 13px;
            font-family: inherit;
        }

        .flow-startend .flow-label {
            font-weight: 700;
        }

        .flow-arrow-label {
            fill: var(--clr-muted);
            font-size: 12px;
            font-style: italic;
        }

        .flow-arrow,
        .flow-connector {
            stroke: var(--clr-muted);
            stroke-width: 1.5;
            fill: none;
        }

        /* v15.17: Pfeilspitzen als eigene Dreiecke am Symbol (_flowHead()) */
        .flow-arrowhead {
            fill: var(--clr-muted);
            stroke: none;
        }

        /* Hervorhebung beim schrittweisen Debuggen - dieselbe
           Akzentfarbe wie beim Struktogramm (siehe .struct-active),
           damit beide Darstellungen optisch als "derselbe aktuelle
           Schritt" erkennbar sind. */
        .flow-node.flow-active rect,
        .flow-node.flow-active polygon {
            stroke: var(--clr-accent);
            stroke-width: 3;
        }

        .flow-node.flow-active.flow-stmt rect {
            fill: rgba(var(--success-rgb), 0.28);
        }

        .flow-node.flow-active.flow-decision polygon {
            fill: rgba(var(--success-rgb), 0.28);
        }

        .flow-node.flow-active.flow-io polygon {
            fill: rgba(var(--success-rgb), 0.28);
        }

        .flow-node.flow-active.flow-subroutine rect {
            fill: rgba(var(--success-rgb), 0.28);
        }

        .c-line {
            padding: 2px 4px;
            border-radius: 3px;
            margin-bottom: 2px;
            word-break: break-all;
        }

        .c-info {
            color: var(--clr-accent2);
        }

        .c-success {
            color: var(--success);
        }

        .c-error {
            color: var(--danger);
        }

        .c-output {
            color: var(--clr-text);
            background: var(--box-bg);
        }

        .c-system {
            color: var(--clr-muted);
            font-style: italic;
        }

        .c-debug {
            color: var(--warning);
            background: rgba(var(--warning-rgb), 0.06);
            border-left: 2px solid var(--warning);
            padding-left: 6px;
        }

        /* ── Blockly Toolbox ── (Blockly 13: .blocklyToolbox statt .blocklyToolboxDiv,
           .blocklyToolboxCategoryLabel statt .blocklyTreeLabel, .blocklyToolboxSelected statt .blocklyTreeSelected) */
        .blocklyToolbox {
            background: var(--clr-bg) !important;
            border-right: 1px solid var(--border-color) !important;
        }

        /* v15.59 (iPad-Screenshots 05.11.2026): Blockly 13 gibt jeder Kategorie-Zeile eine FESTE Höhe
           (height/line-height 22px). Unser Innenabstand + Emoji ergibt 26 px (Chromium) bzw. deutlich mehr
           (Safari, Apple Color Emoji) -> Beschriftung rutschte unter die Trennlinie in die nächste Zeile,
           Farbstreifen und Text liefen auseinander. Höhe folgt jetzt dem Inhalt. */
        .blocklyToolboxCategory {
            height: auto !important;
            min-height: 22px;
            line-height: 1.25 !important;
            background: var(--clr-surface) !important;
            border-bottom: 1px solid var(--border-color) !important;
            transition: background .15s !important;
        }

        .blocklyToolboxCategory:hover {
            background: var(--box-bg) !important;
        }

        .blocklyToolboxCategoryLabel {
            color: var(--clr-text) !important;
            font-size: .8rem !important;
            font-weight: 600 !important;
            text-shadow: 0 1px 3px rgba(0, 0, 0, .4) !important;
        }

        .blocklyTreeRowContentContainer {
            display: flex !important;
            align-items: center !important;
            gap: 6px !important;
            padding: 5px 8px !important;
        }

        .blocklyToolboxSelected {
            background: rgba(var(--success-rgb), 0.1) !important;
            border-left: 3px solid var(--clr-accent) !important;
        }

        .blocklyToolboxSelected .blocklyToolboxCategoryLabel {
            color: var(--clr-accent) !important;
            font-weight: 700 !important;
        }

        .blocklyFlyout {
            background: rgba(9, 14, 26, 0.97) !important;
        }

        /* ── App-Modals ── */
        .modal-bg {
            position: fixed;
            inset: 0;
            background: rgba(0, 0, 0, .7);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 9999;
            opacity: 0;
            pointer-events: none;
            transition: opacity .25s;
            backdrop-filter: blur(4px);
        }

        .modal-bg.show {
            opacity: 1;
            pointer-events: all;
        }

        .modal-box {
            background: var(--clr-surface);
            color: var(--clr-text);
            border-radius: var(--radius);
            padding: 28px 32px;
            max-width: 420px;
            width: 90%;
            text-align: center;
            transform: scale(.88);
            transition: transform .25s;
            box-shadow: var(--shadow-lg);
            border: 1px solid var(--border-color);
            /* Sicherheitsnetz für alle Modals: egal wie viel Inhalt (z.B.
               im Klassenraum-Fenster mit mehreren Formularen), das Fenster
               kann nie höher als der sichtbare Bereich werden – stattdessen
               scrollt der Inhalt innerhalb des Fensters. */
            max-height: 90vh;
            max-height: 90dvh;
            overflow-y: auto;
        }

        .modal-bg.show .modal-box {
            transform: scale(1);
        }

        /**
         * NEU (02.10.2026, live gemeldet: "Das Popup-Modal für nicht
         * bestandene Aufgaben blendet den Hintergrund so stark aus, dass
         * man nichts mehr sieht (damit auch nicht, was das Programm
         * gemacht hat - z. B. Konsolenausgabe, Turtle-Weg, Grid-Welt)"):
         * das Fehler-Modal dunkelt nur noch leicht ab und verzichtet auf
         * den Weichzeichner - Konsole, Turtle-Weg und Grid-Welt bleiben
         * dahinter lesbar. Der deutliche Schatten hebt das Fenster selbst
         * trotzdem klar vom Hintergrund ab.
         * ERWEITERT (04.10.2026, live angefordert: "auch bei bestandenen
         * Aufgaben soll der Hintergrund nicht so stark ausgeblendet
         * werden"): gilt jetzt genauso für das Erfolgs-Modal.
         */
        #error-modal,
        #success-modal {
            background: rgba(0, 0, 0, .15);
            backdrop-filter: none;
        }

        #error-modal .modal-box,
        #success-modal .modal-box {
            box-shadow: 0 12px 40px rgba(0, 0, 0, .45);
        }

        /**
         * NEU (live gemeldet: "ergänze im Modal oben einen
         * Schließen-Button"): "X"-Schließen-Button oben rechts in
         * langen/scrollbaren Modalen (aktuell: Changelog), zusätzlich
         * zum bestehenden Schließen-Button unten.
         */
        .modal-close-x:hover {
            color: var(--clr-text);
            background: var(--box-bg);
        }

        .modal-icon {
            font-size: 2.8rem;
            margin-bottom: 10px;
        }

        .modal-stars {
            font-size: 1.8rem;
            margin-bottom: 10px;
        }

        .modal-box h2 {
            margin-bottom: 8px;
            font-size: 1.2rem;
            color: var(--clr-accent);
        }

        .modal-box p {
            color: var(--clr-muted);
            font-size: .84rem;
            line-height: 1.6;
            margin-bottom: 14px;
        }

        .mbtn {
            padding: 7px 18px;
            border: 1px solid var(--border-color);
            border-radius: 999px;
            cursor: pointer;
            font-size: .82rem;
            font-weight: 700;
            margin: 3px;
            transition: var(--transition-fast);
            display: inline-block;
            background: var(--box-bg);
            color: var(--clr-text);
        }

        .mbtn:hover {
            transform: scale(1.04);
        }

        .mbtn.primary {
            background: linear-gradient(135deg, var(--clr-accent), var(--clr-accent2));
            color: var(--clr-on-accent);
            border: none;
        }

        /**
         * NEU (live gemeldet: "Im Predict-Teil ... fehlt eine Meldung
         * wenn man auf weiter klickt"): rein optische Abschwächung -
         * der Button bleibt bewusst klickbar (kein disabled-Attribut
         * mehr), damit ein Klick trotzdem eine Meldung auslösen kann
         * (siehe _primmNextPhase() in funci.html).
         */
        .mbtn.primary.primm-btn-incomplete {
            opacity: .55;
        }

        .mbtn.next-diff {
            background: linear-gradient(135deg, var(--warning), #f59e0b);
            color: var(--clr-on-accent);
            border: none;
        }

        .mbtn.secondary {
            background: var(--box-bg);
            color: var(--clr-muted);
        }

        /* Solution Modal */
        #solution-modal .modal-box {
            max-width: 640px;
            text-align: left;
            border-color: var(--clr-accent);
            padding: 22px 26px;
        }

        #solution-modal h2 {
            font-size: 1.05rem;
            margin-bottom: 4px;
        }

        .sol-tabs {
            display: flex;
            gap: 6px;
            margin-bottom: 10px;
            flex-wrap: wrap;
        }

        .sol-tab {
            padding: 3px 13px;
            border-radius: 999px;
            border: 1px solid transparent;
            cursor: pointer;
            font-size: .76rem;
            font-weight: 700;
            background: var(--box-bg);
            color: var(--clr-muted);
            transition: var(--transition-fast);
        }

        .sol-tab.easy {
            border-color: var(--success);
        }

        .sol-tab.medium {
            border-color: var(--warning);
        }

        .sol-tab.hard {
            border-color: var(--danger);
        }

        .sol-tab.easy.active {
            background: var(--success);
            color: var(--clr-on-accent);
        }

        .sol-tab.medium.active {
            background: var(--warning);
            color: var(--clr-on-accent);
        }

        .sol-tab.hard.active {
            background: var(--danger);
            color: #fff;
        }

        #solution-steps {
            background: var(--clr-bg);
            border-radius: 10px;
            padding: 10px;
            max-height: 300px;
            overflow-y: auto;
            border: 1px solid var(--border-color);
            font-size: .8rem;
            line-height: 1.6;
        }

        .sol-info {
            background: rgba(var(--success-rgb), 0.07);
            border: 1px solid var(--clr-accent);
            border-radius: 8px;
            padding: 7px 11px;
            margin-bottom: 9px;
            font-size: .76rem;
            color: var(--clr-accent);
            line-height: 1.5;
        }

        .sol-step {
            display: flex;
            align-items: flex-start;
            gap: 9px;
            padding: 5px 7px;
            border-radius: 6px;
            margin-bottom: 4px;
            background: var(--box-bg);
            border-left: 3px solid var(--border-color);
        }

        .sol-step.step-var {
            border-left-color: var(--clr-accent2);
        }

        .sol-step.step-loop {
            border-left-color: var(--warning);
        }

        .sol-step.step-if {
            border-left-color: var(--hint-purple);
        }

        .sol-step.step-print {
            border-left-color: var(--success);
        }

        .sol-step.step-turtle {
            border-left-color: var(--clr-accent);
        }

        .sol-step.step-nested {
            margin-left: 20px;
        }

        .sol-num {
            min-width: 20px;
            height: 20px;
            background: var(--box-bg);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: .66rem;
            font-weight: 700;
            color: var(--clr-muted);
            flex-shrink: 0;
        }

        .sol-text {
            flex: 1;
            color: var(--clr-muted);
        }

        .sol-text strong {
            color: var(--clr-text);
        }

        .sol-text code {
            background: var(--box-bg);
            padding: 1px 4px;
            border-radius: 3px;
            font-family: monospace;
            font-size: .76rem;
            color: var(--clr-accent);
        }

        .sol-load-btn {
            padding: 6px 16px;
            background: linear-gradient(135deg, var(--danger), #c0392b);
            color: #fff;
            border: none;
            border-radius: 999px;
            cursor: pointer;
            font-size: .78rem;
            font-weight: 700;
            transition: var(--transition-fast);
        }

        .sol-load-btn:hover {
            transform: scale(1.04);
            filter: brightness(1.1);
        }

        /* Code Modal */
        #code-modal .modal-box {
            max-width: 560px;
            text-align: left;
            border-color: var(--clr-accent2);
        }

        #stats-info-modal .modal-box {
            max-width: 560px;
            text-align: left;
        }

        /**
         * NEU (live gemeldet: "analog zum Code-Reiter einen Button zur
         * Vergrößerung"): deutlich breiter als der Code-Dialog (560px) -
         * Diagramme (insbesondere Flussdiagramme mit nebeneinander
         * liegenden Ja/Nein-Zweigen) brauchen spürbar mehr Platz als
         * reiner Text, um beim "Vergrößern" tatsächlich einen Mehrwert
         * zu bieten.
         */
        /**
         * GEÄNDERT (live gemeldet: "Das Visualisierungs-Modal soll nicht
         * größer werden bei Vergrößerung, sondern nur das Diagramm.
         * Gestalte das Modal insgesamt größer (fast volle
         * Seitengröße)"): bisher richtete sich die Höhe des Modals nach
         * seinem INHALT (nur mit max-height als Sicherheitsnetz nach
         * oben begrenzt) - dadurch wuchs beim Hineinzoomen des Diagramms
         * auch #viz-modal-scroll und in Folge das ganze Modal mit.
         * Jetzt eine FESTE Größe (fast bildschirmfüllend) über Flexbox:
         * .modal-box wird zur Spalte, #viz-modal-scroll bekommt über
         * flex:1 den gesamten verbleibenden Platz nach Titel/Symbolleiste
         * /Schließen-Button - unabhängig vom Zoom-Level bleibt dieser
         * Bereich immer gleich groß, NUR der Inhalt darin (die Zoom-
         * Sizer-Skalierung) wird größer/kleiner, mit Scrollbalken bei
         * Bedarf.
         */
        #viz-modal .modal-box {
            max-width: 98vw;
            width: 98vw;
            height: 95vh;
            height: 95dvh;
            max-height: 95vh;
            max-height: 95dvh;
            display: flex;
            flex-direction: column;
            text-align: left;
            border-color: var(--clr-accent2);
            overflow-y: hidden;
        }

        #viz-modal-title,
        #viz-modal-toolbar,
        #viz-modal > .modal-box > .mbtn {
            flex-shrink: 0;
        }

        #viz-modal-toolbar {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 8px;
            flex-wrap: wrap;
            gap: 8px;
        }

        #viz-modal-type-tabs {
            display: flex;
            gap: 6px;
        }

        #viz-zoom-controls {
            display: flex;
            align-items: center;
            gap: 4px;
            background: var(--box-bg);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 3px;
        }

        #viz-zoom-controls button {
            width: 26px;
            height: 26px;
            border: none;
            background: transparent;
            color: var(--clr-text);
            font-size: .9rem;
            font-weight: 700;
            cursor: pointer;
            border-radius: 5px;
            line-height: 1;
        }

        #viz-zoom-controls button:hover {
            background: var(--clr-accent);
            color: var(--clr-on-accent);
        }

        #viz-zoom-label {
            font-size: .68rem;
            color: var(--clr-muted);
            min-width: 34px;
            text-align: center;
        }

        /**
         * #viz-modal-scroll reserviert den sichtbaren, scrollbaren
         * Bereich; #viz-zoom-sizer bekommt von _applyVizZoom() aus
         * gezielt die tatsächliche (skalierte) Zielgröße zugewiesen,
         * damit beim Vergrößern nichts abgeschnitten wird und der ganze
         * Inhalt erreichbar bleibt (siehe ausführlicher Kommentar dort).
         */
        #viz-modal-scroll {
            background: var(--box-bg);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            flex: 1;
            min-height: 0;
            overflow: auto;
            margin-bottom: 12px;
            padding: 14px;
        }

        #viz-zoom-sizer {
            position: relative;
        }

        /**
         * BUGFIX (live gemeldet: "Struktogramm soll sich dem zur
         * Verfügung stehenden Bereich anpassen"): fehlte hier bisher -
         * als normales Block-Element (display:block Standard bei <div>)
         * dehnte sich #viz-modal-output/#embedded-viz-inner IMMER auf
         * die volle Breite seines Eltern-Elements (#viz-zoom-sizer)
         * aus, statt sich auf die tatsächliche Inhaltsgröße zu
         * beschränken. Da genau dieser Sizer von der JS-Seite aus
         * (siehe _applyVizZoom()/_applyEmbeddedVizZoom()) explizit auf
         * die zuletzt berechnete skalierte Zielgröße gesetzt wird,
         * entstand dadurch eine Rückkopplungsschleife: jede erneute
         * "natürliche Größe"-Messung (scrollWidth/scrollHeight) maß in
         * Wirklichkeit die Größe des VORHERIGEN Sizers mit, nicht die
         * echte Inhaltsgröße - wodurch die Einpass-Berechnung bei jedem
         * Aufruf zunehmend verfälscht wurde. display:inline-block lässt
         * das Element stattdessen immer auf seine tatsächliche
         * Inhaltsgröße schrumpfen ("shrink-to-fit"), unabhängig von der
         * Breite des umgebenden Sizers.
         */
        #viz-modal-output {
            display: inline-block;
            transform-origin: top left;
        }

        /* Zoom-Kontrollen des eingebetteten Visualisierungs-Reiters: seit
           01.10.2026 im Reiter-Kopf (#output-panel-head) im Stil der
           Schriftgrößen-Knöpfe (.font-size-ctrl), siehe dort. */

        #embedded-viz-zoom-sizer {
            position: relative;
        }

        /** BUGFIX: siehe ausführlicher Kommentar bei #viz-modal-output
         *  weiter oben - dieselbe Rückkopplungsschleife trat auch hier
         *  auf. */
        #embedded-viz-inner {
            display: inline-block;
            transform-origin: top left;
        }

        /**
         * NEU (live gemeldet): Abzeichen-Übersicht innerhalb von
         * showStatsInfo('badges') - freigeschaltete Abzeichen zeigen ihr
         * eigentliches Symbol in Farbe, noch nicht erreichte ein
         * einheitliches Schloss-Symbol mit reduzierter Deckkraft (bewusst
         * NICHT komplett ausgeblendet, damit erkennbar bleibt, WIE VIELE
         * es insgesamt zu erreichen gibt, als zusätzlicher Anreiz).
         */
        .stats-info-hint {
            font-size: .74rem;
            color: var(--clr-muted);
            margin-top: 10px;
        }

        .badge-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
            gap: 10px;
        }

        .badge-item {
            display: flex;
            flex-direction: column;
            align-items: center;
            text-align: center;
            padding: 10px 6px;
            border-radius: 10px;
            border: 1px solid var(--border-color);
            background: var(--box-bg);
        }

        .badge-item.badge-earned {
            border-color: var(--clr-accent);
            background: rgba(var(--success-rgb), 0.1);
        }

        .badge-item.badge-locked {
            opacity: .55;
        }

        .badge-icon {
            font-size: 1.7rem;
            margin-bottom: 4px;
        }

        .badge-name {
            font-size: .74rem;
            font-weight: 700;
            color: var(--clr-text);
            margin-bottom: 2px;
        }

        .badge-desc {
            font-size: .66rem;
            color: var(--clr-muted);
        }

        .code-lang-btn {
            padding: 6px 12px;
            border-radius: 6px;
            border: 1px solid var(--border-color);
            background: var(--box-bg);
            color: var(--clr-text);
            font-size: .75rem;
            font-weight: 600;
            cursor: pointer;
            font-family: inherit;
        }

        .code-lang-btn.active {
            background: var(--clr-accent2);
            color: var(--clr-on-accent); /* v14.91: #fff auf #38bdf8 = 2,1:1 im Darkmode */
            border-color: var(--clr-accent2);
        }

        .code-lang-btn:focus-visible {
            outline: 2px solid var(--clr-accent);
            outline-offset: 1px;
        }

        /* ── Code-Ansicht: Zeilen, Syntax-Hervorhebung, Zoom ── */
        #code-toolbar {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 8px;
            flex-wrap: wrap;
            gap: 8px;
        }

        #console-modal-toolbar {
            display: flex;
            justify-content: flex-end;
            margin-bottom: 8px;
        }

        #console-modal-output {
            background: var(--box-bg);
            color: var(--clr-text);
            padding: 8px 10px;
            border-radius: 8px;
            font-family: 'Courier New', monospace;
            max-height: 60vh;
            overflow: auto;
            margin-bottom: 12px;
            border: 1px solid var(--border-color);
            font-size: .82rem;
        }

        #code-zoom-controls,
        #console-zoom-controls {
            display: flex;
            align-items: center;
            gap: 4px;
            background: var(--box-bg);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 3px;
        }

        #code-zoom-controls button,
        #console-zoom-controls button {
            width: 26px;
            height: 26px;
            border: none;
            background: transparent;
            color: var(--clr-text);
            font-size: .9rem;
            font-weight: 700;
            cursor: pointer;
            border-radius: 5px;
            line-height: 1;
        }

        #code-zoom-controls button:hover,
        #console-zoom-controls button:hover {
            background: var(--clr-accent);
            color: var(--clr-on-accent);
        }

        #code-zoom-label,
        #console-zoom-label {
            font-size: .68rem;
            color: var(--clr-muted);
            min-width: 34px;
            text-align: center;
        }

        #code-output {
            background: var(--box-bg);
            color: var(--clr-text);
            padding: 10px 0;
            border-radius: 8px;
            font-family: 'Cascadia Code', 'Fira Code', 'Courier New', monospace;
            white-space: pre;
            max-height: 320px;
            overflow: auto;
            margin-bottom: 12px;
            border: 1px solid var(--border-color);
            tab-size: 2;
            font-size: .8rem;
            line-height: 1.65;
        }

        .code-line {
            display: flex;
            padding: 0 12px;
            transition: background-color .15s;
            border-left: 3px solid transparent;
        }

        .code-line-num {
            flex-shrink: 0;
            width: 2.4em;
            text-align: right;
            margin-right: 14px;
            color: var(--clr-muted);
            opacity: .6;
            user-select: none;
        }

        .code-line-content {
            white-space: pre;
        }

        .code-line.debug-active {
            background: rgba(var(--warning-rgb), .22);
            border-left-color: var(--warning);
        }

        .code-line.debug-active .code-line-num {
            color: var(--warning);
            font-weight: 700;
            opacity: 1;
        }

        /* Sehr einfache, regelbasierte Syntax-Einfärbung (kein echter
           Parser) - reicht für die kurzen, generierten Programme hier
           völlig aus und bleibt leichtgewichtig. Standardfarben sind für
           den Dunkelmodus abgestimmt (übliche "VS Code Dark"-Palette);
           html[data-theme="light"] weiter unten überschreibt sie mit
           kräftigeren, auf hellem Hintergrund gut lesbaren Farbtönen -
           damit übernimmt die Code-Ansicht jetzt tatsächlich das gerade
           aktive Dark-/Light-Theme statt fest auf Dunkel zu stehen. */
        .tok-kw {
            color: #c586c0;
        }

        .tok-str {
            color: #ce9178;
        }

        .tok-num {
            color: #b5cea8;
        }

        .tok-com {
            color: #6a9955;
            font-style: italic;
        }

        .tok-fn {
            color: #dcdcaa;
        }

        html[data-theme="light"] .tok-kw {
            color: #a626a4;
        }

        html[data-theme="light"] .tok-str {
            color: #b5651d;
        }

        html[data-theme="light"] .tok-num {
            color: #1a7a3c;
        }

        html[data-theme="light"] .tok-com {
            color: #6b7280;
        }

        html[data-theme="light"] .tok-fn {
            color: #986801;
        }

        /* Toast */
        #toast {
            position: fixed;
            bottom: 60px;
            left: 50%;
            transform: translateX(-50%) translateY(12px);
            background: var(--danger);
            color: #fff;
            padding: 6px 16px;
            border-radius: 999px;
            font-weight: 700;
            font-size: .78rem;
            opacity: 0;
            transition: opacity .3s, transform .3s;
            z-index: 99999;
            pointer-events: none;
            white-space: nowrap;
            box-shadow: var(--shadow);
        }

        /* 22.10.2026: lange Meldungen (z.B. erreichte Grenze) umbrechen statt aus dem Bild laufen */
        #toast.long {
            white-space: normal;
            width: max-content;
            max-width: min(560px, calc(100vw - 32px));
            border-radius: 12px;
            text-align: center;
            line-height: 1.4;
        }

        #toast.show {
            opacity: 1;
            transform: translateX(-50%) translateY(0);
        }

        /* Scrollbars */
        ::-webkit-scrollbar {
            width: 4px;
            height: 4px;
        }

        ::-webkit-scrollbar-track {
            background: transparent;
        }

        ::-webkit-scrollbar-thumb {
            background: var(--border-color);
            border-radius: 2px;
        }

        /* Classroom-Button im Header */
        #btn-classroom {
            padding: 4px 14px;
            border-radius: 999px;
            border: 1px solid var(--clr-accent2);
            background: rgba(var(--clr-accent2-rgb), 0.1);
            color: var(--clr-accent2);
            font-size: .76rem;
            font-weight: 700;
            cursor: pointer;
            transition: var(--transition-fast);
            white-space: nowrap;
        }

        #btn-classroom:hover {
            background: rgba(var(--clr-accent2-rgb), 0.22);
            transform: translateY(-1px);
        }

        /* Angemeldeter Benutzer im Header */
        #user-badge {
            display: none;
            align-items: center;
            gap: 7px;
            background: var(--box-bg);
            border: 1px solid var(--border-color);
            border-radius: 999px;
            padding: 3px 12px 3px 6px;
            font-size: .76rem;
            /**
             * BUGFIX (live gemeldet: "der Anmeldebutton mit Name und
             * Raumnummer reicht bei verkleinerter Fensterbreite über den
             * oberen Bereich hinaus"): #user-badge selbst darf schrumpfen
             * (kein flex-shrink:0), soll dabei aber nicht unter eine
             * sinnvolle Mindestbreite fallen (min-width statt komplettem
             * Verschwinden) - der eigentliche Platzgewinn kommt aus der
             * Textkürzung in .ub-text-wrap weiter unten.
             */
            min-width: 0;
            flex-shrink: 1;
        }

        #user-badge.visible {
            display: flex;
        }

        #user-badge .ub-avatar {
            width: 22px;
            height: 22px;
            border-radius: 50%;
            background: linear-gradient(135deg, var(--clr-accent), var(--clr-accent2));
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: .7rem;
            font-weight: 700;
            color: var(--clr-on-accent);
            flex-shrink: 0;
        }

        /* Siehe Kommentar bei #user-badge oben - dieselbe Flexbox-Falle
           (min-width:auto auf Flex-Kindern) wie beim Header-Titel. */
        #user-badge .ub-text-wrap {
            min-width: 0;
            overflow: hidden;
        }

        #user-badge .ub-name {
            color: var(--clr-text);
            font-weight: 600;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        #user-badge .ub-room {
            color: var(--clr-muted);
            font-size: .68rem;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        #user-badge .ub-logout {
            background: none;
            border: none;
            color: var(--clr-muted);
            cursor: pointer;
            font-size: .78rem;
            padding: 0 0 0 4px;
            transition: color .2s;
        }

        #user-badge .ub-logout:hover {
            color: var(--danger);
        }

        /* ── Classroom-Modal ── */
        #classroom-modal .modal-box {
            max-width: 500px;
            text-align: left;
            padding: 24px 28px;
        }

        #classroom-modal h2 {
            color: var(--clr-accent2);
            font-size: 1.1rem;
            margin-bottom: 4px;
        }

        .cr-tabs {
            display: flex;
            gap: 6px;
            margin: 14px 0 16px;
            border-bottom: 1px solid var(--border-color);
            padding-bottom: 10px;
        }

        .cr-tab {
            padding: 5px 14px;
            border-radius: 999px;
            border: 1px solid var(--border-color);
            background: var(--box-bg);
            color: var(--clr-muted);
            font-size: .8rem;
            font-weight: 700;
            cursor: pointer;
            transition: var(--transition-fast);
        }

        .cr-tab.active {
            background: var(--clr-accent2);
            color: var(--clr-on-accent);
            border-color: var(--clr-accent2);
        }

        .cr-panel {
            display: none;
        }

        .cr-panel.active {
            display: block;
        }

        .cr-field {
            margin-bottom: 12px;
        }

        .cr-field label {
            display: block;
            font-size: .75rem;
            font-weight: 700;
            color: var(--clr-muted);
            text-transform: uppercase;
            letter-spacing: .05em;
            margin-bottom: 5px;
        }

        .cr-field input {
            width: 100%;
            background: var(--clr-bg);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 8px 12px;
            color: var(--clr-text);
            font-size: .9rem;
            font-family: inherit;
            transition: border-color .2s;
            box-sizing: border-box;
        }

        .cr-field input:focus {
            outline: none;
            border-color: var(--clr-accent2);
        }

        .cr-field input::placeholder {
            color: var(--clr-muted);
            opacity: .7;
        }

        .cr-btn {
            width: 100%;
            padding: 9px;
            border-radius: 999px;
            border: none;
            background: linear-gradient(135deg, var(--clr-accent), var(--clr-accent2));
            color: var(--clr-on-accent);
            font-weight: 800;
            font-size: .9rem;
            cursor: pointer;
            transition: var(--transition-fast);
            margin-top: 4px;
        }

        .cr-btn:hover {
            filter: brightness(1.1);
            transform: translateY(-1px);
        }

        .cr-btn:disabled {
            opacity: .4;
            cursor: not-allowed;
            transform: none;
            filter: none;
        }

        .cr-result {
            margin-top: 14px;
            background: var(--box-bg);
            border: 1px solid var(--border-color);
            border-radius: 10px;
            padding: 14px;
            display: none;
        }

        .cr-result.visible {
            display: block;
        }

        .cr-result-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }

        .cr-result-row:last-child {
            margin-bottom: 0;
        }

        .cr-result-label {
            font-size: .72rem;
            color: var(--clr-muted);
            text-transform: uppercase;
            letter-spacing: .05em;
            font-weight: 700;
        }

        .cr-result-value {
            font-family: 'Courier New', monospace;
            font-size: 1rem;
            font-weight: 800;
            color: var(--clr-accent);
            letter-spacing: .1em;
            background: rgba(var(--success-rgb), 0.1);
            padding: 4px 12px;
            border-radius: 6px;
            border: 1px solid rgba(var(--success-rgb), 0.3);
            cursor: pointer;
            transition: background .2s;
        }

        .cr-result-value:hover {
            background: rgba(var(--success-rgb), 0.2);
        }

        .cr-result-value.teacher {
            color: var(--clr-accent2);
            background: rgba(var(--clr-accent2-rgb), 0.1);
            border-color: rgba(var(--clr-accent2-rgb), 0.3);
        }

        .cr-result-value.teacher:hover {
            background: rgba(var(--clr-accent2-rgb), 0.2);
        }

        .cr-warn {
            font-size: .75rem;
            color: var(--clr-text); /* v14.91: --warning auf der Tönung im Lightmode < 4,5:1; Warnfarbe bleibt am Rand */
            margin-top: 10px;
            padding: 7px 10px;
            background: rgba(var(--warning-rgb), 0.08);
            border-radius: 6px;
            border-left: 3px solid var(--warning);
        }

        .cr-error {
            margin-top: 10px;
            font-size: .8rem;
            color: var(--danger);
            background: rgba(var(--danger-rgb), 0.08);
            border-radius: 6px;
            padding: 7px 10px;
            display: none;
            border-left: 3px solid var(--danger);
        }

        .cr-error.visible {
            display: block;
        }

        /* NEU (25.09.2026, Anleitungen im Anmelde-Modal): dezenter
           Download-Link je Reiter, siehe crp-*-Panels in funci.html. */
        .cr-guide-link {
            display: block;
            margin-top: 16px;
            padding-top: 12px;
            border-top: 1px solid var(--border-color);
            font-size: .75rem;
            text-align: center;
        }

        .cr-guide-link a {
            color: var(--clr-accent2);
            text-decoration: none;
        }

        .cr-guide-link a:hover {
            text-decoration: underline;
        }

        /* ── Lehrer-Übersicht Modal ── */
        #teacher-modal .modal-box {
            max-width: min(1200px, 95vw);
            width: 95vw;
            text-align: left;
            padding: 22px 26px;
        }

        /* v14.69: im Vollbildmodus nutzt die Schüler-Übersicht (wie das
           PRIMM-Modal) fast den ganzen Bildschirm - max-height:90vh aus
           .modal-box wird bewusst überschrieben. */
        body.is-fullscreen #teacher-modal .modal-box {
            max-width: 98vw;
            width: 98vw;
            max-height: 96vh;
            max-height: 96dvh;
        }

        #teacher-modal h2 {
            color: var(--clr-accent2);
            font-size: 1.05rem;
        }

        /* BUGFIX v15.35 (live gemeldet, Erprobungsstunde: "Die Lehrerübersicht
           war zu breit, es sind aber auch keine Scrollbars zu sehen"): die
           Tabelle scrollte waagrecht in einem eigenen Rahmen, dessen
           Scrollleiste aber erst UNTER der letzten Schülerzeile lag (bei 31
           Schüler:innen weit außerhalb des sichtbaren Fensters). Jetzt ist
           das Fenster eine Spalte, die Tabelle füllt den restlichen Platz und
           scrollt darin in beide Richtungen - beide Scrollleisten liegen
           immer im Fenster, der Tabellenkopf bleibt stehen. Die Mindesthöhe
           sorgt bei sehr niedrigen Fenstern dafür, dass wie bisher das ganze
           Fenster scrollt. */
        #teacher-modal .modal-box {
            display: flex;
            flex-direction: column;
        }

        #teacher-modal .modal-box > * {
            flex-shrink: 0;
        }

        #teacher-modal .modal-box > #tv-table-wrap {
            flex: 1 1 auto;
            min-height: 240px;
            overflow: auto;
            border: 1px solid var(--border-color);
            border-radius: 8px;
        }

        #tv-table-wrap .tv-table thead th {
            position: sticky;
            top: 0;
            z-index: 2;
            background: var(--clr-surface);
        }

        .tv-meta {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            margin: 10px 0 14px;
        }

        .tv-pill {
            background: var(--box-bg);
            border: 1px solid var(--border-color);
            border-radius: 999px;
            padding: 3px 10px;
            font-size: .74rem;
            color: var(--clr-muted);
            /* Robuste Umbruch-Absicherung: Icon+Text, Datum und der
               "ändern"-Knopf können bei sehr schmalen Bildschirmen jeweils
               eigenständig umbrechen, statt dass Inhalte durch feste
               Inline-Anordnung (v.a. das <button>-Element mit seinem
               Browser-Standard "white-space:nowrap") abgeschnitten
               werden könnten. */
            display: inline-flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 3px;
            max-width: 100%;
        }

        .tv-pill strong {
            color: var(--clr-accent);
        }

        .tv-table {
            width: 100%;
            border-collapse: collapse;
            font-size: .78rem;
        }

        .tv-table th {
            text-align: left;
            padding: 6px 8px;
            color: var(--clr-muted);
            font-size: .68rem;
            text-transform: uppercase;
            letter-spacing: .06em;
            border-bottom: 1px solid var(--border-color);
            font-weight: 700;
        }

        .tv-table td {
            padding: 7px 8px;
            border-bottom: 1px solid var(--border-color);
            color: var(--clr-text);
            vertical-align: middle;
        }

        .tv-table tr:last-child td {
            border-bottom: none;
        }

        .tv-table tr:hover td {
            background: var(--box-bg);
        }

        .tv-progress-grid {
            display: flex;
            gap: 3px;
            flex-wrap: wrap;
            /* v15.35: mindestens ~10 Kästchen je Zeile - in schmalen Fenstern
               schrumpfte die Spalte sonst auf 2 Kästchen, während die anderen
               Spalten breit blieben; jetzt scrollt stattdessen die Tabelle
               (#tv-table-wrap) waagrecht */
            min-width: 220px;
        }

        /* v14.92: die Aufgabenspalte bekommt Vorrang - vorher nahm die
           Sterne-Spalte (bis zu 36 ⭐ in einer Zeile) den Platz, und die
           Kästchen standen nur zu viert je Zeile */
        #tv-tasks-header {
            width: 60%;
        }

        /* v14.92: Aufgaben-Kästchen je Schüler:in auf zwei Zeilen eingeklappt */
        .tv-tasks-toggle {
            display: block;
            margin-top: 4px;
            padding: 2px 8px;
            border: 1px solid var(--border-color);
            border-radius: 999px;
            background: var(--box-bg);
            color: var(--clr-accent2);
            font-size: .68rem;
            font-weight: 700;
            font-family: inherit;
            cursor: pointer;
        }

        .tv-tasks-toggle[hidden] {
            display: none;
        }

        .tv-tasks-toggle:hover {
            border-color: var(--clr-accent2);
        }

        .tv-tasks-toggle:focus-visible {
            outline: 2px solid var(--clr-accent2);
            outline-offset: 1px;
        }

        .tv-task-dot {
            width: 18px;
            height: 18px;
            border-radius: 4px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: .55rem;
            font-weight: 700;
            border: 1px solid var(--border-color);
            background: var(--box-bg);
            color: var(--clr-muted);
            cursor: default;
            position: relative;
        }

        .tv-task-dot.s1 {
            background: rgba(var(--success-rgb), 0.15);
            border-color: var(--success);
            color: var(--success);
        }

        .tv-task-dot.s2 {
            background: rgba(var(--success-rgb), 0.30);
            border-color: var(--success);
            color: var(--clr-text); /* v14.92: Grün auf 30 %-Grün nur 3,8-4,1:1 */
        }

        .tv-task-dot.s3 {
            background: var(--success);
            border-color: var(--success);
            color: var(--clr-on-accent);
        }

        .tv-task-dot::after {
            content: attr(data-tip);
            position: absolute;
            bottom: calc(100% + 4px);
            left: 50%;
            transform: translateX(-50%);
            background: var(--clr-surface);
            /* dunkler Hintergrund */
            border: 1px solid var(--border-color);
            border-radius: 5px;
            padding: 4px 8px;
            font-size: .65rem;
            white-space: nowrap;
            pointer-events: none;
            opacity: 0;
            transition: opacity .15s;
            z-index: 100;
            color: var(--clr-text);
            /* ← immer Text-Farbe, nie geerbt */
            font-weight: 600;
            box-shadow: 0 2px 8px rgba(0, 0, 0, .3);
        }

        .tv-task-dot:hover::after {
            opacity: 1;
        }

        /* Sicherstellen dass Dot-Farbe nicht auf Tooltip übergeht */
        .tv-task-dot.s3::after {
            color: var(--clr-text);
            /* ← explizit überschreiben */
            background: var(--clr-surface);
        }

        .tv-stars {
            font-size: .85rem;
            letter-spacing: 1px;
            /* v14.92: Emoji-Kette ohne Leerzeichen brach nicht um (bis zu
               36 ⭐ = 650 px Mindestbreite) und drückte die Aufgabenspalte */
            overflow-wrap: anywhere;
            min-width: min(190px, 14vw); /* ~10 Sterne je Zeile, auf schmalen Bildschirmen weniger */
        }

        .tv-last-seen {
            font-size: .7rem;
            color: var(--clr-muted);
        }

        .tv-online {
            display: inline-block;
            width: 7px;
            height: 7px;
            border-radius: 50%;
            background: var(--success);
            margin-right: 4px;
            vertical-align: middle;
            box-shadow: 0 0 5px var(--success);
        }

        .tv-empty {
            text-align: center;
            padding: 2rem;
            color: var(--clr-muted);
            font-size: .85rem;
        }

        /* v14.90 (live angefordert: einheitliches Look&Feel in der
           Schüler-Übersicht): vorher mischten die Knöpfe unten .cr-btn,
           .tv-close-btn, .mbtn und einen lila Verlauf außerhalb der
           Palette (Höhen 29/36/35/31 px, drei Schriftgrößen), das
           Aufgabensatz-Dropdown war unformatiert. Jetzt sind alle Knöpfe
           der Übersicht .mbtn mit gleicher Höhe; Emoji bestimmen die
           Höhe nicht mehr (inline-flex + feste line-height). */
        .mbtn.danger {
            border: 1px solid var(--danger);
            /* 0.06 statt 0.1 wie beim alten .tv-close-btn: dort lag der
               Kontrast im Darkmode bei 4,4:1 (< WCAG AA 4,5:1). */
            background: rgba(var(--danger-rgb), 0.06);
            color: var(--danger);
        }

        .mbtn.danger:hover {
            background: var(--danger);
            color: #fff;
        }

        #teacher-modal .mbtn {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 6px;
            box-sizing: border-box;
            min-height: 34px;
            padding: 0 18px;
            margin: 0;
            font-size: .82rem;
            font-weight: 700;
            line-height: 1.2;
            font-family: inherit;
        }

        .tv-actions {
            display: flex;
            gap: 8px;
            margin-top: 14px;
            flex-wrap: wrap;
            align-items: center;
        }

        #tv-set-filter {
            width: 100%;
            box-sizing: border-box;
            height: 34px;
            padding: 0 10px;
            border-radius: 8px;
            border: 1px solid var(--border-color);
            background: var(--clr-bg);
            color: var(--clr-text);
            font-size: .82rem;
            font-family: inherit;
            cursor: pointer;
            transition: border-color .2s;
        }

        #tv-set-filter:focus-visible {
            outline: 2px solid var(--clr-accent);
            outline-offset: 1px;
        }

        /* Auto-Save Indikator */
        #autosave-indicator {
            position: fixed;
            bottom: 66px;
            right: 14px;
            background: rgba(var(--success-rgb), 0.15);
            border: 1px solid var(--success);
            border-radius: 999px;
            padding: 3px 10px;
            font-size: .68rem;
            color: var(--success);
            opacity: 0;
            transition: opacity .4s;
            pointer-events: none;
            z-index: 500;
        }

        #autosave-indicator.show {
            opacity: 1;
        }

        /* Fußzeile unten rechts: Impressum, Datenschutz + Versionsanzeige
           (öffnet die Kurz-Changelog, siehe openChangelogModal()) - alle
           im selben dezenten Stil, auf Mobile ausgeblendet (Platz für die
           dortige Steuerleiste).
           GEÄNDERT v14.82 (PO-Vorgabe: "kein Bereich darf die Links
           überdecken"): bisher schwebte die Fußzeile per position:fixed
           ÜBER der App - bei Fensterbreiten um 1024px und am iPad hochkant
           verdeckte die Ziehleiste der rechten Spalte die Links, umgekehrt
           lag die Fußzeile über Knöpfen der Ausführen-Leiste (Stopp,
           Hinweis, Debuggen, Geschwindigkeit). Jetzt eine eigene Zeile als
           letztes Flex-Kind von body (siehe dort, display:flex column):
           #layout wird um ihre Höhe kürzer, nichts überlagert sich mehr.
           Eigener Hintergrund wie #control-bar: html/body haben keinen,
           sonst zeigt die Zeile im Darkmode Browser-Weiß (axe: Kontrast). */
        #footer-links {
            flex: 0 0 auto;
            background: var(--clr-bg);
            border-top: 1px solid var(--border-color);
            display: flex;
            justify-content: flex-end;
            align-items: center;
            gap: 8px;
            padding: 1px 10px 2px;
            font-size: .65rem;
            line-height: 1.3;
            font-family: 'Courier New', monospace;
        }

        /**
         * BUGFIX (Code-Review Maßnahme 7 - Barrierefreiheits-Audit): mit
         * echtem axe-core-Scan bestätigt - opacity:.55 auf dem bereits
         * korrekt kontrastierenden --clr-muted-Text verwässerte diesen
         * wieder unter den WCAG-AA-Mindestkontrast (nur 2.53:1 statt der
         * geforderten 4.5:1). .8 bleibt spürbar zurückhaltender als der
         * volle Text, reicht aber für ausreichenden Kontrast.
         */
        #footer-links a,
        #footer-links button {
            color: var(--clr-muted);
            opacity: .95;
            cursor: pointer;
            user-select: none;
            background: none;
            border: none;
            padding: 2px 4px;
            border-radius: 4px;
            font: inherit;
            text-decoration: none;
            transition: opacity .15s, background .15s;
        }

        #footer-links a:hover,
        #footer-links a:focus-visible,
        #footer-links button:hover,
        #footer-links button:focus-visible {
            opacity: 1;
            background: var(--clr-surface);
            text-decoration: underline;
        }

        .changelog-entry {
            margin-bottom: 14px;
            padding-bottom: 12px;
            border-bottom: 1px solid var(--border-color);
        }

        .changelog-entry:last-child {
            border-bottom: none;
            margin-bottom: 0;
            padding-bottom: 0;
        }

        .changelog-entry h3 {
            font-size: .88rem;
            color: var(--clr-accent2);
            margin-bottom: 6px;
        }

        .changelog-entry .changelog-date {
            font-weight: 400;
            color: var(--clr-muted);
            font-size: .75rem;
        }

        .changelog-entry ul {
            margin: 0 0 0 18px;
            padding: 0;
            font-size: .82rem;
            line-height: 1.6;
        }

        /* ── Blockly Prompt Dialog (Custom) ── */
        #blockly-prompt-overlay {
            position: fixed;
            inset: 0;
            background: rgba(0, 0, 0, 0.6);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 999999;
            opacity: 0;
            pointer-events: none;
            transition: opacity .2s;
            backdrop-filter: blur(3px);
        }

        #blockly-prompt-overlay.show {
            opacity: 1;
            pointer-events: all;
        }

        #blockly-prompt-box {
            background: var(--clr-surface);
            border: 1px solid var(--clr-accent2);
            border-radius: 12px;
            padding: 24px 28px;
            min-width: 320px;
            max-width: 400px;
            width: 90%;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
        }

        #blockly-prompt-box h3 {
            margin: 0 0 14px 0;
            font-size: .95rem;
            color: var(--clr-accent2);
            font-weight: 700;
        }

        #blockly-prompt-box p {
            font-size: .82rem;
            color: var(--clr-muted);
            margin: 0 0 10px 0;
            line-height: 1.5;
        }

        #blockly-prompt-input {
            width: 100%;
            background: var(--clr-bg);
            border: 1px solid var(--clr-accent2);
            border-radius: 8px;
            padding: 9px 12px;
            color: var(--clr-text);
            font-size: .95rem;
            font-family: inherit;
            box-sizing: border-box;
            margin-bottom: 14px;
            outline: none;
        }

        #blockly-prompt-input:focus {
            border-color: var(--clr-accent);
            box-shadow: 0 0 0 2px rgba(var(--success-rgb), 0.2);
        }

        /* Bestätigungs-Modus (_appConfirm() in funci.html, ersetzt das
           native confirm() der Lehrer-Ansicht): nur Nachricht + Knöpfe. */
        #blockly-prompt-overlay.confirm-mode #blockly-prompt-title,
        #blockly-prompt-overlay.confirm-mode #blockly-prompt-input {
            display: none;
        }

        #blockly-prompt-overlay.confirm-mode #blockly-prompt-msg {
            white-space: pre-line;
            color: var(--clr-text);
            font-size: .9rem;
            margin-bottom: 18px;
        }

        .blockly-prompt-buttons {
            display: flex;
            gap: 8px;
            justify-content: flex-end;
        }

        .blockly-prompt-btn {
            padding: 7px 18px;
            border-radius: 999px;
            border: 1px solid var(--border-color);
            font-size: .82rem;
            font-weight: 700;
            cursor: pointer;
            transition: all .15s;
        }

        .blockly-prompt-btn.ok {
            background: linear-gradient(135deg, var(--clr-accent), var(--clr-accent2));
            color: var(--clr-on-accent);
            border: none;
        }

        .blockly-prompt-btn.ok:hover {
            filter: brightness(1.1);
            transform: translateY(-1px);
        }

        .blockly-prompt-btn.cancel {
            background: var(--box-bg);
            color: var(--clr-muted);
        }

        .blockly-prompt-btn.cancel:hover {
            color: var(--clr-text);
        }

        .tv-student-code {
            font-family: 'Courier New', monospace;
            font-size: .75rem;
            font-weight: 700;
            color: var(--clr-accent2);
            background: rgba(var(--clr-accent2-rgb), 0.1);
            border: 1px solid rgba(var(--clr-accent2-rgb), 0.3);
            border-radius: 5px;
            padding: 2px 7px;
            cursor: pointer;
            transition: background .2s;
            white-space: nowrap;
            letter-spacing: .05em;
        }

        .tv-student-code:hover {
            background: rgba(var(--clr-accent2-rgb), 0.22);
            border-color: var(--clr-accent2);
        }

        #btn-back-to-teacher {
            padding: 4px 14px;
            border-radius: 999px;
            border: 1px solid var(--warning);
            background: rgba(var(--warning-rgb), 0.12);
            color: var(--warning);
            font-size: .76rem;
            font-weight: 700;
            cursor: pointer;
            transition: var(--transition-fast);
            white-space: nowrap;
            display: none;
        }

        #btn-back-to-teacher:hover {
            background: rgba(var(--warning-rgb), 0.25);
            transform: translateY(-1px);
        }

        /* ── Print / PDF ── */
        @media print {
            body * {
                visibility: hidden;
            }

            #print-area,
            #print-area * {
                visibility: visible;
            }

            #print-area {
                position: fixed;
                inset: 0;
                padding: 24px 32px;
                background: #fff;
                color: #000;
                font-family: Arial, sans-serif;
                font-size: 11pt;
            }
        }

        #print-area {
            display: none;
        }

        .print-header {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            margin-bottom: 18px;
            padding-bottom: 12px;
            border-bottom: 2px solid #333;
        }

        .print-header h1 {
            font-size: 16pt;
            margin: 0 0 4px 0;
        }

        .print-header p {
            font-size: 9pt;
            color: #555;
            margin: 2px 0;
        }

        .print-codes {
            display: flex;
            gap: 24px;
            margin-bottom: 20px;
        }

        .print-code-box {
            border: 1px solid #aaa;
            border-radius: 6px;
            padding: 8px 16px;
            font-size: 10pt;
        }

        .print-code-box strong {
            display: block;
            font-size: 14pt;
            letter-spacing: 2px;
            margin-top: 2px;
        }

        .print-table {
            width: 100%;
            border-collapse: collapse;
            font-size: 9.5pt;
        }

        .print-table th {
            background: #e8e8e8;
            border: 1px solid #aaa;
            padding: 5px 8px;
            text-align: left;
            font-weight: 700;
        }

        .print-table td {
            border: 1px solid #ccc;
            padding: 5px 8px;
            vertical-align: middle;
        }

        .print-table tr:nth-child(even) td {
            background: #f7f7f7;
        }

        .print-dot {
            display: inline-block;
            width: 13px;
            height: 13px;
            border-radius: 3px;
            border: 1px solid #aaa;
            margin: 1px;
            font-size: 7pt;
            text-align: center;
            line-height: 13px;
            vertical-align: middle;
            background: #eee;
        }

        .print-dot.s1 {
            background: #bbf7d0;
            border-color: #16a34a;
        }

        .print-dot.s2 {
            background: #86efac;
            border-color: #16a34a;
        }

        .print-dot.s3 {
            background: #22c55e;
            border-color: #15803d;
            color: #fff;
        }

        .print-footer {
            margin-top: 20px;
            font-size: 8pt;
            color: #888;
            text-align: right;
            border-top: 1px solid #ccc;
            padding-top: 6px;
        }

        /* Reset-Code Button in Tabelle */
        .tv-reset-btn {
            padding: 2px 8px;
            border-radius: 999px;
            border: 1px solid var(--warning);
            background: rgba(var(--warning-rgb), 0.1);
            color: var(--warning);
            font-size: .68rem;
            font-weight: 700;
            cursor: pointer;
            transition: var(--transition-fast);
            white-space: nowrap;
        }

        .tv-reset-btn:hover {
            background: rgba(var(--warning-rgb), 0.25);
        }

        /**
         * NEU (live gemeldet): Umschalter für die Basis-/Standardansicht
         * pro Schüler:in in der Schüler-Übersicht - derselbe Pillen-Stil
         * wie .tv-reset-btn oben, mit zwei unterscheidbaren Farben je
         * nach aktuell aktivem Modus (Standard = Akzentfarbe, Basis =
         * gedämpfter/neutraler, damit auf einen Blick erkennbar ist,
         * welche Schüler:innen aktuell in der eingeschränkteren
         * Basisansicht arbeiten).
         */
        .tv-viewmode-btn {
            padding: 2px 8px;
            border-radius: 999px;
            border: 1px solid var(--clr-accent2);
            background: rgba(var(--clr-accent2-rgb), 0.1);
            color: var(--clr-accent2);
            font-size: .68rem;
            font-weight: 700;
            cursor: pointer;
            transition: var(--transition-fast);
            white-space: nowrap;
        }

        .tv-viewmode-btn:hover {
            background: rgba(var(--clr-accent2-rgb), 0.25);
        }

        .tv-viewmode-btn.basic {
            border-color: var(--clr-muted);
            background: rgba(var(--warning-rgb), 0.1);
            color: var(--warning);
        }

        .tv-viewmode-btn.basic:hover {
            background: rgba(var(--warning-rgb), 0.25);
        }

        /* Lehrer-generierte Sterne (nicht zur Schüler-Summe zählend) */
        .teacher-star {
            filter: hue-rotate(200deg) saturate(0.6);
            opacity: 0.7;
            position: relative;
        }

        .teacher-star::after {
            content: '👩‍🏫';
            font-size: .5rem;
            position: absolute;
            top: -4px;
            right: -6px;
        }

        #app-header .stat-pill.teacher-view-pill {
            background: rgba(var(--warning-rgb), 0.15);
            border-color: var(--warning);
            color: var(--warning);
        }

        /* Kollabierter Task-Panel */
        #task-panel {
            background: var(--clr-surface);
            border-bottom: 1px solid var(--border-color);
            padding: 8px 14px;
            flex-shrink: 0;
            transition: padding .25s ease;
        }

        #task-panel.collapsed {
            padding: 4px 14px;
        }

        /**
         * BUGFIX (05.10.2026, v14.44, live gemeldet am iPad: "Die
         * Ausführen-Leiste ist immer dann nur halb zu sehen, wenn ich den
         * Ausgabebereich verkleinere oder vergrößere"): in #center
         * (Spalte, overflow:hidden) gab nur der Programmierbereich Höhe
         * ab - die Aufgabenbeschreibung (#task-panel, flex-shrink:0, mit
         * Bild bis ~460px) nicht. Wurde der Programmierbereich durch
         * einen breiten Ausgabebereich schmal, brach die Steuerleiste auf
         * bis zu fünf Zeilen (~275px) um; bei niedriger Höhe (iPad quer
         * mit Safari-Leisten) reichte der Platz nicht und die Leiste
         * wurde unten abgeschnitten. Jetzt gibt auch die Beschreibung
         * Höhe ab (ihr Textbereich scrollt, Kopfzeile und Tipp-Box
         * bleiben), und die Steuerleiste bleibt einzeilig - bei
         * Platzmangel horizontal scrollbar statt höher (gleiches
         * Sicherheitsnetz wie bei #mob-control-bar).
         */
        #task-panel {
            display: flex;
            flex-direction: column;
            flex-shrink: 1;
            min-height: 0;
        }

        #task-panel > * {
            flex-shrink: 0;
        }

        #task-panel > #task-panel-body {
            flex-shrink: 1;
            min-height: 0;
        }

        #control-bar {
            flex-wrap: nowrap;
            overflow-x: auto;
            overflow-y: hidden;
            -webkit-overflow-scrolling: touch;
            scrollbar-width: thin;
        }

        #control-bar > * {
            flex-shrink: 0;
        }

        /* Programmierbereich behält im Desktop-/Tablet-Layout immer eine
           brauchbare Höhe - vorher gibt die Aufgabenbeschreibung nach
           (ihr Textbereich scrollt). Smartphone-Layout unberührt. */
        @media (min-width: 701px) {
            #blockly-wrap {
                min-height: 160px;
            }
        }

        #task-panel-body {
            overflow-y: auto;
            overflow-x: hidden;
            max-height: 300px;
            transition: max-height .3s ease, opacity .25s ease;
            opacity: 1;
        }

        #task-panel-body.collapsed {
            max-height: 0;
            opacity: 0;
        }

        #task-collapse-btn {
            background: none;
            border: 1px solid var(--border-color);
            border-radius: 999px;
            color: var(--clr-muted);
            cursor: pointer;
            font-size: .72rem;
            padding: 2px 9px;
            transition: var(--transition-fast);
            white-space: nowrap;
            display: flex;
            align-items: center;
            gap: 4px;
        }

        #task-collapse-btn:hover {
            color: var(--clr-text);
            border-color: var(--clr-accent);
            background: rgba(var(--success-rgb), 0.08);
        }

        /* ── Unangemeldeter Zustand ──
           Der Header selbst übernimmt jetzt die Warnung (statt eines
           separaten Banners darunter): pulsierender Warnfarbverlauf,
           wackelndes Warnzeichen, blinkender "Jetzt anmelden"-Knopf.
           WICHTIG: explizite (helle) Textfarbe hier gesetzt - vorher
           erbte der Header seine Textfarbe vom aktiven Theme; im
           Hell-Modus war das eine dunkle Farbe, die auf diesem
           (unabhängig vom Theme immer dunklen) Warn-Hintergrund
           praktisch unsichtbar war - betraf auch das Vollbild-Symbol. */
        body.not-logged-in #app-header {
            background: linear-gradient(135deg, #451a03, #78350f);
            border-bottom: 2px solid #f59e0b;
            color: #fef3c7;
            animation: headerWarnPulse 3s ease-in-out infinite;
        }

        @keyframes headerWarnPulse {

            0%,
            100% {
                background: linear-gradient(135deg, #451a03, #78350f);
            }

            50% {
                background: linear-gradient(135deg, #5c2504, #92400e);
            }
        }

        body.not-logged-in #app-header h1 {
            background: linear-gradient(90deg, #f59e0b, #fbbf24);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        body.not-logged-in #app-header .sub {
            color: #fcd34d;
            opacity: .85;
        }

        /**
         * GEÄNDERT (01.10.2026, live angefordert: "Nutze im nicht-
         * angemeldeten Zustand sowohl im Light- wie auch im Darkmode das
         * dunkle FUNCI-Logo oben links."). Die Knöpfe der Kopfleiste
         * bekommen ihr Design im unangemeldeten Zustand weiter unten (bei
         * #hdr-task-info: alle im Stil von "Jetzt anmelden").
         * Logo: der Warn-Verlauf ist unabhängig vom Theme immer dunkel,
         * deshalb auch im Hellmodus das dunkle Logo (gleiche Spezifität
         * wie die html[data-theme="light"]-Regeln oben, steht später).
         */
        body.not-logged-in #app-header .logo img.header-logo-img-dark {
            display: block;
        }

        body.not-logged-in #app-header .logo img.header-logo-img-light {
            display: none;
        }

        /* Warnzeichen im Header - nur im unangemeldeten Zustand sichtbar,
           wackelt dezent um Aufmerksamkeit zu erregen. */
        .not-logged-in-icon {
            display: none;
            font-size: 1.5rem;
            flex-shrink: 0;
        }

        body.not-logged-in .not-logged-in-icon {
            display: block;
            animation: shake 2.5s ease-in-out infinite;
        }

        @keyframes shake {

            0%,
            90%,
            100% {
                transform: rotate(0deg);
            }

            92% {
                transform: rotate(-10deg);
            }

            96% {
                transform: rotate(10deg);
            }
        }

        /* "Klassenraum"-Knopf wird im unangemeldeten Zustand zum
           auffälligen "Jetzt anmelden"-Knopf. Zwei Beschriftungen als
           Kind-Elemente, per CSS ein-/ausgeblendet (analog zum
           bestehenden Muster bei Vollbild-/Theme-Knopf) statt den Text
           per JS umzuschreiben. Die eigentliche Blink-/Leucht-Optik
           (Farbverlauf, Puls-Animation) übernimmt weiterhin die
           bestehende Regel weiter unten (body.not-logged-in
           #btn-classroom, .classroomGlow) - hier nur der Text-Swap. */
        #btn-classroom .btn-classroom-attention {
            display: none;
        }

        body.not-logged-in #btn-classroom .btn-classroom-normal {
            display: none;
        }

        body.not-logged-in #btn-classroom .btn-classroom-attention {
            display: inline;
        }

        /**
         * GEÄNDERT (30.09.2026, live angefordert: "Alles außer der
         * Titelleiste und deren Komponenten soll denselben Style im
         * light- und darkmode haben wie im angemeldeten Zustand"): die
         * Warnfärbung (dunkles Braun/Amber) für Sidebar, Kategorien,
         * aktive Aufgabe, Aufgabentext, Steuerleiste, Ausführen/Debuggen,
         * Panel-Köpfe, Schwierigkeits-Knöpfe, Blockzähler und mobile
         * Navigation ist entfallen - die Warnung übernimmt allein die
         * Titelleiste (#app-header samt Warnzeichen, Stat-Pills und
         * "Jetzt anmelden"-Knopf). Damit sind auch die früheren
         * Kontrast-Nachbesserungen für diese Stellen (axe-core
         * "color-contrast") gegenstandslos: dort gelten jetzt wieder die
         * normalen, bereits geprüften Theme-Farben.
         */

        /* BUGFIX: die Musterlösung war bisher die einzige Steuerleisten-
           Funktion OHNE jede Einschränkung im unangemeldeten Zustand -
           anders als bei "Ausführen"/"Debuggen" (reine Lernwerkzeuge,
           bleiben bewusst nutzbar) gibt es hier keinen guten Grund, sie
           ohne Anmeldung zugänglich zu lassen. Betrifft beide Stellen,
           an denen der Knopf vorkommt (Desktop-Steuerleiste UND
           mobile Navigationsleiste, gemeinsame Klasse .btn-solution).
           Reine Zugriffssperre, keine Farbgebung - bleibt deshalb. */
        body.not-logged-in .btn-solution {
            display: none;
        }

        /**
         * GEÄNDERT (01.10.2026, live angefordert): Die
         * Aufgaben-Titelleiste in der Kopfleiste ("Aufgabensatz ·
         * Aufgabe", #hdr-task-info) bekommt dagegen - unabhängig vom
         * Theme - dasselbe Design wie der "Jetzt anmelden"-Knopf
         * (Verlauf, dunkle fette Schrift, Leuchten), bewusst OHNE dessen
         * Puls-Animation (kein zweiter Aufruf zum Anklicken).
         */
        /**
         * GEÄNDERT (01.10.2026, live angefordert: "Bitte nutze für alle
         * Buttons in der Kopfleiste (Sterne, erledigte Aufgaben, Badges,
         * Streaks, Standard-/Basisansicht, Theme, Vollbild und Sprachwahl)
         * im unangemeldeten Zustand das Design des Jetzt-anmelden-
         * Buttons"): dieselbe Optik wie die Aufgaben-Titelleiste
         * (#hdr-task-info) und #btn-classroom - orangener Verlauf, dunkle
         * fette Schrift, Leuchten, Pillen-/Kreisform (border-radius:999px
         * macht die quadratischen Symbol-Knöpfe rund). Wie bei der
         * Titelleiste OHNE die Puls-Animation - die bleibt dem "Jetzt
         * anmelden"-Knopf als eigentlichem Aufruf vorbehalten. Unabhängig
         * vom Theme (der Warn-Verlauf ist immer dunkel).
         */
        body.not-logged-in #app-header #hdr-task-info,
        body.not-logged-in #app-header .stat-pill,
        body.not-logged-in #app-header .theme-toggle {
            background: linear-gradient(135deg, #f59e0b, #d97706);
            border: 1px solid #f59e0b;
            color: #1a1200;
            font-weight: 800;
            box-shadow: 0 0 14px rgba(var(--warning-rgb), 0.45);
        }

        body.not-logged-in #app-header .stat-pill,
        body.not-logged-in #app-header .theme-toggle {
            border-radius: 999px;
            cursor: pointer;
            transition: transform .15s ease;
        }

        /* BUGFIX (v14.28): mit fetter Schrift brachen die Stat-Pills bei
           voller Kopfleiste zweizeilig um ("⭐ 0 / Sterne") - einzeilig
           halten, Platz gibt die Aufgaben-Titelleiste per "…" ab. */
        body.not-logged-in #app-header .stat-pill {
            white-space: nowrap;
            flex-shrink: 0;
        }

        body.not-logged-in #app-header .stat-pill strong {
            color: inherit;
        }

        body.not-logged-in #app-header .stat-pill:hover,
        body.not-logged-in #app-header .theme-toggle:hover {
            transform: scale(1.06);
        }

        /* Klassenraum-Button */
        body.not-logged-in #btn-classroom {
            background: linear-gradient(135deg, #f59e0b, #d97706);
            border-color: #f59e0b;
            color: #1a1200;
            font-weight: 800;
            box-shadow: 0 0 14px rgba(var(--warning-rgb), 0.45);
            animation: classroomGlow 2s ease-in-out infinite;
        }

        @keyframes classroomGlow {

            0%,
            100% {
                box-shadow: 0 0 10px rgba(var(--warning-rgb), 0.4);
            }

            50% {
                box-shadow: 0 0 22px rgba(var(--warning-rgb), 0.75);
            }
        }

        body.not-logged-in #btn-classroom:hover {
            transform: scale(1.06);
        }

        /* ════════════════════════════════════════════════════════════
   RESPONSIVE / MOBILE
════════════════════════════════════════════════════════════ */

        /* ── Mobile Navigation Bar (nur mobil sichtbar) ── */
        #mobile-nav {
            display: none;
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            height: 56px;
            background: var(--clr-surface);
            border-top: 1px solid var(--border-color);
            z-index: 200;
            flex-direction: row;
            align-items: stretch;
            box-shadow: 0 -4px 20px rgba(0, 0, 0, .3);
        }

        .mob-nav-btn {
            flex: 1;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            gap: 3px;
            background: none;
            border: none;
            color: var(--clr-muted);
            font-size: .6rem;
            font-weight: 700;
            cursor: pointer;
            transition: color .2s, background .2s;
            padding: 4px 2px;
            border-top: 2px solid transparent;
            text-transform: uppercase;
            letter-spacing: .04em;
        }

        .mob-nav-btn .mob-icon {
            font-size: 1.3rem;
            line-height: 1;
        }

        .mob-nav-btn.active {
            color: var(--clr-accent);
            border-top-color: var(--clr-accent);
            background: rgba(var(--success-rgb), .06);
        }

        /* ── Mobile Panel Slides ── */
        #mob-panel-tasks,
        #mob-panel-workspace,
        #mob-panel-output {
            display: none;
        }

        /* ── Slide-In Drawer (Aufgabenliste) ── */
        #mob-drawer {
            position: fixed;
            top: 0;
            left: 0;
            bottom: 56px;
            width: min(320px, 88vw);
            background: var(--clr-surface);
            z-index: 300;
            transform: translateX(-100%);
            transition: transform .28s cubic-bezier(.4, 0, .2, 1);
            display: flex;
            flex-direction: column;
            box-shadow: 4px 0 24px rgba(0, 0, 0, .4);
        }

        #mob-drawer.open {
            transform: translateX(0);
        }

        #mob-drawer-overlay {
            position: fixed;
            inset: 0;
            background: rgba(0, 0, 0, .55);
            z-index: 299;
            opacity: 0;
            pointer-events: none;
            transition: opacity .28s;
            backdrop-filter: blur(2px);
        }

        #mob-drawer-overlay.show {
            opacity: 1;
            pointer-events: all;
        }

        #mob-drawer-head {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 12px 16px;
            background: var(--box-bg);
            border-bottom: 1px solid var(--border-color);
            flex-shrink: 0;
        }

        /* v14.83: Impressum + Datenschutz am Smartphone (Fußzeile ist dort
           ausgeblendet), feste Zeile am Ende der Aufgabenliste */
        #mob-drawer-legal {
            flex-shrink: 0;
            display: flex;
            justify-content: center;
            gap: 20px;
            padding: 10px 16px;
            background: var(--box-bg);
            border-top: 1px solid var(--border-color);
            font-size: .8rem;
        }

        #mob-drawer-legal a {
            color: var(--clr-text);
            text-decoration: underline;
            padding: 4px 6px;
        }

        #mob-drawer-head h2 {
            margin: 0;
            font-weight: 800;
            font-size: .9rem;
            color: var(--clr-accent);
        }

        #mob-drawer-head span {
            font-weight: 800;
            font-size: .9rem;
            color: var(--clr-accent);
        }

        #mob-drawer-close {
            background: none;
            border: 1px solid var(--border-color);
            border-radius: 50%;
            width: 30px;
            height: 30px;
            color: var(--clr-muted);
            font-size: 1rem;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all .2s;
        }

        #mob-drawer-close:hover {
            color: var(--danger);
            border-color: var(--danger);
        }

        /* ── Mobile Task Panel ── */
        #mob-task-panel {
            display: none;
            background: var(--clr-surface);
            border-bottom: 1px solid var(--border-color);
            padding: 8px 12px;
            flex-shrink: 0;
        }

        #mob-task-title {
            font-size: .85rem;
            font-weight: 800;
            color: var(--clr-text);
            margin: 0 0 6px 0;
        }

        #mob-diff-row {
            display: flex;
            gap: 5px;
            margin-bottom: 6px;
            flex-wrap: wrap;
        }

        #mob-desc-toggle {
            width: 100%;
            background: var(--box-bg);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 6px 10px;
            color: var(--clr-muted);
            font-size: .75rem;
            cursor: pointer;
            text-align: left;
            display: flex;
            justify-content: space-between;
            align-items: center;
            transition: all .2s;
        }

        #mob-desc-toggle:hover {
            border-color: var(--clr-accent);
            color: var(--clr-text);
        }

        #mob-desc-body {
            overflow: hidden;
            max-height: 0;
            transition: max-height .3s ease;
        }

        #mob-desc-body.open {
            max-height: 300px;
            overflow-y: auto;
        }

        #mob-desc-content {
            background: var(--box-bg);
            border-left: 3px solid var(--clr-accent);
            padding: 8px 10px;
            border-radius: 0 8px 8px 0;
            font-size: .78rem;
            line-height: 1.55;
            color: var(--clr-muted);
            margin-top: 6px;
        }

        #mob-desc-content code {
            background: rgba(var(--success-rgb), .12);
            padding: 1px 4px;
            border-radius: 3px;
            font-family: monospace;
            color: var(--clr-accent);
        }

        /* ── Mobile Control Bar ── */
        #mob-control-bar {
            display: none;
            /* Fest positioniert oberhalb der unteren Navigationsleiste,
               statt normaler Teil des Flex-Layouts zu sein. Vorher konnte
               ein hoher #mob-task-panel-Inhalt (z.B. aufgeklappte, lange
               Aufgabenbeschreibung) den verfügbaren Platz für #blockly-wrap
               (flex:1) bis auf 0 zusammendrücken – und danach blieb kein
               Platz mehr für die Steuerleiste übrig, sie wurde vom
               overflow:hidden des Elternelements abgeschnitten und war
               unsichtbar. Mit position:fixed ist sie davon komplett
               unabhängig und immer an derselben Stelle sichtbar. */
            position: fixed;
            left: 0;
            right: 0;
            bottom: 56px;
            /* über der Steuerleiste, aber unter Modals (die 1000+ nutzen) */
            z-index: 60;
            background: var(--clr-bg);
            padding: 6px 8px;
            gap: 5px;
            border-top: 1px solid var(--border-color);
            /* BUGFIX (Smartphone: Ausführen-/Debug-Knopf teilweise
               verdeckt): Bei schmalen Bildschirmen (v.a. seit dem
               Geschwindigkeitsregler dazugekommen ist, siehe
               .exec-speed-wrap-mob) reichen 8 Elemente (7 Knöpfe +
               Regler) in einer Zeile oft nicht mehr nebeneinander -
               flex-wrap:wrap schob dann einzelne Knöpfe in eine zweite
               Zeile, deren exakte Position/Sichtbarkeit sich je nach
               Gerät/Browser/Zoomstufe unterschiedlich verhielt (genau
               das beobachtete, browserabhängige Verschwinden von
               Knöpfen). Statt zu wraken, jetzt bewusst NIE umbrechen
               (nowrap) und stattdessen horizontal scrollbar - dadurch
               bleibt JEDER Knopf über eine Wisch-Geste zuverlässig
               erreichbar, unabhängig von der Bildschirmbreite, statt
               unvorhersehbar zu verschwinden. */
            flex-wrap: nowrap;
            overflow-x: auto;
            -webkit-overflow-scrolling: touch;
            scrollbar-width: none;
            justify-content: flex-start;
            /* Sicherheitsabstand für Geräte mit Home-Indicator (iPhone etc.) */
            padding-bottom: calc(6px + env(safe-area-inset-bottom, 0px));
        }

        #mob-control-bar::-webkit-scrollbar {
            display: none;
        }

        /* Knöpfe UND Geschwindigkeitsregler dürfen beim Scrollen nicht
           gestaucht werden - behalten immer ihre natürliche Breite,
           genau wie bei einer horizontal scrollbaren Symbolleiste
           erwartet. */
        #mob-control-bar > * {
            flex-shrink: 0;
        }

        .mob-ctrl-btn {
            padding: 7px 10px;
            height: 34px;
            box-sizing: border-box;
            line-height: 1;
            border: 1px solid var(--border-color);
            border-radius: 999px;
            cursor: pointer;
            font-size: .72rem;
            font-weight: 700;
            display: flex;
            align-items: center;
            gap: 3px;
            background: var(--box-bg);
            color: var(--clr-text);
            transition: all .15s;
            white-space: nowrap;
        }

        .mob-ctrl-btn:active {
            transform: scale(.95);
        }

        .mob-ctrl-btn.btn-run {
            background: rgba(var(--success-rgb), .03);
            border-color: var(--success);
            color: var(--success);
        }

        .mob-ctrl-btn.btn-debug {
            background: rgba(var(--warning-rgb), .05);
            border-color: var(--warning);
            color: var(--warning);
        }

        .mob-ctrl-btn.btn-step {
            background: rgba(var(--clr-accent2-rgb), .12);
            border-color: var(--clr-accent2);
            color: var(--clr-accent2);
        }

        .mob-ctrl-btn.btn-stop {
            background: rgba(var(--danger-rgb), .12);
            border-color: var(--danger);
            color: var(--danger);
        }

        .mob-ctrl-btn.btn-hint {
            background: rgba(167, 139, 250, .12);
            border-color: var(--hint-purple);
            color: var(--hint-purple);
        }

        .mob-ctrl-btn.btn-clear {
            color: var(--clr-muted);
        }

        .mob-ctrl-btn.btn-solution {
            background: rgba(var(--danger-rgb), .07);
            border-color: var(--danger);
            color: var(--danger);
        }

        /* ── Mobile Output Panel ── */
        #mob-output-panel {
            display: none;
            flex-direction: column;
            height: 100%;
            overflow: hidden;
        }

        /**
         * BUGFIX (live gemeldet: "horizontale Schiebeleiste ... in der
         * Smartphone-View ist sie gar nicht vorhanden"): feste Höhe
         * durch dieselbe var()-basierte, per Ziehen anpassbare Technik
         * wie beim Desktop-Gegenstück (--sim-area-height) ersetzt, mit
         * eigener Variable --mob-sim-area-height (bewusst getrennt von
         * der Desktop-Variable, da beide Ansichten unterschiedliche
         * Bildschirmgrößen und damit unterschiedliche sinnvolle
         * Ausgangshöhen haben).
         */
        #mob-sim-wrap {
            height: var(--mob-sim-area-height, 160px);
            background: #0e1a2b;
            flex-shrink: 0;
            border-bottom: 1px solid var(--border-color);
            cursor: grab;
            position: relative;
        }

        #mob-sim-wrap canvas {
            width: 100%;
            height: 100%;
            display: block;
        }

        #mob-grid-canvas-wrap {
            height: var(--mob-sim-area-height, 160px);
            background: #0e1a2b;
            flex-shrink: 0;
            border-bottom: 1px solid var(--border-color);
            cursor: grab;
            position: relative;
        }

        #mob-grid-canvas-wrap canvas {
            width: 100%;
            height: 100%;
            display: block;
        }

        /** Analog zu #sim-resize-handle (Desktop) - siehe dort für
         *  ausführliche Kommentare zur Funktionsweise. */
        #mob-sim-resize-handle {
            flex-shrink: 0;
            height: 20px;
            /* etwas höher als am Desktop (9px) - großzügigere Trefferfläche
               für Touch-Bedienung */
            margin: -1px 0;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: row-resize;
            background: var(--clr-bg, #1a2332);
            border-bottom: 1px solid var(--border-color, #33415c);
            touch-action: none;
        }

        #mob-sim-resize-handle:hover,
        #mob-sim-resize-handle:focus-visible,
        #mob-sim-resize-handle.dragging {
            background: rgba(var(--success-rgb), 0.12);
            outline: none;
        }

        #mob-sim-resize-handle:hover .sim-resize-grip,
        #mob-sim-resize-handle:focus-visible .sim-resize-grip,
        #mob-sim-resize-handle.dragging .sim-resize-grip {
            opacity: 1;
            background: var(--clr-accent);
        }

        #mob-console-wrap {
            flex: 1;
            overflow-y: auto;
            padding: 6px;
            font-family: 'Courier New', monospace;
            font-size: var(--console-font-size, .82rem);
        }

        /**
         * BUGFIX (Audit für responsives Design): mobile Unter-Reiter für
         * Code/Visualisierung, ergänzend zur bisher einzigen, immer
         * sichtbaren Konsole - dieselbe flex:1/overflow-y:auto-Grundlage
         * wie #mob-console-wrap, damit alle drei sich innerhalb von
         * #mob-output-panel identisch verhalten, je nachdem, welcher
         * gerade (via .output-hidden) sichtbar ist.
         */
        #mob-code-wrap,
        #mob-viz-wrap {
            flex: 1;
            overflow-y: auto;
            display: flex;
            flex-direction: column;
            min-height: 0;
        }

        #mob-code-output {
            padding: 6px;
            font-family: 'Courier New', monospace;
            font-size: var(--console-font-size, .82rem);
        }

        #mob-viz-output {
            flex: 1;
            overflow: auto;
            padding: 8px;
            font-size: .72rem;
        }

        .mob-output-tabs-row {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 6px;
            padding: 6px 8px;
            border-top: 1px solid var(--border-color);
            flex-shrink: 0;
        }

        .mob-output-tabs {
            overflow-x: auto;
            flex-wrap: nowrap;
            scrollbar-width: none;
            min-width: 0;
        }

        .mob-output-tabs::-webkit-scrollbar {
            display: none;
        }

        .mob-output-tabs .output-tab-btn {
            flex-shrink: 0;
        }

        #mob-clear-console-btn {
            flex-shrink: 0;
            background: none;
            border: none;
            color: var(--clr-muted);
            font-size: .85rem;
            cursor: pointer;
            padding: 2px 6px;
        }

        /* NEU (Anpassung an den Code-Reiter): dieselbe .viz-type-btn-
           Klasse wie im Desktop-Toolbar oben - profitiert automatisch
           von der dortigen kompakten Umgestaltung. Padding hier
           geringfügig reduziert, um näher am kompakten Vorbild zu bleiben. */
        #mob-viz-type-toggle {
            display: flex;
            gap: 4px;
            padding: 4px 6px;
            flex-shrink: 0;
        }

        #mob-var-bar {
            padding: 5px 8px;
            border-top: 1px solid var(--border-color);
            font-family: 'Courier New', monospace;
            font-size: var(--var-font-size, .8rem);
            min-height: 26px;
            max-height: 60px;
            overflow-y: auto;
            flex-shrink: 0;
            background: rgba(0, 0, 0, .15);
        }

        /* ── Mobile Header ── */
        @media (max-width: 700px) {
            body {
                overflow: hidden;
            }

            /* Header kompakter */
            #app-header {
                height: 46px;
                padding: 0 10px;
                gap: 8px;
            }

            #app-header .logo img.header-logo-img {
                height: 40px;
            }

            #app-header h1 {
                font-size: .78rem;
            }

            #app-header .sub {
                display: none;
            }

            /* NEU (live gemeldet: Titelleisten-Aufgabeninfo): auf Mobile
               ausgeblendet wie .sub oben - der ohnehin sehr knappe
               46px-Kopfbereich hat dafür keinen Platz, und das mobile
               Aufgaben-Panel zeigt die aktuelle Aufgabe bereits separat
               (#mob-task-title). */
            #hdr-task-info {
                display: none;
            }

            /**
             * NEU (live gemeldet: "Da der Smartphone-Mode aus
             * Platzgründen immer im Basic-Mode ist, kann der
             * Togglebutton zw. Basic- und Standard-View verschwinden"):
             * verifiziert (siehe .btn-debug/.btn-step sowie
             * .mob-output-tabs [data-tab="code"/"viz"] weiter unten in
             * dieser Datei) - Mobile blendet die Funktionen, die der
             * Umschalter zwischen Basis-/Standardansicht sonst steuert
             * (Debuggen/Einzelschritt, Code-/Visualisierungs-Reiter),
             * bereits UNABHÄNGIG von data-view-mode über diese eigene
             * Medienabfrage aus - der Umschalter selbst bleibt auf
             * Mobile also ohne jede sichtbare Wirkung und kann
             * entfallen, um im ohnehin knappen 46px-Kopfbereich Platz
             * zu sparen.
             */
            #view-mode-toggle {
                display: none;
            }

            .not-logged-in-icon {
                font-size: 1.1rem;
            }

            #app-header .stats {
                gap: 5px;
                /**
                 * BUGFIX (Audit für responsives Design): mit den neu
                 * hinzugekommenen Abzeichen-/Serien-Anzeigen sind es jetzt
                 * VIER Pillen statt zwei - auf schmalen Bildschirmen reicht
                 * der verfügbare Platz neben Logo/Titel/Nutzer-Bereich
                 * nicht mehr sicher aus. min-width:0 ist hier nötig, damit
                 * das Flex-Element (mit margin-left:auto) überhaupt unter
                 * seine Inhaltsbreite schrumpfen kann - erst DANN greift
                 * overflow-x:auto tatsächlich, statt dass die Pillen die
                 * dahinter liegenden, wichtigeren Elemente (Abmelden-Knopf
                 * etc.) einfach aus dem sichtbaren Bereich verdrängen.
                 * Dasselbe Muster (nowrap + horizontal scrollbar statt
                 * Umbruch) wird bereits erfolgreich bei #mob-control-bar
                 * verwendet, siehe dortiger, ausführlicher Kommentar zu
                 * den Gründen gegen flex-wrap.
                 */
                min-width: 0;
                flex-wrap: nowrap;
                overflow-x: auto;
                -webkit-overflow-scrolling: touch;
                scrollbar-width: none;
            }

            #app-header .stats::-webkit-scrollbar {
                display: none;
            }

            .stat-pill {
                flex-shrink: 0;
            }

            /* Beschreibende Wörter ("Sterne"/"Tage") entfallen auf
               schmalen Bildschirmen - Symbol + Zahl bleiben eindeutig
               genug, sparen aber spürbar Platz (siehe HTML: die Wörter
               stecken bewusst in einem eigenen <span> genau dafür). */
            .stat-pill-label {
                display: none;
            }

            .stat-pill {
                font-size: .68rem;
                padding: 2px 7px;
            }

            /**
             * NEU (live gemeldet: "Buttons und Texte ... horizontal
             * passen"): mit VIER Statistik-Pillen (Sterne/Gelöst/
             * Abzeichen/Serie) UND Nutzerabzeichen UND Classroom-Knopf
             * UND drei Theme-/Sprach-Umschaltern reichte der verfügbare
             * Platz auf schmalen Bildschirten (z.B. 360px) nicht mehr
             * annähernd aus - .stats/.ub-text-wrap wurden dabei bis auf
             * 0px Breite zusammengedrückt (Name/Raum komplett
             * unsichtbar, nicht nur "überlappend"). Abzeichen/Serie sind
             * über das eigene Info-Modal ohnehin jederzeit einsehbar -
             * auf Mobile bewusst ausgeblendet, um Sterne/Gelöst UND die
             * wichtigere Nutzeridentität wieder verlässlich Platz zu
             * geben. Dieselbe Regel existierte bisher nur für
             * data-view-mode="basic" (eine unabhängige Nutzereinstellung,
             * nicht automatisch an die Bildschirmbreite gekoppelt).
             */
            .stat-pill-badges,
            .stat-pill-streak {
                display: none;
            }

            #btn-classroom {
                font-size: .68rem;
                padding: 3px 9px;
            }

            /**
             * BUGFIX (live gemeldet: "Buttons und Texte ... horizontal
             * passen"): #user-badge/.ub-text-wrap durften bisher bis auf
             * 0px schrumpfen (min-width:0 ohne jeden Bodenwert, siehe
             * Basis-Regel weiter oben - deren eigener Kommentar
             * beschrieb zwar bereits die Absicht "nicht unter eine
             * sinnvolle Mindestbreite fallen", setzte aber tatsächlich
             * keinen Wert dafür) - bei VIER Statistik-Pillen (jetzt zwei,
             * siehe .stat-pill-badges/.stat-pill-streak oben) PLUS
             * Classroom-Knopf PLUS drei Umschaltern blieb auf schmalen
             * Bildschirmen dadurch buchstäblich kein Platz mehr für Name/
             * Raum übrig - komplett unsichtbar, nicht nur gekürzt. Auf
             * Mobile bewusst ein fester Bodenwert statt "0" - Kürzung per
             * ellipsis (siehe .ub-name/.ub-room) übernimmt den Rest.
             */
            #user-badge .ub-text-wrap {
                min-width: 46px;
            }

            .theme-toggle {
                width: 30px;
                height: 30px;
                font-size: .85rem;
            }

            /* Desktop-Layout ausblenden */
            #sidebar {
                display: none !important;
            }

            #right-panel {
                display: none !important;
            }

            /* v15.28 (live gemeldet): die Ziehleiste zum rechten Bereich
               gehört zum Desktop-Layout - am Smartphone stand sie sonst als
               funktionsloser „Scrollbalken“ am linken Rand (Workspace unten,
               Ausgabe oben, Aufgabenliste) */
            #rightpanel-resize-handle {
                display: none !important;
            }

            .panel-collapse-btn {
                display: none !important;
            }

            #control-bar {
                display: none !important;
            }

            #task-panel {
                display: none !important;
            }

            /* Layout auf Vollbild */
            #layout {
                /* BUGFIX (Smartphone-Ansicht, Steuerleiste teilweise
                   verdeckt): die vorherige feste "height: calc(100dvh -
                   46px - 56px)" hier hatte GENAU dasselbe Grundproblem
                   wie die Desktop-Fassung weiter oben (siehe dortiger
                   Kommentar) - zusätzlich verschärft durch zwei weitere
                   hart einprogrammierte Annahmen (mobile Header-Höhe
                   46px, Höhe der unteren Navigationsleiste #mobile-nav
                   56px), die bei jeder Abweichung (Browser-UI,
                   Schriftgrößen-Einstellung des Geräts, Zoom) zu wenig
                   Platz berechneten - die Steuerleiste (Ausführen/
                   Debug/...) ragte dadurch teilweise unter die fest
                   positionierte #mobile-nav. Die grundsätzliche Höhe
                   übernimmt jetzt automatisch die Basisregel weiter oben
                   (flex:1 gegenüber der TATSÄCHLICHEN Header-Höhe) - hier
                   bleibt nur noch der Platzbedarf für die fest
                   positionierte (position:fixed, daher NICHT Teil der
                   normalen Flex-Berechnung) untere Navigationsleiste als
                   zusätzlicher Innenabstand reserviert. */
                padding-bottom: 56px;
                flex-direction: column;
            }

            #center {
                height: 100%;
                overflow: hidden;
            }

            /* Blockly-Wrap Vollbild */
            #blockly-wrap {
                flex: 1;
                position: relative;
            }

            /* Block-Counter kleiner */
            #block-counter {
                font-size: .65rem;
                padding: 4px 8px;
                gap: 5px;
            }

            #counter-bar {
                width: 40px;
            }

            /**
             * Der ausgeschriebene Label-Text ("Blockauswahl immer
             * sichtbar"/"...verschwindet") ist auf schmalen
             * Bildschirmen zu lang und würde entweder umbrechen oder
             * mit #block-counter rechts kollidieren - nur noch das
             * Symbol anzeigen, die Bedeutung bleibt über den
             * title-Tooltip weiterhin abrufbar (auf Touch per
             * Long-Press, siehe title-Attribut im HTML).
             */

            /**
             * NEU (live gemeldet: "Verzichte in der Smartphone-View auf
             * Debugging-Buttons, Code-Anzeige, Diagramme-Anzeige, den
             * Button Blöcke-Pinnen"): unabhängig von Basis-/
             * Standardansicht - auf dem schmalen Bildschirm sind diese
             * Funktionen bewusst gar nicht mehr erreichbar, nicht nur in
             * der Basisansicht ausgeblendet. #flyout-autoclose-toggle
             * war oben bereits verkleinert dargestellt (nur Symbol, ohne
             * Beschriftung) - wird jetzt stattdessen komplett entfernt.
             */
            #ws-menu {
                display: none;
            }

            #block-counter {
                right: 10px;
            }

            .btn-debug,
            .btn-step {
                display: none;
            }

            .mob-output-tabs [data-tab="code"],
            .mob-output-tabs [data-tab="viz"] {
                display: none;
            }

            /* Mobile Elemente einblenden */
            #mobile-nav {
                display: flex;
            }

            #mob-task-panel {
                display: flex;
                flex-direction: column;
            }

            #mob-control-bar {
                display: flex;
            }

            /* Warnung im unangemeldeten Zustand übernimmt jetzt der
               Header selbst (siehe body.not-logged-in #app-header) -
               einheitlich für Desktop und Mobile, kein separates
               Toast-Element mehr nötig. */

            /* Modals mobil */
            .modal-box {
                width: 95%;
                padding: 18px 16px;
                max-height: 85vh;
                overflow-y: auto;
            }

            #solution-modal .modal-box,
            #code-modal .modal-box,
            #viz-modal .modal-box,
            #stats-info-modal .modal-box,
            #classroom-modal .modal-box,
            #teacher-modal .modal-box {
                max-width: 98vw;
                padding: 14px 12px;
            }

            #code-output {
                font-size: .65rem;
                max-height: 200px;
            }

            #viz-modal-scroll {
                max-height: 56vh;
                max-height: 56dvh;
            }

            #solution-steps {
                max-height: 220px;
            }

            /* Toast höher */
            #toast {
                bottom: 72px;
            }

            /* Autosave-Indikator */
            #autosave-indicator {
                bottom: 66px;
                right: 8px;
            }

            /* Fußzeile (Impressum + Versionsanzeige): auf Mobile
               ausgeblendet, Platz ist dort durch die feste Steuerleiste
               ohnehin knapp. */
            #footer-links {
                display: none;
            }

            /**
             * BUGFIX (live gemeldet: "Im Smartphone-Modus überlagert der
             * Kategoriebereich teilweise die Buttons Ausführen, Stoppen,
             * Geschwindigkeitsregler [...] Das ist im Vollbildmodus
             * nicht so"): Die bereits bestehende Korrektur weiter unten
             * ("Blockly Flyout z-index fix") behandelt nur .blocklyFlyout
             * (die aufgeklappten Bausteine EINER Kategorie) - der
             * KATEGORIEBEREICH selbst (.blocklyToolbox, die Liste
             * "Fahren"/"Sensoren"/... links) hatte dabei nie einen
             * eigenen z-index bekommen und nutzte Blocklys eigenen
             * Standardwert, der höher liegt als #mob-control-bar
             * (z-index:60) - dadurch legte sich der Kategoriebereich
             * BEI JEDEM Aufgabentyp mit Werkzeugkasten über die
             * Steuerleiste. Im Vollbildmodus trat das nie auf, weil dort
             * der Werkzeugkasten (und damit auch dieser Bereich)
             * komplett ausgeblendet ist, nicht weil das Problem dort
             * grundsätzlich anders wäre.
             */
            .blocklyToolbox {
                z-index: 50 !important;
            }

            /* Blockly Flyout z-index fix */
            .blocklyFlyout {
                z-index: 50 !important;
            }

            /* Debug-Overlay */
            #debug-overlay {
                bottom: 62px;
                left: 6px;
                right: 6px;
                font-size: .76rem;
                padding: 8px 12px;
                max-width: none;
            }
        }

        /* ── Sehr kleine Bildschirme ── */
        @media (max-width: 380px) {
            #app-header h1 {
                display: none;
            }

            .mob-nav-btn {
                font-size: .55rem;
            }

            .mob-nav-btn .mob-icon {
                font-size: 1.15rem;
            }

            .mob-ctrl-btn {
                font-size: .65rem;
                padding: 6px 7px;
            }
        }

        /* ── Tablet (701px – 1024px) ── */
        @media (min-width: 701px) and (max-width: 1024px) {
            #sidebar {
                width: 190px;
            }

            /* BUGFIX v15.51 (live gemeldet, iPad hochkant: „der Schließen-Knopf von Aufgaben hängt in
               der Luft“): die Aufgabenliste ist hier nur 190 px breit, der Knopf stand aber fest bei
               240 px (Desktop-Breite) mitten über der Blockauswahl. Eingeklappt gilt weiter left: 0
               (spezifischere Regel #layout.sidebar-collapsed …). */
            #sidebar-collapse-btn {
                left: 190px;
            }

            #right-panel {
                width: var(--right-panel-width, 200px);
            }

            #sim-canvas-wrap {
                height: 150px;
            }

            .ctrl-btn {
                font-size: .72rem;
                padding: 4px 10px;
            }

            #app-header h1 {
                font-size: .85rem;
            }

            #app-header .sub {
                font-size: .6rem;
            }
        }

        /**
         * NEU (live gemeldet: PRIMM-Zyklus vor Grundkonzept-Einführungen).
         * Das Modal ist bewusst BREITER als die übrigen (max-width:420px)
         * angelegt - es zeigt gleichzeitig ein Programm UND eine
         * Grid-Welt nebeneinander, das braucht mehr Platz.
         */
        #primm-modal .modal-box.primm-box {
            max-width: 900px;
            width: 95%;
            text-align: left;
        }

        /**
         * NEU (live gemeldet: "Das Modal soll im Vollbildschirmmodus die
         * Breite und Höhe des Bildschirms fast ausnutzen - analog zum
         * Vollbildschirmmodus in der PRIMM-Zyklus-Erstellung im
         * Aufgaben-Editor"): dieselben Maße (96vw/94vh) wie dort
         * (#primmModalInner in aufgaben_editor.html) - der sonst auf
         * 900px begrenzte Modal-Rahmen sowie die von .modal-box
         * geerbte max-height:90vh werden hier bewusst überschrieben,
         * damit der zusätzliche Platz im Vollbildmodus auch wirklich
         * ausgenutzt wird (v.a. für Welt-Canvas, Blockly-Werkstatt und
         * die neuen Zusatz-Vorhersage-Bereiche relevant).
         */
        body.is-fullscreen #primm-modal .modal-box.primm-box {
            max-width: 96vw;
            width: 96vw;
            height: 94vh;
            height: 94dvh;
            max-height: 94vh;
            max-height: 94dvh;
        }

        .primm-steps {
            display: flex;
            flex-wrap: wrap;
            gap: 4px;
            margin-bottom: 14px;
            border-bottom: 1px solid var(--border-color);
            padding-bottom: 10px;
        }

        .primm-step {
            font-size: .78rem;
            padding: 5px 10px;
            border-radius: 14px;
            background: transparent;
            color: var(--clr-muted);
            /* v14.91: keine opacity mehr (Kontrast 2,6:1); offene Phasen
               unterscheiden sich durch gestrichelten Rand */
            border: 1px dashed var(--border-color);
        }

        .primm-step.active {
            background: var(--clr-accent);
            color: var(--clr-on-accent);
            border: 1px solid var(--clr-accent);
            font-weight: 700;
        }

        .primm-step.done {
            color: var(--clr-text);
            border-style: solid;
        }

        /**
         * NEU (live gemeldet: "Testwelten ... sollen auch im Predict-Teil
         * ... erscheinen"): eigene, KLICKBARE Reiterleiste (im
         * Unterschied zu .primm-steps oben, die nur den aktuellen
         * PRIMM-Fortschritt anzeigt) zum Umschalten zwischen Hauptwelt
         * und Testwelten innerhalb der Predict-Phase.
         */
        .primm-world-tabs {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            margin-bottom: 10px;
        }

        .primm-world-tab {
            font-size: .8rem;
            padding: 6px 12px;
            border-radius: 14px;
            background: var(--clr-bg2, rgba(255, 255, 255, .05));
            color: var(--clr-text);
            border: 1px solid var(--border-color);
            cursor: pointer;
        }

        .primm-world-tab.active {
            background: var(--clr-accent);
            color: #fff;
            border-color: var(--clr-accent);
            font-weight: 700;
        }

        .primm-step.done::after {
            content: ' ✓';
        }

        /* v15.40 (PO-Auftrag): schon bearbeitete Phasen sind Knöpfe (_primmGoToPhase()) */
        .primm-step.primm-step-nav {
            cursor: pointer;
            color: var(--clr-text);
            border-style: solid;
        }

        .primm-step.primm-step-nav:hover {
            border-color: var(--clr-accent);
            text-decoration: underline;
        }

        .primm-step.primm-step-nav:focus-visible {
            outline: 2px solid var(--clr-accent);
            outline-offset: 2px;
        }

        #primm-modal h2 {
            margin: 0 0 10px 0;
            font-size: 1.1rem;
        }

        .primm-text {
            font-size: .92rem;
            line-height: 1.5;
            margin-bottom: 8px;
        }

        .primm-question {
            font-weight: 700;
            font-size: .95rem;
            margin-bottom: 12px;
            color: var(--clr-accent2, var(--clr-accent));
        }

        .primm-predict-layout,
        .primm-run-layout {
            display: flex;
            gap: 16px;
            flex-wrap: wrap;
            margin-bottom: 12px;
        }

        .primm-predict-program-wrap {
            flex: 1 1 260px;
            min-width: 220px;
        }

        .primm-predict-grid-wrap {
            flex: 1 1 300px;
            min-width: 240px;
        }

        /**
         * NEU (04.10.2026, live angefordert: "Teile bei Grid-Aufgaben den
         * Predict-Teil so auf, dass der Programm-Bereich gut sichtbar oben
         * steht, darunter links das Dropdown für Welt/Testwelt mit der Welt
         * darunter ... rechts die ... Grid-Welt zum Einzeichnen ... Unter
         * beiden Welten die jeweiligen Attribute"): Programm über die volle
         * Breite, darunter zwei gleich breite Spalten (Startwelt | Welt zum
         * Einzeichnen), jeweils mit ihren Attributen darunter. Am Smartphone
         * untereinander.
         */
        #primm-phase-predict .primm-predict-layout {
            flex-direction: column;
            flex-wrap: nowrap;
        }

        #primm-phase-predict .primm-predict-layout>.primm-predict-program-wrap {
            width: 100%;
            min-width: 0;
            flex: none;
        }

        .primm-predict-worlds {
            display: grid;
            grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
            gap: 16px;
            align-items: start;
        }

        .primm-predict-world-col {
            min-width: 0;
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .primm-predict-world-col .primm-predict-controls {
            margin-bottom: 0;
        }

        /* Kopfzeile beider Spalten gleich hoch, damit die Welten auf einer Linie beginnen */
        .primm-predict-world-head {
            min-height: 32px;
            display: flex;
            align-items: flex-end;
        }

        .primm-predict-world-head .primm-panel-label {
            margin-bottom: 0;
        }

        .primm-predict-world-head .sim-world-select {
            max-width: 100%;
        }

        #primm-predict-start-attrs:empty {
            display: none;
        }

        /**
         * NEU (04.10.2026, live angefordert: dieselbe Aufteilung im Run-
         * Teil): Programm allein über die volle Breite, darunter je Welt
         * (Standard-Welt, dann alle Testwelten) eine Zeile - links die Welt
         * mit eingezeichnetem Weg, rechts der Vergleich der Attribute.
         */
        #primm-phase-run .primm-run-layout {
            flex-direction: column;
            flex-wrap: nowrap;
        }

        #primm-phase-run .primm-run-layout>.primm-predict-program-wrap {
            width: 100%;
            min-width: 0;
            flex: none;
        }

        #primm-run-grid-wrap {
            margin-top: 4px;
        }

        .primm-run-world-row {
            display: grid;
            grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
            gap: 16px;
            align-items: start;
            margin-bottom: 16px;
        }

        /* ohne Testwelten braucht die Standard-Welt keine eigene Überschrift */
        #primm-run-grid-wrap.no-testworlds>.primm-run-world-row .primm-predict-world-head {
            display: none;
        }

        #primm-run-comparison:empty,
        #primm-run-extra-results:empty,
        .primm-run-world-row .primm-predict-extra:empty,
        .primm-run-world-row .primm-predict-field-result:empty {
            display: none;
        }

        @media (max-width: 700px) {
            .primm-predict-worlds,
            .primm-run-world-row {
                grid-template-columns: minmax(0, 1fr);
            }
        }

        /**
         * NEU (04.10.2026, live angefordert: "Generell sollte der Programm-
         * Bereich größer sein (für eine bessere Übersicht) insbesondere in
         * der Vollbildschirmansicht"): alle drei Predict-Werkstätten (Grid,
         * Turtle, Text) haben eine eigene, größere Höhe (Standard 340px
         * statt 220px), im Vollbild bildschirmabhängig 45% der Höhe -
         * beide per Ziehleiste veränderbar und getrennt gemerkt (siehe
         * PRIMM_BLOCKLY_GROUPS.predict/predictFs in funci.html).
         */
        /* seit 04.10.2026 (Run-Teil im selben Layout) auch für die Run-Werkstatt */
        #primm-phase-predict .primm-blockly-preview,
        #primm-phase-run .primm-blockly-preview {
            height: var(--primm-blockly-h-predict, 340px);
        }

        body.is-fullscreen #primm-phase-predict .primm-blockly-preview,
        body.is-fullscreen #primm-phase-run .primm-blockly-preview {
            height: var(--primm-blockly-h-predict-fs, 45vh);
        }

        body.is-fullscreen #primm-predict-canvas,
        body.is-fullscreen #primm-predict-start-canvas,
        body.is-fullscreen #primm-run-canvas,
        body.is-fullscreen .primm-run-tw-canvas {
            height: 300px;
        }

        /**
         * NEU (live gemeldet: "Verbreitere den Programmierbereich im
         * Modify- und Make-Teil auf die gesamte Modal-Fensterbreite
         * (wenn möglich). Passe im Smartphone-Modus dementsprechend
         * passend an"): In Predict/Run reicht die Hälfte der Modal-
         * Breite für das NUR LESBARE Vorschau-Programm, weil daneben
         * die Weg-Zeichnung bzw. das Ergebnis GLEICHBERECHTIGT wichtig
         * ist. In Modify/Make bearbeitet die Schülerin/der Schüler das
         * Programm dagegen aktiv per Drag&Drop - dafür ist die halbe
         * Breite oft zu wenig Platz für mehrere nebeneinander/
         * verschachtelt liegende Blöcke. Deshalb hier (NUR für diese
         * beiden Phasen, per ID-Selektor auf die jeweilige Phase
         * eingegrenzt) Umschaltung von nebeneinander (flex-row, siehe
         * .primm-run-layout oben) auf übereinander (flex-column): der
         * Editierbereich bekommt dadurch die VOLLE Modal-Breite, das
         * Ergebnis-/Testwelt-Canvas rutscht darunter und wird dort auf
         * eine sinnvolle Breite begrenzt (zentriert), statt ebenfalls
         * auf die volle Breite gestreckt zu werden.
         * Nur ab 701px (Desktop/Tablet) aktiv - im Smartphone-Modus
         * (<=700px, siehe die _isMobile-Prüfung in funci.html) ist
         * .primm-run-layout durch flex-wrap:wrap und die min-width-
         * Werte oben ohnehin schon einspaltig gestapelt; dort wird
         * zusätzlich nur sichergestellt, dass der Canvas-/Ergebnis-
         * Bereich die volle Breite einnimmt statt auf seiner
         * min-width (240px) sitzenzubleiben.
         */
        @media (min-width: 701px) {

            #primm-phase-modify .primm-run-layout,
            #primm-phase-make .primm-run-layout {
                flex-direction: column;
            }

            #primm-phase-modify .primm-run-layout>.primm-predict-program-wrap,
            #primm-phase-make .primm-run-layout>.primm-predict-program-wrap {
                width: 100%;
                max-width: none;
            }

            #primm-modify-blockly,
            #primm-make-blockly {
                min-height: 340px;
            }

            #primm-modify-canvas-wrap,
            #primm-make-canvas-wrap {
                width: 100%;
                max-width: 420px;
                margin: 0 auto;
            }
        }

        @media (max-width: 700px) {

            #primm-modify-canvas-wrap,
            #primm-make-canvas-wrap {
                width: 100%;
            }
        }

        /**
         * NEU (Option 2 - Multiple-Choice-Vorhersage für Turtle-
         * Programme): Kachel-Raster für die Antwortoptionen, jede
         * Kachel enthält ein live berechnetes SVG-Vorschaubild (siehe
         * _turtlePathToSvg()). Auswahl per Klick, deutliche farbliche
         * Hervorhebung der aktuell gewählten Kachel.
         */
        /* GEÄNDERT (04.10.2026, live angefordert: "Bei den Turtle-Aufgaben
           können die Auswahl-Bilder etwas kleiner sein zu gunsten des
           Programm-Bereiches"): Kacheln max. 120px statt mind. 140px breit,
           linksbündig statt auf volle Breite gestreckt. */
        .primm-turtle-mc-options {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(96px, 120px));
            gap: 12px;
            margin-top: 6px;
        }

        .primm-turtle-mc-tile {
            background: var(--clr-surface);
            border: 2px solid var(--border-color);
            border-radius: 10px;
            padding: 6px;
            cursor: pointer;
            transition: border-color .15s, transform .1s;
        }

        .primm-turtle-mc-tile:hover {
            border-color: var(--clr-accent2);
        }

        .primm-turtle-mc-tile.selected {
            border-color: var(--clr-accent2);
            background: rgba(var(--clr-accent2-rgb), 0.12);
            transform: scale(1.02);
        }

        .primm-turtle-mc-tile svg {
            display: block;
            width: 100%;
            height: auto;
        }

        .primm-turtle-opt-empty {
            fill: var(--clr-muted);
            font-size: 11px;
            font-style: italic;
        }

        .primm-turtle-run-result {
            max-width: 260px;
            margin: 0 auto;
            background: var(--clr-surface);
            border: 1px solid var(--border-color);
            border-radius: 10px;
            padding: 10px;
        }

        .primm-turtle-run-result svg {
            display: block;
            width: 100%;
            height: auto;
        }

        /**
         * NEU (live gemeldet: "Erstelle ebenfalls ein Primm-Zyklus zu
         * einer Nur-Text-Aufgabe"): dieselbe Kachel-Optik wie beim
         * Turtle-Zweig, aber mit Textinhalt statt SVG-Bild.
         */
        .primm-text-mc-options {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
            gap: 12px;
            margin-top: 6px;
        }

        .primm-text-mc-tile {
            background: var(--clr-surface);
            border: 2px solid var(--border-color);
            border-radius: 10px;
            padding: 14px 12px;
            cursor: pointer;
            transition: border-color .15s, transform .1s;
            font-family: 'Courier New', monospace;
            font-size: .82rem;
            white-space: pre-wrap;
            text-align: left;
            color: var(--clr-text);
            min-height: 60px;
        }

        .primm-text-mc-tile:hover {
            border-color: var(--clr-accent2);
        }

        .primm-text-mc-tile.selected {
            border-color: var(--clr-accent2);
            background: rgba(var(--clr-accent2-rgb), 0.12);
            transform: scale(1.02);
        }

        .primm-text-run-result {
            max-width: 340px;
            margin: 0 auto;
            background: var(--clr-surface);
            border: 1px solid var(--border-color);
            border-radius: 10px;
            padding: 12px;
            font-family: 'Courier New', monospace;
            font-size: .85rem;
            white-space: pre-wrap;
            text-align: left;
        }

        .primm-panel-label {
            font-size: .74rem;
            color: var(--clr-muted);
            margin-bottom: 4px;
            font-weight: 600;
        }

        /**
         * GEÄNDERT (live gemeldet: "der Programmierbereich in den PRIMM-
         * Zyklen soll sich vertikal vergrößern/verkleinern lassen"):
         * feste Höhe durch eine per Ziehleiste (.primm-blockly-resize-
         * handle, siehe unten und _initPrimmBlocklyResizeHandles() in
         * funci.html) veränderbare CSS-Variable ersetzt - Rückfallwert
         * bleibt die bisherige feste Höhe. EINE gemeinsame Variable für
         * alle reinen Anzeige-Werkstätten (Predict/Run/Investigate), da
         * diese alle denselben "Nur betrachten"-Zweck erfüllen; Modify/
         * Make teilen sich dagegen ihre EIGENE Variable (siehe
         * .primm-blockly-editable unten) - analog zu --sim-area-height/
         * --mob-sim-area-height, die ebenfalls unabhängig voneinander
         * gemerkt werden.
         */
        .primm-blockly-preview {
            height: var(--primm-blockly-h, 220px);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            background: var(--clr-bg2, rgba(255, 255, 255, .03));
            overflow: hidden;
        }

        /**
         * NEU (live gemeldet: "leider werden aber keine kopierten
         * Blöcke angezeigt"): die Modify-Werkstatt ist bewusst deutlich
         * größer als die reinen Anzeige-Werkstätten (Predict/Run) - in
         * Kombination mit dem addChangeListener()-Auto-Scroll in
         * _primmSetupModify() sinkt damit das Risiko, dass ein
         * dupliziertes Blockpaar aus dem sichtbaren Bereich
         * herausrutscht.
         */
        .primm-blockly-editable {
            height: var(--primm-blockly-h-editable, 380px);
        }

        /**
         * NEU (live gemeldet: "der Programmierbereich in den PRIMM-
         * Zyklen soll sich vertikal vergrößern/verkleinern lassen,
         * vielleicht durch eine horizontale Leiste"): direkt unter jeder
         * PRIMM-Blockly-Werkstatt sitzende Ziehleiste - optisch/
         * technisch analog zu #sim-resize-handle (siehe dort), aber als
         * wiederverwendbare KLASSE statt ID, da hier mehrere Instanzen
         * gleichzeitig im Markup existieren (jede Phase hat ihre eigene
         * Werkstatt, siehe _initPrimmBlocklyResizeHandles()).
         */
        .primm-blockly-resize-handle {
            height: 9px;
            margin: -1px 0 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: row-resize;
            background: var(--clr-bg, #1a2332);
            border: 1px solid var(--border-color, #33415c);
            border-top: none;
            border-radius: 0 0 8px 8px;
            touch-action: none;
        }

        .primm-blockly-resize-handle:hover,
        .primm-blockly-resize-handle:focus-visible,
        .primm-blockly-resize-handle.dragging {
            background: rgba(var(--success-rgb), 0.12);
            outline: none;
        }

        .primm-blockly-resize-grip {
            width: 36px;
            height: 4px;
            border-radius: 3px;
            background: var(--clr-muted, #64748b);
            opacity: .5;
            transition: opacity .15s, background .15s;
        }

        .primm-blockly-resize-handle:hover .primm-blockly-resize-grip,
        .primm-blockly-resize-handle:focus-visible .primm-blockly-resize-grip,
        .primm-blockly-resize-handle.dragging .primm-blockly-resize-grip {
            opacity: 1;
            background: var(--clr-accent);
        }

        #primm-predict-canvas,
        #primm-predict-start-canvas,
        #primm-run-canvas,
        .primm-run-tw-canvas {
            width: 100%;
            height: 220px;
            border: 1px solid var(--border-color);
            border-radius: 8px;
            /* touch-action:none verhindert, dass beim Zeichnen auf dem
               Canvas gleichzeitig die ganze Seite gescrollt/gezoomt wird -
               ohne das würde ein Zieh-Versuch auf dem Smartphone eher als
               Scroll-Geste interpretiert. */
            touch-action: none;
            cursor: crosshair;
        }

        /**
         * BUGFIX (live gemeldet: "Wenn man im Modify- und Make-Teil
         * außerhalb des Simulations-Frames den Bereich mit dem Finger
         * scrollt, so bewegt sich die Grid-Welt mit"): Modify/Make
         * haben - anders als Predict/Run - KEINE Ein-Finger-Zieh-
         * Verschiebung mehr (siehe disableSingleFingerPan in
         * _attachSimZoom()) und brauchen deshalb touch-action:none
         * NICHT - touch-action:pan-y erlaubt normales Scrollen der
         * Seite, während Mausrad-Zoom UND Zwei-Finger-Pinch weiterhin
         * funktionieren (touch-action betrifft nur Ein-Finger-Gesten).
         */
        /* Startwelt (Predict, linke Spalte) dient nur zur Ansicht */
        #primm-predict-start-canvas {
            cursor: default;
            touch-action: pan-y;
        }

        #primm-modify-canvas,
        #primm-make-canvas {
            width: 100%;
            height: 220px;
            border: 1px solid var(--border-color);
            border-radius: 8px;
            touch-action: pan-y;
        }

        .primm-predict-controls {
            margin-bottom: 10px;
        }

        /**
         * NEU (live gemeldet: "zusätzlich zum Wegmalen auch
         * Variablenwerte, Listeneinträge, Textausgaben und das
         * aufgesammelte Inventar vorhersagen können"): eigener,
         * kompakter Block je Vorhersage-Art unterhalb der
         * Wegzeichnen-Steuerung - bewusst schlicht (Label + Eingabe +
         * Ergebnis in einer Zeile), da hier viele Felder gleichzeitig
         * sichtbar sein können (mehrere Variablen/Listen/Gegenstände).
         */
        .primm-predict-extra {
            display: flex;
            flex-direction: column;
            gap: 14px;
        }

        .primm-predict-extra-group {
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 10px 12px;
            background: var(--clr-bg2, rgba(255, 255, 255, .03));
        }

        .primm-predict-field-row {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-top: 6px;
            flex-wrap: wrap;
        }

        .primm-predict-field-row label {
            min-width: 90px;
            font-size: .85rem;
            font-weight: 600;
        }

        .primm-predict-field-row input[type="text"],
        .primm-predict-field-row input[type="number"] {
            flex: 1;
            min-width: 100px;
            padding: 6px 10px;
            border-radius: 6px;
            border: 1px solid var(--border-color);
            background: var(--clr-bg);
            color: var(--clr-text);
        }

        .primm-predict-field-row input[type="number"] {
            flex: none;
            width: 70px;
        }

        .primm-predict-field-row textarea {
            flex: 1 1 100%;
            padding: 6px 10px;
            border-radius: 6px;
            border: 1px solid var(--border-color);
            background: var(--clr-bg);
            color: var(--clr-text);
            font-family: inherit;
            resize: vertical;
        }

        .primm-predict-field-result {
            font-size: .8rem;
            font-weight: 600;
            white-space: nowrap;
        }

        .primm-predict-field-result.match {
            color: var(--success, #16a34a);
        }

        .primm-predict-field-result.mismatch {
            color: var(--warning);
        }

        .primm-comparison {
            font-size: .88rem;
            font-weight: 600;
            padding: 8px 12px;
            border-radius: 8px;
            margin-top: 8px;
            min-height: 1.4em;
        }

        .primm-comparison.match {
            background: color-mix(in srgb, var(--success, #16a34a) 18%, transparent);
            color: var(--success, #16a34a);
        }

        .primm-comparison.mismatch {
            background: color-mix(in srgb, var(--warning) 18%, transparent);
            color: var(--warning);
        }

        .primm-investigate-q {
            margin-bottom: 14px;
            padding: 10px 12px;
            border: 1px solid var(--border-color);
            border-radius: 8px;
            background: var(--clr-bg2, rgba(255, 255, 255, .03));
        }

        .primm-investigate-q p {
            margin: 0 0 8px 0;
            color: var(--clr-text); /* v14.91: --clr-muted auf der Kartenfläche < 4,5:1 */
            font-size: .88rem;
        }

        .primm-investigate-opt {
            display: block;
            width: 100%;
            text-align: left;
            padding: 7px 10px;
            margin-bottom: 5px;
            border-radius: 6px;
            border: 1px solid var(--border-color);
            background: var(--clr-surface);
            color: inherit;
            cursor: pointer;
            font-size: .84rem;
        }

        .primm-investigate-opt.correct {
            border-color: var(--success, #16a34a);
            background: color-mix(in srgb, var(--success, #16a34a) 15%, transparent);
        }

        .primm-investigate-opt.wrong {
            border-color: var(--warning);
            background: color-mix(in srgb, var(--warning) 15%, transparent);
        }

        .primm-make-icon {
            font-size: 2.4rem;
            text-align: center;
            margin-bottom: 10px;
        }

        /**
         * NEU (04.10.2026, live angefordert: Titelleiste mit Schritten UND
         * allen Navigations-Knöpfen immer sichtbar, auch beim Scrollen):
         * klebt oben im scrollenden Modal-Rahmen (.modal-box hat
         * overflow-y:auto). Die negativen Ränder heben das Innenpolster
         * des Rahmens auf, damit die Leiste bündig von Kante zu Kante
         * reicht und beim Scrollen nichts darüber hindurchscheint. Passt
         * nicht alles in eine Zeile, rutschen die Knöpfe geschlossen in
         * eine zweite Zeile (rechtsbündig) - jeder Knopf selbst bleibt
         * einzeilig.
         */
        #primm-modal .primm-header {
            position: sticky;
            /* Sticky-Bezug ist die Innenkante (nach dem Polster) - -28px = bündig mit der Rahmenoberkante */
            top: -28px;
            /* über Blocklys Kategorie-Spalte (.blocklyToolbox, z-index 70) und Blockauswahl */
            z-index: 100;
            margin: -28px -32px 14px;
            padding: 12px 32px 10px;
            background: var(--clr-surface);
            border-bottom: 1px solid var(--border-color);
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            justify-content: space-between;
            gap: 8px 12px;
        }

        #primm-modal .primm-header .primm-steps {
            margin: 0;
            padding: 0;
            border: 0;
        }

        .primm-header-actions {
            display: flex;
            align-items: center;
            justify-content: flex-end;
            gap: 6px;
            margin-left: auto;
        }

        .primm-header-actions .mbtn {
            white-space: nowrap;
            padding: 6px 12px;
            font-size: .8rem;
        }

        /* Schrittleiste in der Titelleiste etwas kompakter */
        #primm-modal .primm-header .primm-step {
            /* v15.42: etwas kompakter - die serifenlose Seitenschrift läuft breiter, sonst bricht die
               Titelleiste bei 1400 px mit Opt-out-Knopf in zwei Zeilen um */
            font-size: .72rem;
            padding: 4px 7px;
            white-space: nowrap;
        }

        /* Lang-/Kurzbeschriftung (Opt-out, Fertig): im normalen Fenster
           (max. 900px breit) und am Smartphone die kurze, nur im Vollbild
           auf genügend breiten Bildschirmen die lange - so passt die
           Titelleiste jeweils in eine Zeile. */
        .primm-header-actions .primm-lbl-long {
            display: none;
        }

        /* "← Zurück" ist standardmäßig lang; nur neben dem Opt-out-Knopf kurz ("←") */
        #primm-btn-back .primm-lbl-long {
            display: inline;
        }

        #primm-btn-back .primm-lbl-short,
        .primm-header-actions.has-optout #primm-btn-back .primm-lbl-long {
            display: none;
        }

        .primm-header-actions.has-optout #primm-btn-back .primm-lbl-short {
            display: inline;
        }

        @media (min-width: 1400px) {
            body.is-fullscreen .primm-header-actions .primm-lbl-long {
                display: inline;
            }

            body.is-fullscreen .primm-header-actions .primm-lbl-short,
            body.is-fullscreen .primm-header-actions.has-optout #primm-btn-back .primm-lbl-short {
                display: none;
            }

            body.is-fullscreen .primm-header-actions.has-optout #primm-btn-back .primm-lbl-long {
                display: inline;
            }
        }

        @media (max-width: 700px) {
            #primm-modal .primm-header {
                top: -18px;
                margin: -18px -16px 12px;
                padding: 10px 16px 8px;
            }

            .primm-header-actions .mbtn {
                padding: 5px 10px;
            }
        }
        .shared-color-picker-popup {
            position: fixed;
            z-index: 10000;
            background: var(--clr-surface);
            border: 1px solid var(--border-color);
            border-radius: 10px;
            box-shadow: 0 8px 24px rgba(0,0,0,.35);
            padding: 12px;
            width: 258px;
            max-width: calc(100vw - 16px);
        }
        .scp-preview {
            height: 32px;
            border-radius: 6px;
            border: 1px solid var(--border-color);
            margin-bottom: 8px;
        }
        .scp-palette {
            display: grid;
            grid-template-columns: repeat(8, 1fr);
            gap: 4px;
            margin-bottom: 8px;
        }
        .scp-swatch {
            width: 100%;
            aspect-ratio: 1;
            border-radius: 4px;
            border: 1px solid var(--border-color);
            cursor: pointer;
            padding: 0;
        }
        .scp-swatch:hover { outline: 2px solid var(--clr-accent2); outline-offset: 1px; }
        .scp-section-label {
            font-size: .68rem;
            color: var(--clr-muted);
            margin: 6px 0 4px;
        }
        .scp-recent-row { min-height: 22px; }
        .scp-recent-empty { font-size: .68rem; color: var(--clr-muted); }
        .scp-input-row {
            display: flex;
            align-items: center;
            gap: 6px;
            margin-top: 8px;
        }
        .scp-input-row label {
            font-size: .7rem;
            color: var(--clr-muted);
            width: 16px;
            flex-shrink: 0;
        }
        .scp-hex-input {
            flex: 1;
            font-family: monospace;
            font-size: .78rem;
            padding: 4px 6px;
            border: 1px solid var(--border-color);
            border-radius: 6px;
            background: var(--clr-bg);
            color: var(--clr-text);
        }
        .scp-eyedropper-btn {
            flex-shrink: 0;
            width: 28px;
            height: 28px;
            border-radius: 6px;
            border: 1px solid var(--border-color);
            background: var(--clr-bg);
            cursor: pointer;
            font-size: .9rem;
        }
        .scp-eyedropper-btn:hover { background: var(--clr-accent2); }
        .scp-rgb-row { gap: 4px; }
        .scp-num-wrap { display: flex; align-items: center; gap: 3px; flex: 1; }
        .scp-num-wrap label { width: 9px; flex-shrink: 0; }
        .scp-num-input::-webkit-outer-spin-button,
        .scp-num-input::-webkit-inner-spin-button {
            -webkit-appearance: none;
            margin: 0;
        }
        .scp-num-input {
            -moz-appearance: textfield;
            width: 100%;
            min-width: 0;
            box-sizing: border-box;
            font-size: .72rem;
            padding: 3px 3px;
            border: 1px solid var(--border-color);
            border-radius: 5px;
            background: var(--clr-bg);
            color: var(--clr-text);
        }
        .scp-close-btn {
            width: 100%;
            margin-top: 10px;
        }
        .scp-pick-hint {
            position: fixed;
            top: 16px;
            left: 50%;
            transform: translateX(-50%);
            background: var(--clr-surface);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 8px 14px;
            font-size: .8rem;
            color: var(--clr-text);
            z-index: 10001;
            box-shadow: 0 4px 16px rgba(0,0,0,.3);
        }
        .scp-magnifier {
            position: fixed;
            z-index: 10002;
            pointer-events: none;
            display: flex;
            align-items: center;
            gap: 6px;
            background: var(--clr-surface);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 5px 9px;
            box-shadow: 0 4px 14px rgba(0,0,0,.35);
        }
        .scp-magnifier-swatch {
            width: 22px;
            height: 22px;
            border-radius: 5px;
            border: 2px solid #fff;
            box-shadow: 0 0 0 1px var(--border-color);
            flex-shrink: 0;
        }
        .scp-magnifier-hex {
            font-family: monospace;
            font-size: .74rem;
            color: var(--clr-text);
            white-space: nowrap;
        }

        /* ── Aufgabenbereich hervorgehoben (v14.53, live angefordert:
           "verändere den Aufgabenbereich so, dass die zu erledigende
           Aufgabe in den Vordergrund rückt ... vergrößere die Schrift
           ... + und - zum Vergrößern/Verkleinern"). Eigene Akzentfarbe
           (--clr-accent2, blau) statt des Grüns der Ausführen-/Erfolgs-
           Elemente, Beschreibung in voller Textfarbe statt --clr-muted,
           alle Schriftgrößen relativ zu --task-font-size (gesetzt über
           adjustPanelFontSize('task', ...), Grundwert .95rem). Bewusst am
           Dateiende, damit diese Regeln die älteren #task-panel-Regeln
           weiter oben überschreiben. */
        #task-panel {
            background:
                linear-gradient(180deg, rgba(var(--clr-accent2-rgb), .12), rgba(var(--clr-accent2-rgb), .04)),
                var(--clr-surface);
            border-bottom: 2px solid rgba(var(--clr-accent2-rgb), .5);
            box-shadow: inset 4px 0 0 var(--clr-accent2), 0 3px 10px rgba(0, 0, 0, .18);
            position: relative;
            z-index: 2;
        }

        #task-panel h2 {
            font-size: calc(var(--task-font-size, .95rem) * 1.15);
            font-weight: 800;
            line-height: 1.3;
        }

        #task-desc {
            font-size: var(--task-font-size, .95rem);
            line-height: 1.55;
            color: var(--clr-text);
            background: var(--clr-surface);
            border: 1px solid rgba(var(--clr-accent2-rgb), .35);
            border-left: 4px solid var(--clr-accent2);
            border-radius: 0 10px 10px 0;
            padding: 8px 12px;
            box-shadow: var(--shadow-sm);
        }

        #task-desc code {
            font-size: .92em;
        }

        #task-meta .meta-pill {
            font-size: calc(var(--task-font-size, .95rem) * .78);
        }

        #task-meta .meta-goal {
            color: var(--clr-text);
            font-weight: 600;
            border-color: rgba(var(--clr-accent2-rgb), .6);
            background: rgba(var(--clr-accent2-rgb), .12);
        }

        #hint-box {
            font-size: calc(var(--task-font-size, .95rem) * .86);
        }

        #hint-nav button {
            font-size: calc(var(--task-font-size, .95rem) * .76);
        }

        #task-font-ctrl {
            margin-left: 2px;
        }

        /* ── ☰-Menüs im Aufgabenkopf und über dem Programmierbereich
           (v14.55) ── gleiche Mechanik wie das Simulations-Menü
           (toggleSimMenu(), .sim-menu/.sim-menu-panel/.sim-menu-check),
           die dortigen Knopf-Stile sind aber an .panel-head gebunden -
           hier deshalb eigene Größen im Stil der Schriftgrößen-Knöpfe.
           #task-panel liegt dabei über dem Programmierbereich, damit das
           aufgeklappte Menü nicht von Blockzähler/Werkzeugkasten
           verdeckt wird. */
        #task-panel {
            z-index: 90;
        }

        #task-menu {
            margin-left: 2px;
        }

        #task-menu .sim-menu-toggle,
        #ws-menu .sim-menu-toggle {
            width: 22px;
            height: 20px;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 0;
            background: var(--clr-surface);
            color: var(--clr-text);
            border: 1px solid var(--border-color);
            border-radius: 4px;
            font-size: .82rem;
            line-height: 1;
            cursor: pointer;
        }

        /* v14.57: gleiches Aussehen wie die übrigen ☰-Knöpfe (22×20,
           eckig); nur ein leichter Schatten, da es frei über dem
           Programmierbereich schwebt. Senkrecht mittig zur Blockanzahl. */
        #ws-menu {
            height: 25px;
        }

        /* v15.42: feste Höhe = #ws-menu, damit die Blockanzahl unabhängig von der Seitenschrift
           mittig neben dem ☰-Knopf bleibt (mit der neuen serifenlosen Schrift wurde sie 2 px höher) */
        @media (min-width: 701px) {
            #block-counter {
                box-sizing: border-box;
                height: 25px;
                padding-top: 0;
                padding-bottom: 0;
            }
        }

        #ws-menu .sim-menu-toggle {
            box-shadow: var(--shadow);
        }

        #task-menu .sim-menu-toggle:hover,
        #task-menu.open .sim-menu-toggle,
        #ws-menu .sim-menu-toggle:hover,
        #ws-menu.open .sim-menu-toggle {
            border-color: var(--clr-accent);
            color: var(--clr-accent);
        }

        #task-menu .sim-menu-panel,
        #ws-menu .sim-menu-panel {
            min-width: 260px;
        }

        #task-menu-panel > #task-collapse-btn {
            display: grid;
            grid-template-columns: 22px 1fr;
            justify-items: start;
            align-items: center;
            gap: 8px;
            width: 100%;
            padding: 6px 8px;
            border: none;
            border-radius: 6px;
            background: none;
            text-align: left;
            color: var(--clr-text);
            font-family: Arial, Helvetica, sans-serif;
            font-size: .74rem;
            font-weight: 600;
            cursor: pointer;
        }

        #task-menu-panel > #task-collapse-btn::after {
            content: none;
        }

        #task-menu-panel > #task-collapse-btn #task-collapse-icon {
            justify-self: center;
        }

        #task-menu-panel > #task-collapse-btn:hover {
            background: var(--clr-bg2, rgba(127, 127, 127, .12));
        }

        /* ── Tastatur-Kürzel in Menüs (v14.58) ──
           Ziffer 1-9 rechts an jedem Menüeintrag (siehe _menuKbdNumber()
           in funci.html); leer = kein Kürzel (mehr als 9 Einträge oder
           Menü noch nie geöffnet). Dritte Spalte im Raster der Einträge;
           beim Knopf mit Beschriftung aus ::after (title) per order hinter
           die Beschriftung sortiert. */
        kbd.menu-key {
            justify-self: end;
            min-width: 1.5em;
            padding: 0 4px;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            background: var(--box-bg);
            color: var(--clr-muted);
            font-family: Arial, Helvetica, sans-serif;
            font-size: .62rem;
            font-weight: 700;
            line-height: 1.5;
            text-align: center;
        }

        kbd.menu-key:empty {
            display: none;
        }

        .sim-menu .sim-menu-panel > button,
        .sim-menu .sim-menu-panel .sim-menu-check,
        #task-menu-panel > #task-collapse-btn {
            grid-template-columns: 22px 1fr auto;
        }

        .sim-menu .sim-menu-panel > button::after {
            order: 1;
        }

        .sim-menu .sim-menu-panel > button > kbd.menu-key {
            order: 2;
        }

        .lang-switcher-item kbd.menu-key {
            float: right;
            margin-left: 12px;
        }

        /* Standard- und WebKit-Vollbild bewusst in GETRENNTEN Regeln - ein
           unbekannter Selektor verwirft sonst die ganze Liste. */
        #sim-focus-wrap:fullscreen kbd.menu-key,
        #mob-sim-focus-wrap:fullscreen kbd.menu-key {
            display: none;
        }

        #sim-focus-wrap:-webkit-full-screen kbd.menu-key,
        #mob-sim-focus-wrap:-webkit-full-screen kbd.menu-key {
            display: none;
        }

        /* sichtbarer Tastaturfokus auf Menüeinträgen */
        .sim-menu-panel button:focus-visible,
        .sim-menu-panel input:focus-visible,
        .lang-switcher-item:focus-visible {
            outline: 2px solid var(--clr-accent2);
            outline-offset: 1px;
        }

        /* ── PRIMM: Gesamtrückmeldung in der Kopfleiste (v14.61) ──
           eigene volle Zeile unter Schrittleiste/Knöpfen innerhalb der
           klebenden .primm-header - bleibt beim Scrollen sichtbar. Die
           Originalmeldung im Phaseninhalt wird ausgeblendet, wenn sie
           selbst die Gesamtrückmeldung ist (siehe
           _primmUpdateHeaderStatus()). */
        #primm-modal .primm-header-status {
            flex-basis: 100%;
            order: 3;
            margin: 2px 0 0;
            padding: 6px 12px;
            border-radius: 8px;
            font-size: .86rem;
            font-weight: 700;
            line-height: 1.35;
        }

        #primm-modal .primm-header-status[hidden] {
            display: none;
        }

        #primm-modal .primm-header-status.match {
            background: color-mix(in srgb, var(--success, #16a34a) 18%, var(--clr-surface));
            color: var(--success, #16a34a);
        }

        #primm-modal .primm-header-status.mismatch {
            background: color-mix(in srgb, var(--warning) 18%, var(--clr-surface));
            color: var(--warning);
        }

        #primm-modal .primm-comparison.primm-feedback-in-header {
            display: none;
        }

        /* GEÄNDERT (live angefordert, v14.75: "verbreitere auf Tablets die
           Zugleisten ... so, dass man mit den Fingern die Leiste gut ziehen
           kann, jedoch die Leiste nicht zu dick ist und zu viel Platz
           wegnimmt"): auf Geräten mit Touch-Bedienung (any-pointer: coarse -
           iPad/Android-Tablet, auch mit angestecktem Trackpad) wird die
           SICHTBARE Leiste nur maßvoll breiter (9 → 14px, Griff größer und
           deutlicher), die BERÜHRFLÄCHE aber per unsichtbarem ::before um
           je 10px zu beiden Seiten erweitert (insgesamt 34px). Dieser
           Streifen liegt über dem Rand der Nachbarbereiche, nimmt also
           keinen Platz weg. Desktop mit Maus bleibt unverändert.
           Hinweis: Simulations-Leiste am Smartphone (#mob-sim-resize-handle)
           ist eine eigene Regel weiter oben und nicht betroffen. */
        @media (any-pointer: coarse) {
            #rightpanel-resize-handle {
                position: relative;
                width: 14px;
            }

            #sim-resize-handle {
                position: relative;
                height: 14px;
                z-index: 5; /* über Simulation/Konsole, unter den ☰-Menüs (400) */
            }

            #rightpanel-resize-handle::before,
            #sim-resize-handle::before {
                content: '';
                position: absolute;
                inset: -10px 0;
                z-index: 1;
            }

            #rightpanel-resize-handle::before {
                inset: 0 -10px;
            }

            .rightpanel-resize-grip {
                width: 6px;
                height: 48px;
                opacity: .75;
            }

            .sim-resize-grip {
                width: 48px;
                height: 6px;
                opacity: .75;
            }
        }

/* v15.05: Hinweis bei fehlender Verbindung (offline / Server antwortet nicht).
   Klickt nicht weg und blockiert nichts (pointer-events: none), oben mittig. */
#conn-banner {
    position: fixed;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 100000;
    max-width: min(640px, calc(100vw - 16px));
    box-sizing: border-box;
    padding: 6px 12px;
    border: 2px solid var(--warning);
    border-radius: 8px;
    background: var(--clr-surface);
    color: var(--clr-text);
    font-size: .85rem;
    line-height: 1.35;
    box-shadow: var(--shadow-lg);
    pointer-events: none;
}
#conn-banner[hidden] { display: none; }

/* v15.05: Grund einer nicht bestandenen Turtle-Prüfung im Fehlerfenster */
#e-diag {
    text-align: left;
    margin: -4px 0 14px;
    padding: 8px 12px;
    border-radius: 8px;
    background: var(--clr-bg);
    border: 1px solid var(--border-color);
    font-size: .82rem;
    line-height: 1.5;
    color: var(--clr-text);
    max-height: 40vh;
    overflow-y: auto;
}
#e-diag .e-diag-title { font-weight: 600; margin-bottom: 4px; }
#e-diag ul { margin: 0; padding-left: 18px; }
#e-diag li + li { margin-top: 4px; }
#e-diag .e-diag-stats { color: var(--clr-muted); }

/* ═══════════════════════════════════════════════════════════════
   v15.24 (PO-Wunsch): Zoom-Fenster für Konsole, Code und
   Visualisierung - gleicher Aufbau, so groß wie möglich (Titel,
   Symbolleiste, Inhalt füllt den Rest, Fußleiste mit Ausführen/
   Geschwindigkeit/Debug/Schritt/Stopp und Schließen). Steht am
   Dateiende und nennt die IDs, damit es die älteren Einzelregeln
   (u.a. im Smartphone-Media-Query) sicher überschreibt.
═══════════════════════════════════════════════════════════════ */
#console-modal .modal-box,
#code-modal .modal-box,
#viz-modal .modal-box {
    width: calc(100vw - 16px);
    max-width: none;
    height: calc(100vh - 16px);
    height: calc(100dvh - 16px);
    max-height: none;
    display: flex;
    flex-direction: column;
    padding: 14px 18px 12px;
    text-align: left;
    border-color: var(--clr-accent2);
    overflow: hidden;
}

#console-modal .modal-box > *,
#code-modal .modal-box > *,
#viz-modal .modal-box > * {
    flex-shrink: 0;
}

.out-zoom-toolbar {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    flex-wrap: wrap;
    gap: 8px;
    min-height: 34px;
    margin-bottom: 8px;
}

/* Zoom-Regler (und bei der Visualisierung der PNG-Knopf) immer rechts */
.out-zoom-toolbar > #console-zoom-controls,
.out-zoom-toolbar > #code-zoom-controls,
.out-zoom-toolbar > #viz-zoom-controls {
    margin-left: auto;
}

#console-modal-output.out-zoom-body,
#code-output.out-zoom-body,
#viz-modal-scroll.out-zoom-body {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    overflow: auto;
    margin: 0 0 10px;
}

.out-zoom-footer {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}

.out-zoom-footer > .mbtn {
    margin: 0 0 0 auto;
}

#out-zoom-runbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
}

/* v15.25 (PO-Wunsch): Struktogramm/Flussdiagramm im Zoom-Fenster so groß wie
   JavaScript/Python im Code-Fenster (gleiche Werte wie .code-lang-btn) */
#viz-modal-type-tabs .viz-type-btn {
    padding: 6px 12px;
    background: var(--box-bg);
    color: var(--clr-text);
    font-size: .75rem;
    font-weight: 600;
}

#viz-modal-type-tabs .viz-type-btn.active {
    background: var(--clr-accent2);
    color: var(--clr-on-accent);
}

/* v15.26 (PO-Wunsch: Visualisierung verschiebt sich beim Debuggen, wenn Variablen
   dazukommen): Variablenbereich mit fester Höhe (--var-area-height, per
   #var-resize-handle änderbar, _initVarAreaResizeHandle()). flex-basis statt
   height: wird es eng, schrumpft er bis 40 px, bevor die Ausgabe unter ihre
   Mindesthöhe (90 px = SIM_AREA_MIN_BELOW) fällt. Inhalt ändert die Höhe nie. */
#var-display,
#list-display,
#callstack-display {
    flex: 0 1 var(--var-area-height, 72px);
    height: auto;
    min-height: 40px;
    max-height: none;
    overflow: auto;
    box-sizing: border-box;
    padding: 5px 8px;
    border-bottom: 1px solid var(--border-color);
    background: var(--clr-bg);
}

#console-wrap,
#embedded-code-wrap,
#embedded-viz-wrap {
    min-height: 90px;
}

/* Inventar ebenfalls fest (eine Reihe Kacheln, mehr per Scrollen) - sonst
   schiebt der erste aufgehobene Gegenstand alles darunter um ~15 px */
#grid-inventory-list {
    height: 44px;
    min-height: 0;
    max-height: none;
    box-sizing: border-box;
}

#var-resize-handle {
    flex-shrink: 0;
    height: 9px;
    margin: -1px 0;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: row-resize;
    background: var(--clr-bg);
    border-bottom: 1px solid var(--border-color);
    touch-action: none;
}

#var-resize-handle:hover,
#var-resize-handle:focus-visible,
#var-resize-handle.dragging {
    background: rgba(var(--success-rgb), 0.12);
    outline: none;
}

#var-resize-handle:hover .sim-resize-grip,
#var-resize-handle:focus-visible .sim-resize-grip,
#var-resize-handle.dragging .sim-resize-grip {
    opacity: 1;
    background: var(--clr-accent);
}

body.hide-out-console #var-resize-handle {
    display: none;
}

@media (any-pointer: coarse) {
    #var-resize-handle {
        position: relative;
        height: 14px;
        z-index: 5;
    }

    #var-resize-handle::before {
        content: '';
        position: absolute;
        inset: -10px 0;
        z-index: 1;
    }
}

/* v15.27 (PO-Wunsch): Prozentzahl zwischen − und + überall; Klick = 100 % */
.zoom-reset {
    cursor: pointer;
    border-radius: 3px;
    user-select: none;
}

.zoom-reset:hover {
    color: var(--clr-accent);
    text-decoration: underline;
}

.zoom-reset:focus-visible {
    outline: 2px solid var(--clr-accent);
    outline-offset: 1px;
}

/* NEU v15.49: Knopf „Ausgabe“ zum Öffnen des ganz eingeklappten Ausgabebereichs – nur an
   Touch-Geräten (dort schob das Aufziehen an der Ziehleiste am Bildschirmrand das
   Safari-Fenster weg), Gegenstück zu „Aufgaben“ links (#sidebar-collapse-btn). Im
   eingeklappten Zustand ersetzt er dort die Ziehleiste; offen bleibt die Leiste. */
#rightpanel-expand-btn {
    display: none;
    right: 0;
    left: auto;
    border-radius: 6px 0 0 6px;
    border-right: none;
    height: 108px;
    flex-direction: column;
    gap: 6px;
}

.rightpanel-expand-label {
    writing-mode: vertical-rl;
    text-orientation: mixed;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .02em;
    white-space: nowrap;
}

@media (any-pointer: coarse) {
    #layout.rightpanel-collapsed-drag #rightpanel-expand-btn {
        display: flex;
    }

    #layout.rightpanel-collapsed-drag #rightpanel-resize-handle {
        display: none;
    }
}

/* ── NEU v15.52: Basisansicht ruhiger (PO-Auftrag „Schüler:innen sind von zu vielen Elementen
   schnell abgelenkt“), Punkt 1 – Steuerleiste zeigt nur, was gerade geht ──
   „Stop“ und der Geschwindigkeitsregler nur während eines Laufs (Stop ist sonst disabled,
   siehe _setRunBtnState()); „Lösung“ im ☰-Menü des Aufgabenbereichs (#task-menu-solution),
   „Tipp“ abgesetzt rechts. Standardansicht unverändert. */
html[data-view-mode="basic"] .btn-stop:disabled,
html[data-view-mode="basic"] #control-bar:has(#btn-stop:disabled) > .exec-speed-wrap,
html[data-view-mode="basic"] #mob-control-bar:has(#mob-btn-stop:disabled) > .exec-speed-wrap {
    display: none !important;
}

html[data-view-mode="basic"] #control-bar .btn-solution {
    display: none !important;
}

html[data-view-mode="basic"] #control-bar .btn-hint {
    margin-left: auto;
}

html:not([data-view-mode="basic"]) #task-menu-solution,
body.not-logged-in #task-menu-solution {
    display: none !important;
}

/* ── NEU v15.53: Punkt 3 – Aufgabenbereich auf das Wesentliche ──
   Beide Ansichten: ohne echte Blockgrenze nur die Anzahl (#block-counter.no-limit, siehe
   _hasRealBlockLimit()). Basisansicht: Ziel-Pille aus, wenn sie nur den Aufgabentext
   wiederholt (meta-goal-dup); Schriftgröße im ☰-Menü statt im Kopf; nicht aktive Stufen
   nur als Sterne. */
#block-counter.no-limit .cnt-sep,
#block-counter.no-limit #cnt-max,
#block-counter.no-limit #counter-bar {
    display: none;
}

html[data-view-mode="basic"] .meta-goal-dup,
html[data-view-mode="basic"] #task-font-ctrl {
    display: none !important;
}

.task-menu-font {
    display: none;
    align-items: center;
    gap: 6px;
    padding: 5px 8px;
    font-family: Arial, Helvetica, sans-serif;
    font-size: .74rem;
    font-weight: 600;
    color: var(--clr-text);
}

html[data-view-mode="basic"] .task-menu-font {
    display: flex;
}

.task-menu-font-title {
    flex: 1;
    padding-left: 30px; /* bündig mit dem Text der übrigen Einträge (Symbolspalte 22 px + Abstand) */
}

.task-menu-font button {
    min-width: 26px;
    height: 24px;
    padding: 0 6px;
    border: 1px solid var(--border-color);
    border-radius: 5px;
    background: var(--clr-surface);
    color: var(--clr-text);
    cursor: pointer;
}

html[data-view-mode="basic"] .diff-btn:not(.active) .tab-txt {
    display: none;
}

html[data-view-mode="basic"] .diff-btn:not(.active) {
    padding-left: 8px;
    padding-right: 8px;
}

#task-menu-panel > #task-menu-solution {
    display: grid;
    grid-template-columns: 22px 1fr auto;
    justify-items: start;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 6px 8px;
    border: none;
    border-radius: 6px;
    background: none;
    text-align: left;
    color: var(--clr-text);
    font-family: Arial, Helvetica, sans-serif;
    font-size: .74rem;
    font-weight: 600;
    cursor: pointer;
}

#task-menu-panel > #task-menu-solution > span:first-child {
    justify-self: center;
}

#task-menu-panel > #task-menu-solution:hover {
    background: var(--clr-bg2, rgba(127, 127, 127, .12));
}

#task-menu-panel > #task-menu-solution::after {
    content: none;
}

/* ── NEU v15.55: Punkt 4 – Basisansicht, Turtle/Grid ohne Ausgabe-Baustein: Konsole aus, die
   Simulation füllt den Platz (body.auto-hide-console, siehe _updateAutoConsole(); dieselbe
   Wirkung wie body.hide-out-console weiter oben). ── */
body.auto-hide-console .sim-menu-out,
body.auto-hide-console .out-area-console,
body.auto-hide-console #sim-resize-handle,
body.auto-hide-console #var-resize-handle {
    display: none !important;
}

body.auto-hide-console #sim-focus-wrap {
    flex: 1;
    min-height: 0;
}

body.auto-hide-console #sim-canvas-wrap,
body.auto-hide-console #grid-canvas-wrap {
    flex: 1;
    height: auto;
    min-height: 0;
}

/* ── NEU v15.57: Punkt 6 – ein klarer nächster Schritt (nur Basisansicht): „Ausführen“ nach dem
   ersten angehängten Block, „Tipp“ nach einem Fehlversuch leuchten kurz auf (_attnPulse()). ── */
@keyframes funciAttnPulse {
    0%, 100% {
        box-shadow: 0 0 0 0 rgba(var(--success-rgb, 16, 185, 129), .55);
    }

    50% {
        box-shadow: 0 0 0 7px rgba(var(--success-rgb, 16, 185, 129), 0);
    }
}

html[data-view-mode="basic"] .attn-pulse {
    animation: funciAttnPulse 1.4s ease-in-out infinite;
    outline: 2px solid var(--clr-accent);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    html[data-view-mode="basic"] .attn-pulse {
        animation: none;
    }
}

/* ── ☰-Menü in der Kopfzeile (v15.61, PO-Auftrag nach iPad-Screenshot 05.11.2026) ──
   Standardansicht: Hell/Dunkel + Sprachwahl rechts neben dem Vollbild-Knopf in einem Menü (gleiche Mechanik
   wie die übrigen ☰-Menüs: toggleSimMenu(), Tastaturbedienung, Ziffern). Einträge einzeilig mit fester
   Ziffernspalte – im alten Sprachmenü brach „Französisch“ mit dem float-Kürzel in zwei Zeilen um. */
#header-menu {
    margin-left: 0;
}

#header-menu .sim-menu-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    line-height: 1;
}

#header-menu.open .sim-menu-toggle {
    border-color: var(--clr-accent);
    color: var(--clr-accent);
}

#header-menu .sim-menu-panel {
    min-width: 220px;
    max-width: calc(100vw - 16px);
    max-width: calc(100dvw - 16px);
    box-sizing: border-box;
    z-index: 600;
}

#header-menu-langs {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

#header-menu-langs .header-lang-item {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 6px 8px;
    border: none;
    border-radius: 6px;
    background: none;
    color: var(--clr-text);
    font-family: Arial, Helvetica, sans-serif;
    font-size: .8rem;
    font-weight: 600;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}

#header-menu-langs .header-lang-item:hover {
    background: var(--clr-bg2, rgba(127, 127, 127, .12));
}

#header-menu-langs .header-lang-item.active {
    color: var(--clr-accent);
    font-weight: 800;
}

#header-menu-langs .header-lang-item:focus-visible {
    outline: 2px solid var(--clr-accent2);
    outline-offset: 1px;
}

#header-menu .sim-menu-check {
    white-space: nowrap;
}
