* {
    box-sizing: border-box;
    touch-action: manipulation;
    /* App-Verhalten, nicht Webseite: NICHTS ist markierbar, und iOS zeigt kein
       Lupe-/Kopieren-/Nachschlagen-Menü bei langem Tippen. */
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}
/* Echte Eingabefelder müssen editierbar/markierbar bleiben (delay/duration). */
input, textarea { -webkit-user-select: text; user-select: text; }
html {
    background-color: #000;
    &.font-codystar { font-family: 'Codystar', system-ui; font-weight: 900; font-size: 14px; }
    &.font-dotgothic16 { font-family: 'DotGothic16', system-ui; }
    &.font-inter { font-family: 'Inter', system-ui; }
    &.font-libre-baskerville { font-family: 'Libre Baskerville', system-ui; }
    &.font-marcellus { font-family: 'Marcellus', system-ui; }
    &.font-open-sans { font-family: 'Open Sans', system-ui; }
    &.font-orbitron { font-family: 'Orbitron', system-ui; }
    &.font-raleway-dots { font-family: 'Raleway Dots', system-ui; }
    &.font-roboto-mono { font-family: 'Roboto Mono', system-ui; }
    &.font-silkscreen { font-family: 'Silkscreen', system-ui; font-size: 14px; }
    &.font-teko { font-family: 'Teko', system-ui; }
    &.font-wdxl-lubrifont-tc { font-family: 'WDXL Lubrifont TC', system-ui; }
    /* --- Future-Retro --- */
    &.font-audiowide { font-family: 'Audiowide', system-ui; }
    &.font-michroma { font-family: 'Michroma', system-ui; }
    &.font-major-mono-display { font-family: 'Major Mono Display', monospace; }
    &.font-vt323 { font-family: 'VT323', monospace; }
    &.font-share-tech-mono { font-family: 'Share Tech Mono', monospace; }
    &.font-tektur { font-family: 'Tektur', system-ui; }
    &.font-monoton { font-family: 'Monoton', system-ui; }
    &.font-wallpoet { font-family: 'Wallpoet', system-ui; }
    &.font-syncopate { font-family: 'Syncopate', system-ui; }
    &.font-turret-road { font-family: 'Turret Road', system-ui; }
    &.font-zen-dots { font-family: 'Zen Dots', system-ui; }
    &.font-quantico { font-family: 'Quantico', system-ui; }
    /* --- Crazy Display Fonts --- */
    &.font-rubik-glitch { font-family: 'Rubik Glitch', system-ui; }
    &.font-rubik-wet-paint { font-family: 'Rubik Wet Paint', system-ui; }
    &.font-rubik-iso { font-family: 'Rubik Iso', system-ui; }
    &.font-rubik-maze { font-family: 'Rubik Maze', system-ui; }
    &.font-rubik-burned { font-family: 'Rubik Burned', system-ui; }
    &.font-rubik-puddles { font-family: 'Rubik Puddles', system-ui; }
    &.font-rubik-vinyl { font-family: 'Rubik Vinyl', system-ui; }
    &.font-faster-one { font-family: 'Faster One', system-ui; }
    &.font-bungee-shade { font-family: 'Bungee Shade', system-ui; }
    &.font-nosifer { font-family: 'Nosifer', system-ui; }
    &.font-eater { font-family: 'Eater', system-ui; }
    &.font-rubik-marker-hatch { font-family: 'Rubik Marker Hatch', system-ui; }
}
body { 
    --glow-intensity: 5;
    margin: 0;
    background-color: #000; 
    color: #fff; 
    font-weight: 100;
    line-height: 1;
    overflow: hidden;
    /* &.browser-version { overflow-y: visible; } */
    transition: filter 0.3s ease-in-out, background-color 0.2s ease-in-out;
    &:has(dialog[open]) { filter: blur(5px); }
    &.body-screen-flash { background-color: rgba(255, 255, 255, 1); }
    &.glow-effect {
        /* 7 Blur-Schichten (1–7px × --glow-intensity). NICHT auf 4 reduzieren: das macht den
           Glow bei hoher Stärke (Regler ~10) dünn/fad, weil die Schichten dann zu weit
           auseinander liegen und kaum noch blühen. Die DICHTE (viele eng gestaffelte Schichten)
           ist genau das, was den satten Neon-Look ausmacht. Perf ist hier unkritisch: die Ziffern
           sind per `will-change: transform` (s.u.) eigene GPU-Ebenen → der Glow wird EINMAL
           gerastert und nur bei echtem Ziffernwechsel (≈1×/s) neu gezeichnet, nicht pro Frame. */
        text-shadow:
            0 0 calc(var(--glow-intensity) * 1px) #fff,
            0 0 calc(var(--glow-intensity) * 2px) #fff,
            0 0 calc(var(--glow-intensity) * 3px) var(--glow-color, #0073e6),
            0 0 calc(var(--glow-intensity) * 4px) var(--glow-color, #0073e6),
            0 0 calc(var(--glow-intensity) * 5px) var(--glow-color, #0073e6),
            0 0 calc(var(--glow-intensity) * 6px) var(--glow-color, #0073e6),
            0 0 calc(var(--glow-intensity) * 7px) var(--glow-color, #0073e6);
    }
}
/* Einstellungs-Panel: KEIN Glow auf den Labels – einheitlich mit Buttons/Inputs
   (die haben text-shadow:none). Das große „zur Uhr"-Label liegt AUSSERHALB von
   .settings und behält daher den Glow. (id-Spezifität schlägt body.glow-effect.) */
#app-wrapper .cell .settings { text-shadow: none; }
body #app-wrapper {
    /* Höhe je nach Kontext (s. die zwei Regeln direkt darunter):
       - INSTALLIERT/Standalone: `100vh` = volle iOS-Bildschirmhöhe inkl. Safe-Areas →
         Gitter reicht bis zur Unterkante (svh/dvh würden den Home-Indicator-Bereich abziehen
         = ~34px-Lücke unten).
       - BROWSER (Safari-Tab): `100vh` reicht hinter die Statusbar → „alles nach oben
         verschoben". Dort `100svh` = Safari-Content-Bereich (unter der Statusbar).
       `100vh` hier ist Fallback (vor dem JS-Klassen-Setzen). */
    height: 100vh;
    width: 100vw;
    max-width: 800px;
    margin: auto;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: 1fr 1fr 1fr;
    transition: grid-template-rows 0.5s ease-in-out;
    /* Die oberen Zellen werden in den Einstellungen NICHT mehr größer (= gleiche Größe wie
       in der Uhr). Vorteil: die Hintergrund-Labels der unteren Zellen „springen" nicht mehr
       (kein Zellen-Resize). cell1 (font/glow/sound + reset) ist dafür minimal kompakter
       gestylt, damit es auch auf dem kleinsten iPhone (SE) in 1/3 Höhe passt. */
    body.settings-mode & { grid-template-rows: 1fr 1fr 1fr; }
    /* Höhe je Kontext (s. Kommentar oben): Browser-Tab = Safari-Content-Bereich (100svh,
       nicht hinter die Statusbar), installiert/standalone = volle Höhe bis unten (100vh). */
    body.browser-version & { height: 100svh; }
    body.app-version & { height: 100vh; }
    .cell {
        position: relative;
        display: grid;
        align-items: center;
        justify-content: center;
        grid-template-columns: 0fr 1fr 0fr;
        grid-template-rows:  0fr 1fr 0fr;
        overflow: hidden;
        padding: 8px;
        border-top: 1px solid rgba(255, 255, 255, 0.1);
        /* border-color UND background-color hier zusammen, sonst überschreibt die
           Flash-Klasse (.cell-flash-effect setzt `transition: background-color …`) die
           Rahmen-Transition → der Rahmen würde beim Antippen sofort springen statt zu faden. */
        transition: border-color .8s ease, background-color .2s ease-in-out;
        &:nth-child(even) { border-left: 1px solid rgba(255, 255, 255, 0.1); }
        /* Die obersten beiden Zellen ohne obere Trennlinie. */
        &:nth-child(1), &:nth-child(2) { border-top: 0; }
        /* In den Einstellungen die Gitterlinien deutlich heller → man sieht, dass die
           unteren vier Einstellungsblöcke zu den unteren vier Kacheln gehören. */
        body.settings-mode & { border-color: rgba(255, 255, 255, 0.4); }
        &:nth-child(even) .digit { justify-content: start; }
        &:nth-child(odd) .digit { justify-content: end; }
        &.cell-flash-effect {
            background-color: rgba(255, 255, 255, 0.3); /* Example: a light, semi-transparent white flash */
            /* KEINE eigene transition hier – die liegt jetzt auf der Basis-`.cell`-Regel
               (border-color + background-color zusammen), sonst überschreibt diese Klasse
               die Rahmen-Transition und der Rahmen springt beim Antippen (statt zu faden). */
        }
        .digit {
            grid-column: 1/4;
            grid-row: 1/4;
            height: 100%;
            width: 100%;
            font-size: 28vh;
            font-size: 28svh;
            /* Gleich breite Ziffern → die rechtsbündige linke Ziffer „springt" beim
               Wertwechsel (z. B. Timer-Neustart) NICHT mehr seitlich (bei proportionalen
               Schriften hatten Ziffern unterschiedliche Breiten). */
            font-variant-numeric: tabular-nums;
            /* Schriftstärke per Regler: echtes font-weight (greift bei Mehr-Gewicht-Schriften).
               Die synthetische Kontur (`-webkit-text-stroke`) wird BEWUSST nur ab Stufe > 100
               gesetzt – siehe `html.digit-stroked …` unten. */
            font-weight: var(--digit-weight, 100);
            /* Schriftfarbe der Ziffern (per Einstellung; Standard weiß). Stroke folgt via
               currentColor, Glow bleibt unabhängig (--glow-color). */
            color: var(--digit-color, #fff);
            display: grid;
            align-items: center;
            justify-items: center;
            scale: 0; /* Standard: ausgeblendet – die passende Ebene blendet je Modus ein */
            transition: scale .5s ease-in-out .25s, opacity .5s ease-in-out .25s;
            body.preload & { scale: 0; position: absolute; }
        }
        /* Uhr-Ebene: nur im Uhr-Modus sichtbar; erscheint leicht verzögert,
           nachdem die Timer-/Stoppuhr-Ziffern weganimiert sind. */
        .digit.is-clock {
            body.clock-mode & { scale: 1; opacity: 1; transition-delay: .55s; }
        }
        /* Timer/Stoppuhr-Ebene: mittlere Kacheln (3,4) immer, äußere (1,2) nur bei
           Minuten (view_minutes), 5/6 nie. Erscheint ebenfalls leicht verzögert. */
        .digit.is-run {
            body.stopwatch-mode &, body.timer-mode & {
                grid-column: 1/3;
                scale: 1;
                opacity: 1;
                transition-delay: .55s;
                .cell:nth-child(1) &, .cell:nth-child(2) &, .cell:nth-child(5) &, .cell:nth-child(6) & {
                    scale: 0;
                }
                .cell:nth-child(1) &.view_minutes, .cell:nth-child(2) &.view_minutes {
                    scale: 1;
                }
            }
        }
        /* Auch die uebrigen Beschriftungs-Ebenen folgen der Schriftfarbe (Niels 2026-08-19:
           „Wenn man in der Stoppuhr ist oder im Timer ist ... muessen dort die Elemente
           natuerlich auch in der gewaehlten Schriftfarbe sein."). `.label` hatte das schon;
           hier fehlten die Hundertstel, pause/resume und die Wiederholungszahl. */
        .label_small {
            color: var(--digit-color, #fff);
            scale: 0;
            grid-column: 3/4;
            grid-row: 1/4;
            transition: all .5s ease-in-out 0s;
            .cell:nth-child(2) & { 
                scale: 1;
            }
            body.stopwatch-mode &, body.timer-mode & {
                .cell:nth-child(4) & { scale: 1; }
            }
        /* ⚠️ UNSICHTBARE label_small DÜRFEN KEINEN PLATZ BELEGEN (Befund Niels 2026-08-19:
           „wenn die Stoppuhr dann richtig losgeht, rückt die linke Null abrupt ein Stückchen
           nach rechts"). Die Zelle ist ein Raster `0fr 1fr 0fr`; ein `0fr`-Streifen ist so
           breit wie sein INHALT. Während des Vorlaufs setzt der Code in Zelle 3 ein „-" als
           Phasen-Merker (`updateStopwatchDisplay`/`updateTimerDisplay`) — sichtbar ist das
           Label dort nie (`scale: 0`), aber es machte die dritte Spalte 10 px breit und nahm
           sie der Ziffern-Spalte weg. Gemessen: Spalten `0px 169px 10px` im Vorlauf gegen
           `0px 179px 0px` danach; die Ziffer in Zelle 3 ist rechtsbündig und sprang deshalb
           exakt diese 10 px nach rechts.
           Sichtbar ist das Label nur in Zelle 2 (Wiederholungen) und — in den Lauf-Modi — in
           Zelle 4 (Hundertstel). Überall sonst ist es reiner Merker und gehört aus dem Fluss.
           ⚠️ `display: none` statt `scale: 0`: `scale` ändert nur die Darstellung, nicht die
           Größe im Raster. Zelle 4 bleibt bewusst unangetastet — dort blendet das Label per
           `scale` ein, und `display` lässt sich nicht animieren. */
        .cell:nth-child(1) &, .cell:nth-child(3) &,
        .cell:nth-child(5) &, .cell:nth-child(6) & { display: none; }
        }
        /* ⚠️ DIE ECK-LABELS FOLGEN DER SCHRIFTFARBE (Niels 2026-08-19: „Wenn die Schriftfarbe
           umgestellt wird, sollen auch die Labels in der Uhr Ansicht die eingestellte Farbe
           haben. In den Einstellungen soll alles in der Farbe bleiben, wie es jetzt auch ist.")
           Die Labels erbten bisher das weiße `color` vom `body`.
           ⚠️⚠️ OHNE JEDE BEDINGUNG — und das ist der zweite Anlauf. Zuerst stand hier
           `body:not(.settings-mode) &`, in der Annahme, das halte die Farbe während des
           Hinausfahrens. Falsch: Die Klasse `settings-mode` sitzt SOFORT am body, die Farbe
           kippte also im selben Moment auf Weiß, während die Bewegung noch 0,5 s lief —
           genau der von Niels gemeldete Fehler („werden die Labels erst weiß und dann fahren
           sie raus"). Eine Bedingung, die an einem Zustandswechsel hängt, taugt nicht für eine
           Eigenschaft, die WÄHREND des Wechsels gelten soll.
           Die Einstellungen bleiben trotzdem weiß: Diese Regel trifft nur `.label` — die sechs
           Eck-Labels, allesamt direkte Kinder der Zellen. Die Beschriftungen im Panel sind
           `<label>`-Elemente ohne diese Klasse. */
        .label {
            color: var(--digit-color, #fff);
            grid-column: 1 / 4;
            grid-row: 1 / 4;
            align-self: start;
            width: 100%;
            /* Labels fahren ERST, wenn die Ziffern-Animation komplett durch ist:
               Ziffern fertig bei .55s Delay + .5s Dauer = 1.05s → Label-Delay 1.05s.
               Gilt für beide Richtungen (raus bei Timer/Stoppuhr, rein zurück zur Uhr). */
            transition: translate .5s ease-in-out 1.05s;
            /* body.preload = App-Start: Labels starten ausgefahren und kommen – wie beim
               Zurückkehren von der Stoppuhr – ERST nach der Ziffern-Animation (1.05s) rein. */
            .cell:nth-child(odd) & {
                body.stopwatch-mode &, body.timer-mode &, body.preload & { translate: -100%; }
            }
            .cell:nth-child(even) & {
                text-align: right;
                body.stopwatch-mode &, body.timer-mode &, body.preload & { translate: 100%; }
            }
            /* ⚠️ IN DEN EINSTELLUNGEN FAHREN ALLE ECK-LABELS RAUS (Niels 2026-08-19):
               „Eigentlich dachte ich, dass es sinnvoll ist, die Labels stehen zu lassen …
               Das sieht aber unaufgeräumt aus und bringt vermutlich nicht mehr Verständnis."
               Vorher fuhr NUR „Einstellungen" (Zelle 2) hinaus, die übrigen blieben mit
               opacity .2 stehen — also lagen „timer 30s -5" und „stopwatch -10" als blasse
               Schrift über den Bedienelementen.
               Jetzt dieselbe Bewegung wie beim Wechsel in Timer/Stoppuhr: ungerade Zellen nach
               links, gerade nach rechts. Zelle 2 ist gerade und behält damit automatisch genau
               ihr bisheriges Verhalten (translate: 100%) — die Regel wurde nicht ersetzt,
               sondern verallgemeinert. */
            body.settings-mode & {
                opacity: .2;
                pointer-events: none;
                position: absolute;
                /* ⚠️ ZUERST DIE LABELS, DANN DIE EINSTELLUNGEN (Niels 2026-08-19: „erst die
                   Labels wegfahren und wenn die Labels weggefahren sind, erst dann sollen die
                   Einstellungen sichtbar werden … so, wie bei den anderen Animationen auch").
                   Hier stand `.5s` Verzögerung — dieselbe wie bei `.settings`. Beide liefen
                   also GLEICHZEITIG los. Solange die Labels nur auf opacity .2 abblendeten,
                   fiel das nicht auf; seit sie wirklich hinausfahren, schoben sich Bewegung
                   und Einblendung übereinander.
                   Jetzt: Labels 0 → .5s, Einstellungen .5 → 1.0s (deren `.5s`-Verzögerung
                   steht unverändert bei `.settings`). Auf dem Rückweg gilt die Haus-Reihenfolge
                   aus Konvention #8 — erst verschwinden die Einstellungen (0 → .5s), die Labels
                   kommen bei 1.05s nach, gemeinsam mit allen anderen. */
                transition: opacity .5s ease-in-out 0s, translate .5s ease-in-out 0s;
                .cell:nth-child(odd) & { translate: -100%; }
                .cell:nth-child(even) & { translate: 100%; }
            }
        }
        .label_big { 
            color: var(--digit-color, #fff);
            scale: 0;
            grid-row: 2/2;
            grid-column: 1/4;
            font-size: 2rem; 
            text-align: center; 
            transition: all .5s ease-in-out 0s;
            body.stopwatch-mode &, body.timer-mode & { 
                .cell:nth-child(5) &, .cell:nth-child(6) & { 
                    scale: 1;
                    transition: all .5s ease-in-out .5s;
                }
            }
            body.timer-mode & { 
                .cell:nth-child(1) & { 
                    scale: 1;
                    transition: all .5s ease-in-out .75s;
                }
            }
            .cell:nth-child(2) & {
                scale: 0;
                body.timer-mode & {
                    scale: 1;
                    transition: all .5s ease-in-out 1s;
                }
                /* In den Einstellungen übernimmt das Inline-Label .settings-back das
                   „back to clock"; das label_big bleibt hier ausgeblendet (wird nur im
                   Timer-Modus für die Wiederholungszahl genutzt). */
                body.settings-mode & { scale: 0; }
            }
        }
        .label_medium {
            color: var(--digit-color, #fff);
            scale: 0;
            grid-row: 1/4;
            grid-column: 1/4;
            transition: all .5s ease-in-out 0s;
            .cell:nth-child(1) & { 
                translate: 0 -30px;
                text-align: center;
                body.timer-mode & { 
                    scale: 1;
                    transition: all .5s ease-in-out .75s;
                }
            }
        }
        .settings { 
            width: 100%;
            scale: 0;
            transition: all .5s ease-in-out .0s;
            grid-column: 1 / 4;
            grid-row: 1 / 4;
            .cell:nth-child(1) &, .cell:nth-child(2) & { align-self: start; }
            body.stopwatch-mode &, body.timer-mode & { display: none; }
            body.settings-mode & {
                opacity: 1;
                scale: 1;
                pointer-events: all;
                transition: all .5s ease-in-out .5s;
            }
            & > * {
                display: flex;
                flex-wrap: wrap;
                justify-content: space-between;
                align-items: center;
                column-gap: 15px;
                padding: 7px 0;
                border-bottom: 0; /* keine Trennstriche mehr im Panel – alles sind Buttons */
                &.sound-settings { padding-bottom: 0; }
                &.flash-settings, &.sound-settings, &.glow-settings {
                    justify-content: end;
                    label:not(.ios-switch) { margin-right: auto; }
                }
                input[type="color"] {
                    width: 25px;
                    height: 25px;
                    border: none;
                    padding: 0;
                    background: none;
                    margin-left: 5px;
                    margin-right: 5px;
                    border: 1px solid rgba(255, 255, 255, 0.1);
                }
                dialog {
                    background: #000;
                    color: #fff;
                    border: 1px solid rgba(255, 255, 255, 0.1);
                    /* nur der direkte Schließen-Button (×), NICHT verschachtelte Buttons
                       wie die Schrift-Vorschau-Zeilen */
                    & > button {
                        width: auto;
                        font-size: 29px;
                        position: absolute;
                        right: 0px;
                        top: 0px;
                        border: 0;
                        background: none;
                        padding: 10px;
                    }
                    h2 { margin-top: 2px; }
                    p { margin: 0; }
                }
                button.no_button_style {
                    all: unset;
                    cursor: pointer;
                }
                .glow-intensity {
                    display: flex;
                    justify-content: center;
                    flex-basis: 100%;
                    width: 100%;
                    input { flex-grow: 1; }
                    span { display: flex; align-items: center; margin-left: 10px; }
                }
            }

            .font-settings {
                label { margin-bottom: 5px; }
                select { width: 100%; }
            }
            .setting-item:last-child { border-bottom: none; }
            .setting-item label[for] { color: #fff; }
            input { 
                width: 47px; 
                height: 25px; 
                border-radius: 31px;
                padding: 0 7px;
                border: 1px solid rgba(255, 255, 255, 0.1);
                background-color: rgba(255, 255, 255, 0.1);
                color: #fff;
                text-align: right;
            }
            select {
                position: relative;
                font-family: inherit;
                font-size: .9rem;
                color: #fff;
                background-color: #000;
                border: 1px solid rgba(255, 255, 255, 0.1);
                border-radius: 31px;
                min-height: 34px;
                padding: 6px 28px 6px 13px;
                appearance: none;
                background-image: linear-gradient(45deg, transparent 50%, gray 50%), linear-gradient(135deg, gray 50%, transparent 50%);
                background-position: calc(100% - 15px) center, calc(100% - 10px) center;
                background-size: 5px 5px, 5px 5px, 1px 1.5em;
                background-repeat: no-repeat;
            }
            button {
                all: unset; 
                height: 23px; 
                width: 100%;
                border: 1px solid rgba(255, 255, 255, 0.1);
                border-radius: 31px;
                background-color: rgba(255, 255, 255, 0.1);
                color: #fff;
                padding: 0 3px;
                font-size: .8rem;
                text-shadow: none;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
                &:hover { background-color: rgba(255, 255, 255, 0.2); }
            }
            svg {
                fill: #fff;
                display: inline-block;
                width: 1rem;
                height: 1rem;
                margin-left: 5px;
                margin-right: 10px;
            }
            .function {
                gap: 5px;
                justify-content: start;
                & > div { 
                    display: flex;
                    flex-direction: column;
                    align-items: center;
                    flex-grow: 1;
                    input { display: none; }
                    label {
                        border: 1px solid rgba(255, 255, 255, 0.1);
                        border-radius: 31px;
                        background-color: rgba(255, 255, 255, 0.0);
                        color: #fff;
                        text-shadow: none;
                        padding: 4px 8px;
                        font-size: .9rem;
                        cursor: pointer;
                        width: 100%;
                        text-align: center;
                        &:hover { background-color: rgba(255, 255, 255, 0.2); } 
                    }
                    input:checked + label {
                        background-color: rgba(255, 255, 255, 0.2);
                    }
                }
            }
        }
        .timer-progress {
            grid-column: 1 / 4;
            grid-row: 1 / 4;
            justify-self: center;
            align-self: center;
            scale: 0;
            transition: scale .5s ease-in-out .25s;
            body.timer-mode & { scale: 1; transition: scale .5s ease-in-out 1s; }
            .circular-progress circle {
                fill: none;
                stroke-width: 3px;
                stroke-linecap: round;
            }
            .circular-progress circle.bg { stroke: rgba(255, 255, 255, 0.1); }
            .circular-progress circle.fg {
                /* Der Fortschritts-Ring gehoert dazu: Er umgibt die Ziffern und waere sonst
                   das einzige weisse Element im Timer. */
                stroke: var(--digit-color, #fff);
                transform: rotate(-90deg);
                transform-origin: 60px 60px;
                /* Umfang = 2·π·54 ≈ 339.292; JS setzt stroke-dashoffset exakt nach Restzeit */
                stroke-dasharray: 339.292;
                stroke-dashoffset: 339.292;
                transition: stroke-dashoffset 0.2s linear;
            }
        }
    }
    .cell {
        .settings {
            .ios-switch {
                position: relative;
                display: inline-block;
                width: 41px;
                height: 25px;
                vertical-align: middle;
                input[type="checkbox"] {
                    opacity: 0;
                    width: 0;
                    height: 0;
                    position: absolute;
                    /* EIN: deutlich in der Akzent-/Glow-Farbe (wie iOS grün) */
                    &:checked + .slider {
                        background-color: var(--glow-color, #4fd4ff);
                        border-color: var(--glow-color, #4fd4ff);
                    }
                    &:focus + .slider { box-shadow: 0 0 1px rgba(255, 255, 255, 0.5); }
                    &:checked + .slider::before { transform: translateX(16px); }
                }
                .slider {
                    position: absolute;
                    cursor: pointer;
                    top: 0;
                    left: 0;
                    right: 0;
                    bottom: 0;
                    /* AUS: sichtbare graue Pille (vorher #000 → verschwamm mit dem Hintergrund) */
                    background-color: rgba(255, 255, 255, 0.18);
                    transition: .2s;
                    border-radius: 31px; /* should be equal to height */
                    border: 1px solid rgba(255, 255, 255, 0.28);
                    &::before {
                        position: absolute;
                        content: "";
                        height: 19px;
                        width: 19px;
                        left: 2px;
                        bottom: 2px;
                        background-color: white;
                        box-shadow: 0px 1px 3px rgba(0,0,0,0.2), 0px 1px 1px rgba(0,0,0,0.14);
                        transition: .2s;
                        border-radius: 50%;
                    }
                }
            }
        }
    }
}

/* ---------------------------------------------------------------------------
   Ergänzungen: Sprachauswahl + Safe-Areas (native App)
   --------------------------------------------------------------------------- */

/* Sprache: Label + Dropdown NEBENEINANDER (spart vertikalen Platz). Voller
   #app-wrapper-Präfix, sonst gewinnt `…settings > * {flex-wrap:wrap}` und der Select
   bricht auf schmalen (iPhone-)Zellen in die nächste Zeile um. Kompakte Maße + Shrink,
   damit Label + Dropdown auch auf einer halben iPhone-Zelle in EINE Zeile passen. */
/* Sprache + Zurücksetzen als unterste Gruppe: Trennlinie ÜBER „language", KEINE unter
   „reset settings" (die wirkte am Zellenboden doppelt mit dem Zellenrahmen). */
#app-wrapper .cell .settings .lang-settings { border-top: 0; border-bottom: 0; }
#app-wrapper .cell .settings .reset { border-bottom: 0; }
/* Sprache ist jetzt ein Button + Modal (wie font/glow/sound): Label links, aktueller
   Sprachname rechts. */
#app-wrapper .cell .settings .lang-open-btn { justify-content: space-between; }
#app-wrapper .cell .settings .lang-open-btn .lang-open-label { opacity: .6; }
/* ⚠️ Bei breiten Display-Schriften lief der Sprach-Knopf ueber (gemessen: Bungee Shade
   178 px Text in 169 px Knopf, ebenso Nosifer, Syncopate, Major Mono Display) — der Text war
   an BEIDEN Seiten abgeschnitten. Der Schrift-Knopf loest das seit jeher mit Auslassungs-
   punkten; hier fehlte es. Dieselbe Behandlung, nicht eine neue Sonderregel.
   `min-width: 0` ist noetig, damit ein Flex-Kind ueberhaupt schrumpfen DARF. */
#app-wrapper .cell .settings .lang-open-btn .lang-open-label,
#app-wrapper .cell .settings .lang-open-btn .lang-open-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
dialog.lang-dialog { width: min(92vw, 300px); max-width: 300px; padding: 16px 16px 16px; text-shadow: none; }
dialog.lang-dialog h2 { margin: 0 0 12px; }
#app-wrapper .cell .settings .lang-dialog .lang-grid { display: flex; flex-direction: column; gap: 8px; }
#app-wrapper .cell .settings .lang-dialog .lang-row {
    all: unset; box-sizing: border-box; cursor: pointer;
    display: flex; align-items: center; width: 100%;
    padding: 12px 14px; border: 1px solid rgba(255,255,255,0.12); border-radius: 12px;
    background: rgba(255,255,255,0.04); font-size: 1rem;
    transition: background-color .15s ease, border-color .15s ease;
}
#app-wrapper .cell .settings .lang-dialog .lang-row:hover { background: rgba(255,255,255,0.10); }
#app-wrapper .cell .settings .lang-dialog .lang-row.active {
    border-color: var(--glow-color, #4fd4ff); background: rgba(255,255,255,0.13);
    box-shadow: 0 0 10px -2px var(--glow-color, #4fd4ff);
}

/* Der Schriftstärke-Regler lebt jetzt IM Schrift-Vorschau-Dialog (#font_weight),
   damit man die Stärke direkt an allen Schriften der Liste sieht (siehe Dialog-CSS). */

/* Synthetische Kontur (Schriftstärke-Regler) NUR ab Stufe > 100 setzen (JS toggelt
   `html.digit-stroked`, wenn fontWeight > 100). GRUND/STOLPERFALLE: `-webkit-text-stroke`
   unterdrückt auf iOS-WebKit das `text-shadow` der Ziffern – SELBST mit 0-Breite. Auf der
   niedrigsten Weight-Stufe (Stroke 0) leuchteten die Ziffern auf dem iPhone deshalb nicht
   mehr (auf Desktop/Blink schon). Also bei Stufe 100 GAR KEINEN Stroke setzen → Glow bleibt.
   Voller `#app-wrapper .cell .digit`-Präfix nötig, sonst gewinnt die Basis-Regel (id). */
html.digit-stroked #app-wrapper .cell .digit {
    -webkit-text-stroke: var(--digit-stroke, 0) currentColor;
    paint-order: stroke fill;
}

/* Schriften mit überstehenden/​tropfenden Glyphen (Horror-Fonts) werden in den
   Ziffern-Kacheln sonst unten abgeschnitten → etwas kleiner skalieren, damit alles passt.
   Voller Präfix nötig, sonst gewinnt die Basis-`#app-wrapper .cell .digit`-Regel.
   Werte je Schrift kalibriert (Audit mit simulierten iPhone-Safe-Area-Insets, kleinere
   Zellen): die Tropfen von Nosifer/Wet Paint reichten bei 21/24svh bis an die Unterkante. */
html.font-eater #app-wrapper .cell .digit { font-size: 21vh; font-size: 21svh; }
html.font-nosifer #app-wrapper .cell .digit { font-size: 20vh; font-size: 20svh; }
html.font-rubik-puddles #app-wrapper .cell .digit { font-size: 24vh; font-size: 24svh; }
html.font-rubik-wet-paint #app-wrapper .cell .digit { font-size: 22vh; font-size: 22svh; }

/* Notch-/Dynamic-Island-/Home-Indicator-Abstände – UNBEDINGT (nicht an body.app-version
   gekoppelt!). `env(safe-area-inset-*)` ist im normalen Browser-Tab 0 (harmlos) und liefert
   nur in der installierten/Standalone-App echte Werte. So wirkt der Abstand ZUVERLÄSSIG,
   selbst wenn die Standalone-Erkennung (display-mode/navigator.standalone) mal nicht greift
   und `app-version` fehlt – genau das war der Fehler beim Homescreen-Lesezeichen.
   Wegen `black-translucent` + `viewport-fit=cover` läge der Inhalt sonst randlos unter
   Statusbar/Dynamic Island. `box-sizing:border-box` kommt global aus `* {}` → das Padding
   wird in die 100svh-Höhe eingerechnet, das Raster schrumpft passend statt überzulaufen.
   Der Hintergrund (Sterne/Gitter, position:fixed) bleibt bewusst randlos = immersiv. */
body #app-wrapper {
    box-sizing: border-box;
    padding-top: env(safe-area-inset-top);
    /* UNTEN bewusst KEIN Inset: das Gitter soll bis an die Bildschirmunterkante reichen.
       Der Home-Indicator ist nur ein dünner, halbtransparenter Overlay-Balken – er liegt
       über dem (leeren, zentrierten) unteren Kachelbereich, verdeckt aber keine Ziffern.
       (Oben ist das Inset Pflicht, weil die Dynamic Island echt verdeckt.) */
    padding-bottom: 0;
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
}

/* ---------------------------------------------------------------------------
   Hintergrund-Effekte: Sternenhimmel (hinter den Ziffern) + Funken (davor)
   --------------------------------------------------------------------------- */
body #app-wrapper { position: relative; z-index: 1; }
#starfield {
    position: fixed; inset: 0; width: 100%; height: 100%;
    z-index: 0; pointer-events: none; display: none;
}
#sparkfield {
    position: fixed; inset: 0; width: 100%; height: 100%;
    z-index: 2; pointer-events: none; display: none;
}

/* ---------------------------------------------------------------------------
   Labels: Animations-Timing
   --------------------------------------------------------------------------- */
/* Uhr -> Timer/Stoppuhr: Labels fahren ERST nach der Ziffern-Animation (1.05s) raus.
   Einheitlicher Wert für ALLE Labels + beide Richtungen (raus wie rein). */
body.timer-mode #app-wrapper .cell .label,
body.stopwatch-mode #app-wrapper .cell .label {
    transition: translate .5s ease-in-out 1.05s;
}
/* "Einstellungen"-Label (Zelle 2): GLEICHER Delay wie die übrigen Labels, sonst
   fährt es beim Zurückkehren zur Uhr früher rein als cell3–6. (opacity für den
   Rückweg aus dem Einstellungen-Modus.) */
#app-wrapper .cell:nth-child(2) .label {
    transition: translate .5s ease-in-out 1.05s, opacity .5s ease-in-out 1.05s;
}

/* ---------------------------------------------------------------------------
   Performance: animierte, leuchtende Elemente auf eigene GPU-Ebene heben.
   So muss der teure Glow-Text-Shadow beim Skalieren (z. B. Einstellungen öffnen)
   NICHT pro Frame neu gerastert werden -> deutlich flüssiger.
   --------------------------------------------------------------------------- */
#app-wrapper .digit,
#app-wrapper .settings,
#app-wrapper .label,
#app-wrapper .label_big,
#app-wrapper .label_medium,
#app-wrapper .label_small {
    will-change: transform;
}

/* ===========================================================================
   Atmosphäre-Effekte (alle in den Einstellungen abschaltbar)
   =========================================================================== */

/* Beide oberen Zellen: Settings als Spalte über die volle Höhe. UNBEDINGT (nicht nur im
   settings-mode!) – sonst „schnappt" das Layout beim Verlassen zusammen, bevor es skaliert
   (holprig). Im clock-mode ist `.settings` scale:0 = unsichtbar, das Layout stört dort nicht. */
#app-wrapper .cell:nth-child(1) .settings,
#app-wrapper .cell:nth-child(2) .settings {
    height: 100%;
    display: flex;
    flex-direction: column;
}
/* cell1: font/glow/sound oben, „reset" ganz UNTEN. */
#app-wrapper .cell:nth-child(1) .settings .reset { margin-top: auto; }
/* cell2: „go crazy" oben, „back to clock" MITTIG, „language" ganz UNTEN (zwei auto-Margins
   → die freie Höhe teilt sich: Lücke über back-to-clock UND über language). */
#app-wrapper .cell:nth-child(2) .settings .settings-back { margin-top: auto; }
#app-wrapper .cell:nth-child(2) .settings .lang-settings { margin-top: auto; }
/* Scrollen nur im aktiven Settings-Modus nötig (beide oberen Zellen). */
body.settings-mode #app-wrapper .cell:nth-child(1) .settings,
body.settings-mode #app-wrapper .cell:nth-child(2) .settings {
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,0.25) transparent;
}
body.settings-mode #app-wrapper .cell .settings::-webkit-scrollbar { width: 4px; }
body.settings-mode #app-wrapper .cell .settings::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.22); border-radius: 4px; }

/* „back to clock"-Label im Panel: groß, zentriert, antippbar (schließt die Einstellungen) */
#app-wrapper .cell:nth-child(2) .settings .settings-back {
    flex: 0 0 auto;
    width: 100%;
    text-align: center;
    justify-content: center;
    font-size: 2rem;
    padding: 14px 0;
    border-bottom: none;
    cursor: pointer;
}
/* „zur Uhr" darf – anders als die übrigen Settings-Labels – leuchten */
body.glow-effect #app-wrapper .cell:nth-child(2) .settings .settings-back {
    text-shadow:
        0 0 calc(var(--glow-intensity) * 1px) #fff,
        0 0 calc(var(--glow-intensity) * 2px) #fff,
        0 0 calc(var(--glow-intensity) * 3px) var(--glow-color, #0073e6),
        0 0 calc(var(--glow-intensity) * 4px) var(--glow-color, #0073e6),
        0 0 calc(var(--glow-intensity) * 5px) var(--glow-color, #0073e6),
        0 0 calc(var(--glow-intensity) * 6px) var(--glow-color, #0073e6),
        0 0 calc(var(--glow-intensity) * 7px) var(--glow-color, #0073e6);
}

/* Farb-Presets – kompakt + umbruchfähig, damit alle 6 auf iPhone-Breite passen */
#app-wrapper .cell .settings .glow-presets {
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: center;
    padding-top: 6px;
    border-bottom: none;
}
#app-wrapper .cell .settings .glow-presets .preset {
    all: unset;
    width: 19px; height: 19px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: var(--c, #fff);
    border: 1px solid rgba(255,255,255,0.25);
    box-shadow: 0 0 8px var(--c, #fff);
    cursor: pointer;
    transition: transform .15s ease;
}
#app-wrapper .cell .settings .glow-presets .preset:hover { transform: scale(1.15); }
/* --- Glow als Button + Modal (statt inline im Panel) --- */
/* Der Glow-Button zeigt den Zustand: leuchtet in der Glow-Farbe, wenn Glow aktiv ist. */
body.glow-effect #app-wrapper .cell .settings .glow-open-btn {
    border-color: var(--glow-color, #4fd4ff);
    box-shadow: 0 0 0 1px var(--glow-color, #4fd4ff), 0 0 12px -3px var(--glow-color, #4fd4ff);
}
dialog.glow-dialog { width: min(92vw, 340px); max-width: 340px; padding: 16px 16px 16px; text-shadow: none; }
dialog.glow-dialog h2 { margin: 0 0 4px; }
/* Die vier Gruppen (an/aus · eigene Farbe · Presets · Intensität) durch Trennstriche
   abgegrenzt – im MODAL gewünscht (im Panel dagegen nicht). */
#app-wrapper .cell .settings .glow-dialog .glow-row,
#app-wrapper .cell .settings .glow-dialog .glow-section {
    margin: 0; padding: 14px 0;
    border-top: 1px solid rgba(255,255,255,0.1);
}
/* erste Inhaltszeile (Glow an/aus) direkt unter h2: kein Strich oben. */
#app-wrapper .cell .settings .glow-dialog h2 + .glow-row { border-top: 0; padding-top: 6px; }
#app-wrapper .cell .settings .glow-dialog .glow-row {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
#app-wrapper .cell .settings .glow-dialog .glow-row-controls { display: flex; align-items: center; gap: 14px; }
#app-wrapper .cell .settings .glow-dialog .glow-section-label {
    display: block; font-size: .78rem; opacity: .6; margin-bottom: 8px; text-align: left;
}
/* Eigener Farb-Mischer: prominenteres Kästchen, damit klar ist „hier mischt man selbst". */
#app-wrapper .cell .settings .glow-dialog .glow-custom input[type="color"] {
    width: 46px; height: 30px; margin: 0;
}
#app-wrapper .cell .settings .glow-dialog .glow-presets { margin: 0; padding-top: 0; justify-content: flex-start; }
#app-wrapper .cell .settings .glow-dialog .glow-intensity { padding-left: 0; justify-content: flex-start; }

/* Sound-Button (wie Glow): zentrierter „sound"-Text, kein Pfeil, leuchtet in der
   Akzentfarbe, wenn der Ton an ist. */
#app-wrapper .cell .settings .sound-open-btn { justify-content: center; }
#app-wrapper .cell .settings .sound-open-btn::after { display: none; }
body.sound-on #app-wrapper .cell .settings .sound-open-btn {
    border-color: var(--glow-color, #4fd4ff);
    box-shadow: 0 0 0 1px var(--glow-color, #4fd4ff), 0 0 12px -3px var(--glow-color, #4fd4ff);
}

/* Bei aktivem Glow leuchtet auch der Timer-Fortschrittsring (der fg-Bogen) leicht in der
   Glow-Farbe – Stärke an `--glow-intensity` gekoppelt wie bei den Ziffern. */
body.glow-effect #app-wrapper .circular-progress circle.fg {
    filter: drop-shadow(0 0 calc(var(--glow-intensity, 5) * 0.7px) var(--glow-color, #4fd4ff))
            drop-shadow(0 0 calc(var(--glow-intensity, 5) * 1.6px) var(--glow-color, #4fd4ff));
}

/* --- Einheitlicher linker Einzug: alle linken Texte (Labels) starten am selben x
   wie die Texte in den Dropdowns/Buttons (~14px). Räumt den „ausgefransten" linken
   Rand auf, damit z. B. „sound"/„Alarm" und „font"/„Roboto Mono" exakt fluchten. --- */
#app-wrapper .cell .settings .setting-item > label[for],
#app-wrapper .cell .settings .start_delay > label,
#app-wrapper .cell .settings .duration > label,
#app-wrapper .cell .settings .timer-repeat > label[for] { padding-left: 14px; }
/* „Wiederholen"-Schalter rechts auf denselben Einzug wie die Zahlenfelder bringen. */
#app-wrapper .cell .settings .timer-repeat { transition: opacity .25s ease; }
#app-wrapper .cell .settings .timer-repeat .ios-switch { margin-right: 14px; }
/* --- „repeat delay": Unteroption von „repeat" ------------------------------------------
   ⚠️ GEMESSEN, NICHT GESCHÄTZT (2026-08-19, iPhone SE): Die Zeile hat 172 px, gebraucht
   hätte sie 204 —
       Label 134 („repeat delay", 16 px)  +  gap 15  +  Schalter 41  +  rechter Einzug 14
   — also brach der Schalter in eine zweite Zeile um und hing links am Rand. ⚠️ Und das
   betrifft NICHT nur das kleinste Gerät: Selbst auf dem 15 Pro Max (Zeile ~199 px) reicht es
   nicht. Eine Media-Query wäre hier also die falsche Lösung gewesen — die Regel gilt
   unbedingt.
   Zwei Stellschrauben, zusammen 41 px: Schrift 16 → 12,5 px (−29) und gap 15 → 8 (−7).
   Ergebnis 168 ≤ 172. Der rechte Einzug von 14 px bleibt, damit der Schalter mit den
   Zahlenfeldern darüber fluchtet (Konvention #6).
   ⚠️ Die kleinere Schrift ist kein Notbehelf, sondern passt zur Sache: „repeat delay" IST
   eine Unteroption von „repeat" und wirkt nur, solange dieses an ist (die Zeile wird sonst
   gedimmt). Die kleinere Schrift zeigt genau diese Rangfolge. */
#app-wrapper .cell .settings .timer-repeat-delay { column-gap: 8px; }
#app-wrapper .cell .settings .timer-repeat-delay > label[for] { font-size: .78rem; }
#app-wrapper .cell .settings .glow-intensity { padding-left: 14px; }
/* Die schwachen Hintergrund-Eck-Labels (timer 30s … / delay-Zellen) bekommen im
   Settings-Modus BEWUSST KEINEN 14px-Einzug mehr. Früher tat das ein
   `padding-left/right:14px` – das wird aber ohne Transition gesetzt und ließ beim
   Eintreten in die Einstellungen ALLE Labels horizontal um 14px springen (#43).
   Die 0.2-Opacity-Labels bleiben jetzt exakt an ihrer Uhr-Position → kein Sprung. */
/* RECHTS analog: die Werte-Zahlen (delay/duration) enden am selben Einzug (~14px)
   wie die rechtsbündigen Eck-Labels und die Dropdown-Pfeile. Breiter, damit die Zahl
   trotz größerem rechten Padding nicht abgeschnitten wird. */
/* ⚠️ ALLE ZAHLENFELDER GLEICH BREIT (Niels 2026-08-19: „Schön wäre es, wenn alle Felder
   gleich lang wären, dann würde es aufgeräumt aussehen."). Vorher war `duration` breiter als
   `delay` — aus einem sachlichen Grund: `duration` geht bis 3600 (vierstellig), `delay` nur
   bis 60 (zweistellig), und ein zu schmales Feld schneidet die Zahl ab (Konvention #6).
   Die Lösung ist nicht, `duration` zu verkleinern, sondern BEIDE auf das Maß zu bringen, das
   die längste Zahl braucht: Das schmalere Feld wird breiter, kein Wert wird abgeschnitten.
   Weil die Zahlen rechtsbündig stehen und der rechte Einzug (14 px) für beide gilt, fluchten
   damit auch die LINKEN Kanten — genau das macht den aufgeräumten Eindruck. */
#app-wrapper .cell .settings input[type="number"] { width: 68px; padding-right: 10px; }
/* ⚠️ SCHMALER (Niels 2026-08-19: „die Inputfelder könnten ein bisschen kürzer"), aber die
   Grenze liegt tiefer, als man denkt — und mein erster Versuch (62 px) hat „3600" SICHTBAR
   ABGESCHNITTEN.
   ⚠️ WARUM DIE RECHNUNG DANEBENLAG: Ich hatte die Textbreite über ein Canvas mit der Schrift
   aus `getComputedStyle` gemessen — die meldet „Arial", gerendert wird aber die Monospace der
   App. Und selbst mit der richtigen Breite (29,7 px) stimmt die Rechnung nicht: Ein
   `input[type=number]` hält INTERN rund 17 px zusätzlich frei, auch mit `appearance: none`.
   Also empirisch gemessen statt gerechnet — Mindestbreite, bei der „3600" noch vollständig
   steht (Desktop-Chromium):
       rechtes Polster 13 → 67 px · 10 → 64 · 8 → 62 · 7 → 61 · 5 → 59
   Gewählt: Polster 10, Breite 68 → 4 px Reserve. Damit ist das Feld 8 px schmaler als vorher
   (76) und die Zahl sitzt 3 px näher an der Pillen-Kante — sichtbar kürzer, ohne Risiko.
   ⚠️ In der MOBILEN Darstellung ist die interne Reserve kleiner: Dort liegt das Minimum bei
   45 px, das Feld der ≤400px-Regel (56 px) hat also 11 px Luft. Nicht weiter verkleinert —
   ein abgeschnittener Wert ist teurer als ein paar Pixel zu viel. */


/* --- Die schmalen Zellen-Zeilen behalten eine neutrale Schrift ---------------------------
   ⚠️ BEFUND Niels 2026-08-19: „Bei manchen Schriftarten ist es so, dass die Einstellungen
   nicht mehr ordentlich angezeigt werden." Alle 36 Schriften am ausgelieferten Stand
   durchgemessen (iPhone SE, die engste Zelle): **7 brachen um** — bungee-shade, nosifer,
   syncopate und major-mono-display bei „duration", „repeat delay" UND den drei
   Funktions-Pillen; eater, faster-one und monoton bei „repeat delay".

   Ursache: Die gewählte Anzeigeschrift sitzt auf `html` und gilt damit für die GANZE App,
   also auch für das Einstellungs-Panel. Display-Schriften sind aber breit gebaut — bei
   Bungee Shade misst „repeat delay" 136 px, wo die Zeile 172 hat und der Schalter noch 41
   davon braucht.

   ⚠️ WARUM NICHT „einfach kleiner": nachgerechnet an der Messung. Damit Bungee Shade passt,
   müsste die Beschriftung auf ~10,5 px herunter — eine schattierte Display-Schrift ist dort
   Matsch. Geometrie allein half auch nur teilweise (7 → 4 Schriften). Erst eine neutrale
   Schrift IN DIESEN ZEILEN bringt alle 36 auf null Umbrüche.

   ⚠️ NUR DIESE ZEILEN, nicht das ganze Panel: Die großen Knöpfe (font/glow/sound, „go crazy",
   „back to clock") behalten die gewählte Schrift — dort ist Platz, und der Charakter der App
   lebt davon. Neutral wird genau das, was in eine halbe Zellbreite passen muss. */
#app-wrapper .cell .settings .function label,
#app-wrapper .cell .settings .start_delay > label,
#app-wrapper .cell .settings .duration > label,
#app-wrapper .cell .settings .timer-repeat > label[for],
#app-wrapper .cell .settings input[type="number"] {
    font-family: 'Roboto Mono', ui-monospace, monospace;
}

/* --- Schmale Geräte: „duration" muss NEBEN dem Feld bleiben ---------------------------
   ⚠️ BEFUND Niels 2026-08-19 (iPhone SE, 375×667): „das inputfeld unter duration und nicht
   daneben". Nachgemessen auf genau diesem Gerät — die Zeile hat 157 px zur Verfügung und
   braucht 171:
       Label 86 (14 px Einzug + 72 px Text „duration")  +  gap 15  +  Feld 70  =  171
   Die Zeile ist `flex-wrap: wrap` (aus `.settings > *`), also bricht sie um. `delay` passt
   mit 59 + 15 + 50 = 124 und war deshalb nie auffällig — der Fehler zeigt sich NUR am
   längeren Wort und NUR auf dem kleinsten Gerät.

   ⚠️ NICHT über `flex-wrap: nowrap` gelöst: Dann schrumpft das Label und „duration" wird
   abgeschnitten — aus einem sichtbaren Umbruch würde ein unsichtbarer Textverlust, und das
   ist schlechter. Stattdessen 19 px eingespart, verteilt auf drei Stellen, die alle drei
   Reserve haben:
       gap 15 → 6 (−9) · rechtes Feld-Padding 13 → 7 (−6) · Label-Einzug 14 → 10 (−4)
   Ergebnis 152 ≤ 157, also 5 px Luft. Das Feld behält seine Breite (76 px), damit die
   vierstellige 3600 weiterhin vollständig dasteht (Konvention #6).

   ⚠️ BEIDE Zeilen, auch `start_delay`: Zwei verschiedene Abstände direkt untereinander in
   derselben Zelle sähen nach Versehen aus. Die Grenze liegt bei 400 px — darüber bleibt
   alles wie bisher. */
@media (max-width: 400px) {
    #app-wrapper .cell .settings .start_delay,
    #app-wrapper .cell .settings .duration { column-gap: 6px; }
    #app-wrapper .cell .settings .start_delay > label,
    #app-wrapper .cell .settings .duration > label { padding-left: 8px; }
    #app-wrapper .cell .settings .start_delay input[type="number"],
    #app-wrapper .cell .settings .duration input[type="number"] { padding-right: 7px; }
    /* ⚠️ NACHGEMESSEN, NICHT GERECHNET: Nach den drei Reduktionen oben brauchte die Zeile
       immer noch 159 px bei 157 verfügbaren — zwei Pixel zu viel, und sie brach weiter um.
       Meine Überschlagsrechnung (152) lag daneben, weil das Label mit 83 px breiter ist als
       gedacht. Also das Feld selbst schmaler: 76 → 66 px. Der Platz ist da — im Feld stehen
       bei 66 px noch 52 px Inhaltsfläche für höchstens vier Ziffern (13,3 px Schrift ≈ 30 px).
       Endstand 149 ≤ 157, also 8 px Luft statt zwei zu wenig. */
    /* Schmaler Bereich: rechtes Polster ist hier nur 7 px (s. o.), also reicht ein
       schmaleres Feld für dieselbe Inhaltsfläche und dieselbe Reserve. */
    /* ⚠️ 56 → 50 px (Niels: kuerzere Eingabefelder). Hier ist Luft: Das gemessene Minimum
       fuer ein vollstaendiges 3600 liegt in der mobilen Darstellung bei 45 px, es bleiben also
       5 px Reserve. Auf breiten Schirmen NICHT verkleinert — dort liegt das Minimum bei 64 px,
       und 68 sind schon knapp. */
    #app-wrapper .cell .settings .start_delay input[type="number"],
    #app-wrapper .cell .settings .duration input[type="number"] { width: 50px; }
}


/* --- Scanlines (CRT) --- */
body.fx-scanlines::after {
    content: '';
    position: fixed; inset: 0;
    pointer-events: none;
    z-index: 6;
    background: repeating-linear-gradient(0deg, rgba(0,0,0,0.30) 0 1px, transparent 1px 3px);
    animation: fx-flicker 4s steps(40) infinite;
}
@keyframes fx-flicker { 0%,100%{opacity:.85} 45%{opacity:.7} 50%{opacity:.97} 55%{opacity:.72} }

/* --- Mini-Vorschauen in den Effekt-„i"-Dialogen (per JS injiziert: .fx-preview.fx-prev-X) --- */
/* Info-„i"-Dialog mit Vorschau offen → das darunterliegende Effekte-Modal (und die Uhr mit
   ihrem 6-Zellen-Raster dahinter) abdunkeln + verwischen, damit NUR die Vorschau im Fokus steht.
   `::backdrop` des verschachtelten Dialogs liegt über dem Effekte-Modal → background dunkelt ab,
   backdrop-filter verwischt. (Ohne Blur-Support bleibt wenigstens die Abdunklung.) */
/* Info-„i"-Dialog NIE breiter als der Viewport: auf schmalen iPhones machte der lange Info-Text den
   fit-content-Dialog breiter als den Bildschirm; ein modaler `<dialog>` rückt dann LINKSBÜNDIG und ragt
   rechts über → die im Canvas mittig (W/2) gezeichnete Road landete im abgeschnittenen rechten Teil
   („Road ganz rechts, nur kleiner Teil sichtbar"). `max-width` hält den Dialog im Viewport → er bleibt
   symmetrisch zentriert → Road wieder mittig & voll sichtbar. (Verifiziert: erzwungener 700px-Dialog bei
   500px-Viewport rückt mit 94vw zurück in die Mitte.) */
#app-wrapper .cell .settings .gocrazy-dialog .setting-item dialog { max-width: 94vw; box-sizing: border-box; }
#app-wrapper .cell .settings .gocrazy-dialog .setting-item dialog::backdrop {
    background: rgba(0, 0, 0, 0.66);
    -webkit-backdrop-filter: blur(7px);
    backdrop-filter: blur(7px);
}

/* ===== Effekt-Vorschauen = kleiner Ausschnitt der ECHTEN Uhr (dünne weiße Ziffern + Effekt) =====
   Gerüst (buildEffectPreviews): `.fp-scene` (Hintergrund-Effekte dahinter) + `.fp-digits` (Uhr-Ziffern
   davor). Die dünnen Ziffern lassen die Hintergrund-Szene im Negativraum durchscheinen – wie in der
   echten Uhr. Morph nutzt stattdessen `.fp-morph-goo` (eigener Block weiter unten). */
#app-wrapper .cell .settings .gocrazy-dialog .fx-preview {
    margin-top: 13px; height: 100px;
    border-radius: 10px; overflow: hidden; position: relative;
    background: #000; border: 1px solid rgba(255,255,255,0.12);
    isolation: isolate;
}
#app-wrapper .cell .settings .gocrazy-dialog .fx-preview .fp-scene {
    position: absolute; inset: 0; z-index: 0; overflow: hidden;
}
#app-wrapper .cell .settings .gocrazy-dialog .fx-preview .fp-digits {
    position: absolute; inset: 0; z-index: 1; pointer-events: none;
    /* Wie die ECHTE Uhr: 6 Ziffern (HH/MM/SS) im 2-Spalten-Raster (→ 3 Reihen), mittig. */
    display: grid; grid-template-columns: repeat(2, auto);
    align-content: center; justify-content: center; column-gap: .26em; row-gap: 0;
    color: #fff; font-family: 'Roboto Mono', monospace; font-weight: 100;
    font-size: 30px; line-height: 1.0; font-variant-numeric: tabular-nums;
}
#app-wrapper .cell .settings .gocrazy-dialog .fx-preview .fp-digits span { display: block; min-width: .62em; text-align: center; }

/* chroma: RGB-Split auf den Ziffern (exakt wie body.fx-chroma) */
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-chroma .fp-digits {
    text-shadow: 3.5px 0 rgba(255,40,70,.92), -3.5px 0 rgba(40,190,255,.92);
}
/* pulse: Ziffern atmen in der Helligkeit (gleiches @keyframes fx-pulse wie echt, 1s) */
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-pulse .fp-digits { animation: fx-pulse 1s ease-in-out infinite; }
/* scanlines: CRT-Linien über allem (0.30-Alpha wie echt) */
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-scanlines::after {
    content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 2;
    background: repeating-linear-gradient(0deg, rgba(0,0,0,0.30) 0 1px, transparent 1px 3px);
    animation: fx-flicker 4s steps(40) infinite;
}
/* flash: ganzer Ausschnitt blitzt kurz weiß (wie body.body-screen-flash) */
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-flash { animation: fx-prev-flash 2.6s ease-in-out infinite; }
@keyframes fx-prev-flash { 0%, 7%, 100% { background: #000; } 3% { background: #fff; } }
/* starfield: weiße Punkte fliegen aus der Mitte auf einen zu (skalieren + heller werden) */
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-stars .fp-scene { background: radial-gradient(120% 120% at 50% 50%, #050508, #000 72%); }
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-stars .fp-scene::before,
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-stars .fp-scene::after {
    content: ''; position: absolute; inset: 0; transform-origin: 50% 50%;
    background-image:
        radial-gradient(1.5px 1.5px at 56% 42%, #fff, transparent),
        radial-gradient(1px 1px at 44% 58%, #fff, transparent),
        radial-gradient(1.5px 1.5px at 63% 55%, #fff, transparent),
        radial-gradient(1px 1px at 38% 46%, #fff, transparent),
        radial-gradient(1px 1px at 52% 36%, #fff, transparent),
        radial-gradient(2px 2px at 47% 64%, #fff, transparent),
        radial-gradient(1px 1px at 67% 49%, #fff, transparent),
        radial-gradient(1.5px 1.5px at 33% 53%, #fff, transparent);
    animation: fx-prev-stars 3.2s linear infinite;
}
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-stars .fp-scene::after { animation-delay: -1.6s; }
@keyframes fx-prev-stars { 0% { transform: scale(.45); opacity: 0; } 16% { opacity: .85; } 84% { opacity: .85; } 100% { transform: scale(3.1); opacity: 0; } }
/* lightning & rainbow: im echten Programm CANVAS-Effekte → die Vorschau rendert einen Mini-Canvas
   mit derselben Logik (attachPreviewCanvas in app.js). Rainbow-Canvas liegt HINTER den Ziffern
   (z 0, über der dunklen Szene), der Blitz-Canvas DARÜBER (z 2), damit die Funken auf der „12"
   knistern – wie in der echten App. */
/* `width/height:100%` ZWINGEND: ein <canvas> ist ein REPLACED element mit intrinsischer Größe
   (= seine width/height-Attribute, die in JS auf W*dpr gesetzt werden). `inset:0`/`left:0;right:0`
   streckt ein replaced element NICHT auf den Container → bei dpr≥2 (echtes iPhone) war der Canvas
   doppelt so breit wie die Box, die mittig gezeichnete Road lief rechts aus dem Bild. `width:100%`
   überschreibt die intrinsische Breite mit der Box-Breite → Anzeige = Box, Road mittig. (Bug fiel im
   Desktop-Test mit dpr=1 NIE auf, weil dort intrinsische Breite == Box-Breite.) */
#app-wrapper .cell .settings .gocrazy-dialog .fp-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-rainbow .fp-canvas { z-index: 0; }
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-lightning .fp-canvas { z-index: 2; }
/* grid: Mini-Synthwave – Sonne am Horizont + scrollender violetter Boden (wie #synthgrid) */
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-grid .fp-scene { perspective: 90px; }
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-grid .fp-scene::before {
    content: ''; position: absolute; left: 50%; bottom: 40%; transform: translateX(-50%);
    width: 38px; height: 38px; border-radius: 50%; z-index: 0;
    background:
        linear-gradient(to bottom, transparent 0 55%, #000 55% 57.5%, transparent 57.5% 64%,
            #000 64% 67%, transparent 67% 74%, #000 74% 78%, transparent 78% 86%, #000 86% 92%, transparent 92% 100%),
        linear-gradient(to bottom, #ffe14d 0%, #ff8a3c 46%, #ff2d80 100%);
    -webkit-mask-image: linear-gradient(to top, transparent 0, #000 34%);
    mask-image: linear-gradient(to top, transparent 0, #000 34%);
    filter: drop-shadow(0 0 7px rgba(255,70,135,.45));
}
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-grid .fp-scene::after {
    content: ''; position: absolute; left: -60%; right: -60%; bottom: -10%; height: 64%; z-index: 1;
    background-image:
        repeating-linear-gradient(to right, rgba(150,90,255,.55) 0 1px, transparent 1px 7%),
        repeating-linear-gradient(to bottom, rgba(150,90,255,.55) 0 1px, transparent 1px 12px);
    transform: rotateX(80deg); transform-origin: bottom center;
    -webkit-mask-image: linear-gradient(to top, #000 25%, transparent 90%);
    mask-image: linear-gradient(to top, #000 25%, transparent 90%);
    animation: fx-prev-grid 1.6s linear infinite;
}
@keyframes fx-prev-grid { from { background-position: 0 0, 0 0; } to { background-position: 0 0, 0 12px; } }
/* rainbow: nur der dunkle Welt-Hintergrund; die perspektivische Fahrbahn rendert der Mini-Canvas
   (fpDrawRoad in app.js, portiert aus rainbow.js). */
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-rainbow { height: 140px; }
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-rainbow .fp-scene { background: radial-gradient(130% 90% at 50% 30%, #100626, #06030f 62%, #000); }
@media (prefers-reduced-motion: reduce) {
    #app-wrapper .cell .settings .gocrazy-dialog .fx-prev-pulse .fp-digits,
    #app-wrapper .cell .settings .gocrazy-dialog .fx-prev-scanlines::after,
    #app-wrapper .cell .settings .gocrazy-dialog .fx-prev-flash,
    #app-wrapper .cell .settings .gocrazy-dialog .fx-prev-stars .fp-scene::before,
    #app-wrapper .cell .settings .gocrazy-dialog .fx-prev-stars .fp-scene::after,
    #app-wrapper .cell .settings .gocrazy-dialog .fx-prev-grid .fp-scene::after,
    #app-wrapper .cell .settings .gocrazy-dialog .fx-prev-morph .fp-morph-a,
    #app-wrapper .cell .settings .gocrazy-dialog .fx-prev-morph .fp-morph-b { animation: none; }
}

/* --- Morph: Ziffern gehen FORMHAFT ineinander über (Goo, codepen.io/Valgo/pen/PowZaNY) ---
   setDigit() (app.js) legt beim Wechsel ZWEI Glyphen-Ebenen (.dm-old/.dm-new) übereinander in
   dieselbe Grid-Area. Beide werden gegenläufig unscharf und – weil die Ziffer währenddessen den
   Alpha-Schwellwert-Filter `#digit-goo` trägt – zusammen geschwellt: die verschwommenen Ränder
   verschmelzen zu Blobs, die neue Ziffer „wächst" aus der alten heraus. */
/* Der Goo-Filter sitzt auf dem WRAPPER (nicht auf der will-change-Ziffer → kein Versatz). */
.digit .dm-wrap { grid-area: 1 / 1; display: grid; position: relative; }
.digit-gooing .dm-wrap { filter: url(#digit-goo); }
/* (Glow + Morph schließen sich aus – s. applyGlobalSettings; daher KEIN drop-shadow-Glow während
   des Morphs nötig. Der Goo-Schwellwert würde weiches Glow ohnehin zerstören.) */
/* KEIN seitlicher Sprung – egal ob linke (odd, rechtsbündig) oder rechte (even, linksbündig)
   Ziffer und egal bei welcher (proportionalen) Schrift:
   Die NEUE Ziffer liegt im Fluss und bestimmt ALLEIN die Wrap-Breite (= exakt die spätere
   Ruhebreite des reinen Glyphen). Die ALTE Ziffer ist absolut positioniert (aus dem Fluss) und
   trägt NICHTS zur Wrap-Breite bei. Dadurch sieht das Grid im Morph dieselbe Box wie danach beim
   `textContent`-Festsetzen → `justify-content`/`justify-items` positionieren beide IDENTISCH.
   (Früher war der Wrap so breit wie max(alt,neu); eine schmalere neue Ziffer saß dann versetzt
   und zuckte beim Festsetzen auf die Ruheposition.) */
.digit .dm-new { grid-area: 1 / 1; }
.digit .dm-old { position: absolute; top: 0; }
/* Alte Ziffer an DERSELBEN Seite ankern wie die ruhende Ziffer (linke/odd rechtsbündig, rechte/
   even linksbündig) → sie überlagert die neue passend, während sie ausblendet. */
.cell:nth-child(odd)  .digit .dm-old { right: 0; }
.cell:nth-child(even) .digit .dm-old { left: 0; }
/* text-shadow NUR während des Goo aus (sonst verfälscht das weiche Glow den Schwellwert); die
   Kontur (-webkit-text-stroke, bei höherer Stärke) BLEIBT – sie ist solide und gehört zum Glyphen
   (sonst springt die Strichdicke beim Festsetzen). */
.digit-gooing .dm-old, .digit-gooing .dm-new { text-shadow: none; }
.digit-gooing .dm-old { animation: dm-out .8s ease forwards; }
.digit-gooing .dm-new { animation: dm-in  .8s ease forwards; }
/* Opacity bleibt in der Mitte hoch (sonst fällt der Blob unter den Schwellwert und „blinkt"):
   eingehender Glyph startet stark verschwommen → scharf; ausgehender umgekehrt. */
@keyframes dm-in {
    0%   { opacity: 0;  filter: blur(.16em); }
    35%  { opacity: .9; filter: blur(.07em); }
    100% { opacity: 1;  filter: blur(0); }
}
@keyframes dm-out {
    0%   { opacity: 1;  filter: blur(0); }
    65%  { opacity: .9; filter: blur(.07em); }
    100% { opacity: 0;  filter: blur(.16em); }
}
/* Mini-Vorschau Morph: „12" löst sich auf, „07" blendet ein (Cross-Dissolve) und zurück. */
/* Morph-Vorschau = der ECHTE Goo-Effekt (wie der Effekt selbst): ein gefilterter Wrapper
   (#digit-goo) mit zwei Ziffern (5 ↔ 2), die gegenläufig verschwimmen → verschmelzen. */
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-morph { background: #000; position: relative; }
/* Die letzte Ziffer im 6-Ziffern-Raster morpht → `.fp-morph-goo` ist eine RASTER-ZELLE (nicht
   mehr absolut über die ganze Box). Glyphen erben die 30px der Vorschau-Ziffern, sind aber FETT
   (sonst verschluckt der Alpha-Schwellwert die kleinen Glyphen). Blur in EM → skaliert mit der
   font-size (px-Blur war für die alte 46px-Demo kalibriert). */
/* `.fp-digits-morph` im Selektor NÖTIG: sonst schlägt die generische `.fp-digits span { display:block }`
   (Spezifität 1,5,1 durch das `span`-Element) dieses `display:grid` (wäre nur 1,5,0) → die zwei Glyphen
   stapeln untereinander (Zelle doppelt hoch) statt zu überlagern/morphen. Mit `.fp-digits-morph` = 1,6,0. */
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-morph .fp-digits-morph .fp-morph-goo {
    position: relative; display: grid; place-items: center;
    min-width: .62em; filter: url(#digit-goo);
}
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-morph .fp-morph-a,
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-morph .fp-morph-b {
    grid-area: 1 / 1;
    font-weight: 900; line-height: 1; color: #fff;
    font-variant-numeric: tabular-nums; text-shadow: none; -webkit-text-stroke: 0;
}
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-morph .fp-morph-a { animation: fp-morph-a 2.6s ease-in-out infinite; }
#app-wrapper .cell .settings .gocrazy-dialog .fx-prev-morph .fp-morph-b { animation: fp-morph-b 2.6s ease-in-out infinite; }
/* Opacity bleibt während des Übergangs HOCH (sonst fällt der verschwommene Glyph unter den
   Schwellwert und verschwindet → leere Box). Der Blur unterscheidet, wer „dominiert". Blur in EM. */
@keyframes fp-morph-a {
    0%, 8%    { opacity: 1;   filter: blur(0); }
    30%       { opacity: .85; filter: blur(.13em); }
    50%       { opacity: 0;   filter: blur(.2em); }
    70%       { opacity: .85; filter: blur(.13em); }
    92%, 100% { opacity: 1;   filter: blur(0); }
}
@keyframes fp-morph-b {
    0%, 8%    { opacity: 0;   filter: blur(.2em); }
    30%       { opacity: .85; filter: blur(.13em); }
    50%       { opacity: 1;   filter: blur(0); }
    70%       { opacity: .85; filter: blur(.13em); }
    92%, 100% { opacity: 0;   filter: blur(.2em); }
}

/* --- Chromatische Aberration --- */
/* Chroma = RGB-Split (rot nach rechts, cyan nach links). Mittelwert: 1.6px waren fast
   unsichtbar, 6px zu kräftig → 3.5px Versatz. */
body.fx-chroma { text-shadow: 3.5px 0 rgba(255,40,70,.9), -3.5px 0 rgba(40,190,255,.9); }
body.glow-effect.fx-chroma {
    text-shadow:
        0 0 calc(var(--glow-intensity) * 1px) #fff,
        0 0 calc(var(--glow-intensity) * 3px) var(--glow-color, #0073e6),
        0 0 calc(var(--glow-intensity) * 6px) var(--glow-color, #0073e6),
        3.5px 0 rgba(255,40,70,.9),
        -3.5px 0 rgba(40,190,255,.9);
}

/* --- Sekundenpuls (günstig über Opacity) --- */
/* Periode genau 1s + Phase an die Uhr gekoppelt (`--pulse-delay` = -ms-in-die-Sekunde,
   in app.js gesetzt) → der Puls driftet NICHT mehr gegen die Sekunden, sondern atmet
   gleichmäßig zwischen den Ticks (Tiefpunkt auf dem Sekundenwechsel, Hochpunkt mittig). */
body.fx-pulse .digit { animation: fx-pulse 1s ease-in-out infinite; animation-delay: var(--pulse-delay, 0s); }
/* Puls = NUR Helligkeit (Opacity), KEINE Größenänderung (auf Wunsch). Die Ziffern bleiben
   gleich groß und atmen nur in der Helligkeit. */
@keyframes fx-pulse {
    0%, 100% { opacity: .68; }
    50%      { opacity: 1; }
}

/* --- Synthwave-Gitter --- */
#synthgrid { display: none; }
/* In den EINSTELLUNGEN das Gitter stark abdunkeln + leicht blurren, damit die helle Sonne
   die Einstellungen nicht überstrahlt/unleserlich macht. */
body.settings-mode #synthgrid { opacity: 0.16; filter: blur(2px); transition: opacity .4s ease, filter .4s ease; }
body.fx-grid #synthgrid {
    display: block;
    position: fixed; inset: 0;
    z-index: 0; pointer-events: none; overflow: hidden;
    perspective: 260px;
}
body.fx-grid #synthgrid::before { /* Synthwave-Sonne – wandert JS-gesteuert (--sun-y) hoch */
    content: '';
    /* --sun-y 0..1: 0 = am HORIZONT (Grid-Ende, ~30% v. unten, halb hinter dem Gitter),
       1 = ganz oben aus dem Bild (Loop in grid.js). Geht also wie eine Sonne am Horizont auf,
       NICHT am unteren Bildschirmrand. */
    position: absolute; left: 50%;
    bottom: calc(30% + var(--sun-y, 0.1) * 70% + var(--sun-y, 0.1) * 32vmin);
    width: 32vmin; height: 32vmin; transform: translateX(-50%);
    border-radius: 50%;
    /* unten transparent auslaufen → „geht mit transparentem Verlauf auf" / taucht aus dem
       Horizont auf, statt als harte Scheibe von unten zu kommen. */
    -webkit-mask-image: linear-gradient(to top, transparent 0, #000 34%);
    mask-image: linear-gradient(to top, transparent 0, #000 34%);
    /* obere Ebene: schwarze Querstreifen (= Lücken) nur in der unteren Hälfte;
       untere Ebene: Farbverlauf der Sonne */
    background:
        linear-gradient(to bottom,
            transparent 0 55%,
            #000 55% 57.5%, transparent 57.5% 64%,
            #000 64% 67%,   transparent 67% 74%,
            #000 74% 78%,   transparent 78% 86%,
            #000 86% 92%,   transparent 92% 100%),
        linear-gradient(to bottom, #ffe14d 0%, #ff8a3c 46%, #ff2d80 100%);
    filter: drop-shadow(0 0 16px rgba(255,70,135,.45));
    opacity: var(--sun-opacity, .8); /* von grid.js: weiches Auf-/Untergehen, kein Wrap-Blinken */
}
body.fx-grid #synthgrid::after { /* Boden-Gitter */
    content: '';
    position: absolute; left: -60%; right: -60%; bottom: -2%; height: 62%;
    background-image:
        repeating-linear-gradient(to right, rgba(150,90,255,.55) 0 2px, transparent 2px 7%),
        repeating-linear-gradient(to bottom, rgba(150,90,255,.55) 0 2px, transparent 2px 44px);
    transform: rotateX(80deg);
    transform-origin: bottom center;
    /* Bewegung läuft NICHT mehr per CSS-Animation, sondern JS-gesteuert über --grid-pos
       (grid.js) – damit sie wie der Starfield boosten und sanft anhalten kann. */
    background-position: 0 0, 0 var(--grid-pos, 0px);
    -webkit-mask-image: linear-gradient(to top, #000 25%, transparent 90%);
    mask-image: linear-gradient(to top, #000 25%, transparent 90%);
}

/* --- Rainbow Road (Mario Kart) – jetzt CANVAS (rainbow.js), nicht mehr CSS ---
   Die Fahrbahn wird komplett im Canvas `#rainbowroad` gezeichnet (Pseudo-3D, perspektivisch),
   das rainbow.js per JS anlegt – analog zum Starfield. Das ist deutlich performanter als die
   frühere CSS-Lösung (animiertes background-position + drop-shadow-Filter + mask-composite, die
   pro Frame ein Full-Repaint erzwang). Hier nur noch Positionierung/Sichtbarkeit + Settings-Dim. */
#rainbowroad {
    display: none;
    position: fixed; inset: 0;
    z-index: 0; pointer-events: none;
}
/* In den Einstellungen abdunkeln, damit die kräftigen Farben die UI nicht überstrahlen. */
body.settings-mode #rainbowroad { opacity: 0.2; transition: opacity .4s ease; }

/* ===== Premium-Gating (kostenlose Variante) – KEINE Schlösser; Buttons öffnen das Modal, INHALT
   im Modal ist gesperrt (abgedunkelt + Hinweis) =====
   Greift nur in `body.premium-locked` (= edition 'free' ohne In-App-Kauf). Zwei Marker:
   - `[data-premium]` = einzelne gesperrte Sektion (z. B. Glow-Farbe/Presets/Stärke; Glow-an/aus
     bleibt frei).
   - `dialog.premium-gated` = GANZES Modal gesperrt (Schrift/Sound/Effekte) – alles außer
     Schließen-Button (×), Titel (h2) und Hinweis (.premium-hint) wird abgedunkelt + inert. */
body.premium-locked #app-wrapper .cell .settings [data-premium],
body.premium-locked #app-wrapper .cell .settings dialog.premium-gated > *:not(button):not(h2):not(.premium-hint) {
    opacity: .4;
    pointer-events: none;   /* nicht bedienbar – sichtbar als „Köder", aber gesperrt */
}
/* Hinweis nur in der gesperrten Free-Version sichtbar (sonst weg). */
.premium-hint { display: none; }
body.premium-locked #app-wrapper .cell .settings .premium-hint {
    display: block;
    margin: 2px 0 12px;
    padding: 9px 14px;
    border: 0;
    border-radius: 12px;
    /* Auffällig: animierter Regenbogen-Verlauf (zyklisch, Start==Ende → nahtlose Schleife),
       damit der „Vollversion"-Hinweis ins Auge springt. (Kein Pulsieren – auf Wunsch.) */
    background: linear-gradient(90deg, #ff3df2, #ff8a1e, #ffe000, #39e25b, #00c2ff, #3b63ff, #ff3df2);
    background-size: 220% 100%;
    color: #fff;
    font-size: .86rem;
    font-weight: 700;
    letter-spacing: .2px;
    text-align: center;
    text-shadow: 0 1px 2px rgba(0,0,0,0.5);
    box-shadow: 0 0 14px rgba(255,90,230,0.45);
    animation: premium-hint-shimmer 5s linear infinite;
}
@keyframes premium-hint-shimmer { to { background-position: 220% 0; } }
@media (prefers-reduced-motion: reduce) {
    body.premium-locked #app-wrapper .cell .settings .premium-hint { animation: none; }
}
/* In der gesperrten Gratis-Version ist „Zurücksetzen" überflüssig (man kann nur Glow an/aus)
   → ausblenden. Nach dem Kauf (premium-unlocked) erscheint er wieder. */
body.premium-locked #app-wrapper .cell .settings .reset { display: none; }
/* Effekte-Modal in Free: die „i"-Infos + Mini-Vorschauen bleiben ZUGÄNGLICH (man soll sehen,
   was die Effekte tun) – daher KEIN `premium-gated` aufs ganze gc-grid, sondern nur die
   Effekt-SCHALTER deaktivieren (gedimmt + nicht bedienbar). */
/* ⚠️ FLASH IST AUSGENOMMEN (seit 2026-08-19): Er ist auch in der Gratis-Ausgabe nutzbar.
   Ohne dieses `:not()` wäre sein Schalter gedimmt und tot — obwohl das Gate ihn erlaubt und
   die Checkbox gar nicht `disabled` ist. Genau die Sorte Widerspruch, bei der man lange sucht:
   Der Wert ändert sich im Speicher, aber der Finger kommt nicht durch. */
body.premium-locked #app-wrapper .cell .settings .gocrazy-dialog .gc-grid .setting-item:not(.flash-settings) .ios-switch {
    pointer-events: none;
    opacity: .4;
}

/* --- Einheitliche Settings-Buttons (Schrift, go crazy, Zurücksetzen) ---
   gleiche Geometrie: min-height, Padding, Font-Size, Border-Radius, Border. */
#app-wrapper .cell .settings .settings-btn {
    all: unset;
    box-sizing: border-box;
    cursor: pointer;
    width: 100%;
    height: auto;
    min-height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 6px 14px;
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: 31px;
    background: #000;
    color: #fff;
    font-size: .9rem;
    line-height: 1.1;
    text-align: center;
    text-shadow: none;
    transition: background-color .15s ease, filter .15s ease;
}
#app-wrapper .cell .settings .settings-btn:hover { background: rgba(255,255,255,0.08); }

/* Schrift-Button: Name links, Auswahl-Pfeil rechts (gleiche Geometrie wie .settings-btn).
   (Sound ist KEIN Namens-Button mehr, sondern zentriert wie Glow – s. weiter oben.) */
#app-wrapper .cell .settings .font-open-btn { justify-content: space-between; }
#app-wrapper .cell .settings .font-open-btn .font-open-name {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#app-wrapper .cell .settings .font-open-btn::after {
    content: '';
    flex: 0 0 auto;
    width: 0; height: 0;
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-top: 6px solid #888;
}

/* Schrift-Button als GROSSER, zentrierter Button mit zwei Zeilen:
   „font:" (klein, gedimmt) über dem Namen der aktuell gewählten Schrift. */
#app-wrapper .cell .settings .font-open-btn {
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 2px;
    min-height: 54px;
    padding: 9px 14px;
    text-align: center;
}
#app-wrapper .cell .settings .font-open-btn::after { display: none; } /* kein Pfeil – zentriert */
/* cell1 minimal kompakter: font/glow/sound + reset müssen auch auf dem kleinsten iPhone (SE)
   in eine 1/3-Höhe-Zelle passen (ohne Scrollen), da die oberen Zellen sich nicht mehr
   vergrößern. Nur cell1 – die anderen Zellen haben genug Platz. */
#app-wrapper .cell:nth-child(1) .settings > * { padding-top: 5px; padding-bottom: 5px; }
#app-wrapper .cell .settings .font-open-btn .font-open-label {
    font-size: .8rem;
    opacity: .6;
}
#app-wrapper .cell .settings .font-open-btn .font-open-label::after { content: ':'; }
#app-wrapper .cell .settings .font-open-btn .font-open-name {
    font-size: 1.15rem;
    line-height: 1.15;
    max-width: 100%;
}

/* „go crazy"-Button: gleiche Geometrie, aber animierter Regenbogen-Verlauf als CTA.
   Verlauf ist ZYKLISCH (Start == Ende = #ff3df2) + background-origin:border-box,
   damit er randlos füllt (kein cyan/magenta-Strich am 1px-Rand) und nahtlos fließt. */
#app-wrapper .cell .settings .gocrazy-btn {
    background-image: linear-gradient(90deg, #ff3df2, #b76bff, #4fd4ff, #2bff88, #ffb000, #ff3df2);
    background-size: 200% 100%;
    background-origin: border-box;
    background-clip: border-box;
    border-color: transparent;
    color: #fff;
    font-weight: 700;
    letter-spacing: .03em;
    animation: gocrazy-flow 4s linear infinite;
}
#app-wrapper .cell .settings .gocrazy-btn:hover { filter: brightness(1.12); }
@keyframes gocrazy-flow {
    from { background-position: 0 0; }
    to   { background-position: 200% 0; }
}
@media (prefers-reduced-motion: reduce) {
    #app-wrapper .cell .settings .gocrazy-btn { animation: none; }
}

/* „go crazy"-Dialog: kompaktes Raster der Effekt-Schalter */
dialog.gocrazy-dialog { width: min(92vw, 360px); max-width: 360px; padding: 16px 16px 14px; text-shadow: none; }
dialog.gocrazy-dialog h2 { margin: 0 0 8px; }
#app-wrapper .cell .settings .gocrazy-dialog .gc-grid { display: flex; flex-direction: column; }
#app-wrapper .cell .settings .gocrazy-dialog .gc-grid .setting-item {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    column-gap: 4px;
    padding: 8px 2px;
    border-bottom: 1px solid rgba(255,255,255,0.08);
}
/* Effektname + „i" links zusammen, der Schalter ganz rechts. Die Effektname-Labels
   bekommen eine FESTE Breite (≥ längstes Label „starfield"/„lightning"/„scanlines" ≈ 6.3em)
   → die Info-„i" fluchten alle in EINER Spalte untereinander. */
#app-wrapper .cell .settings .gocrazy-dialog .gc-grid .setting-item > label[for] { flex: 0 0 9em; }
#app-wrapper .cell .settings .gocrazy-dialog .gc-grid .setting-item .ios-switch { margin-left: auto; }
#app-wrapper .cell .settings .gocrazy-dialog .gc-grid .setting-item:last-child { border-bottom: none; }

/* --- Vorschau-Dialoge (Schrift + Sound) – gemeinsame Basis, schlanke Varianten --- */
dialog.font-preview-dialog,
dialog.sound-preview-dialog {
    max-height: 82vh;
    padding: 18px 16px 16px;
    text-shadow: none;            /* Glow soll die Vorschau nicht überstrahlen */
}
dialog.font-preview-dialog  { width: min(92vw, 460px); max-width: 460px; }
dialog.sound-preview-dialog { width: min(92vw, 360px); max-width: 360px; }
/* Abstand über der Sound-Liste, damit der erste Sound nicht am Hinweistext klebt. */
.sound-preview-dialog .sp-grid { margin-top: 10px; }
/* Schrift-Dialog: Flex-Spalte, damit Kopf + Weight-Regler oben fix bleiben und die
   Schriftliste den Rest füllt. Unten KEIN Padding → die Liste läuft bis an die Kante,
   man sieht, dass sie weitergeht (scrollbar).
   WICHTIG: `display:flex` NUR im geöffneten Zustand (`[open]`), sonst überschreibt es
   die UA-Regel `dialog:not([open]){display:none}` → der Dialog-Inhalt würde sonst
   IMMER im Settings-Panel angezeigt (geschlossener Dialog wäre sichtbar). */
dialog.font-preview-dialog { flex-direction: column; padding-bottom: 0; }
dialog.font-preview-dialog[open] { display: flex; }

/* Weight-Regler im Dialog: Label links (mit 14px-Einzug), Slider füllt den Rest. */
#app-wrapper .cell .settings .font-preview-dialog .fp-weight {
    display: flex;
    align-items: center;
    gap: 12px;
    flex: 0 0 auto;
    margin: 0 0 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid rgba(255,255,255,0.10);
}
#app-wrapper .cell .settings .font-preview-dialog .fp-weight label {
    flex: 0 0 auto;
    padding-left: 14px;
    font-size: .9rem;
    opacity: .85;
}
#app-wrapper .cell .settings .font-preview-dialog .fp-weight input[type="range"] {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
}

/* Touch-freundliche Schieberegler (Font-Weight im Dialog + Glow-Intensität): großer
   runder Daumen + sichtbare Schiene, damit man sie gut mit dem Finger trifft. */
#app-wrapper .cell .settings input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    height: 30px;            /* großzügige Touch-Höhe */
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    cursor: pointer;
}
#app-wrapper .cell .settings input[type="range"]::-webkit-slider-runnable-track {
    height: 5px; border-radius: 3px; background: rgba(255,255,255,0.22);
}
#app-wrapper .cell .settings input[type="range"]::-moz-range-track {
    height: 5px; border-radius: 3px; background: rgba(255,255,255,0.22);
}
#app-wrapper .cell .settings input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 26px; height: 26px;
    margin-top: -10.5px;     /* mittig auf der 5px-Schiene */
    border-radius: 50%;
    background: #fff;
    border: none;
    box-shadow: 0 0 4px rgba(0,0,0,0.5);
}
#app-wrapper .cell .settings input[type="range"]::-moz-range-thumb {
    width: 26px; height: 26px;
    border-radius: 50%;
    background: #fff;
    border: none;
}
dialog.font-preview-dialog h2,
dialog.sound-preview-dialog h2 { margin: 0 4px 6px 0; }
dialog.font-preview-dialog p,
dialog.sound-preview-dialog p { margin: 0 0 12px; font-size: .8rem; opacity: .7; }

.font-preview-dialog .fp-grid,
.sound-preview-dialog .sp-grid {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 64vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 4px;
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,0.25) transparent;
}
/* Schrift-Liste füllt die Resthöhe des Flex-Dialogs (statt fester max-height) und
   scrollt bis an die Unterkante – kein Padding darunter, damit man sieht: es geht weiter. */
.font-preview-dialog .fp-grid { flex: 1 1 auto; min-height: 0; max-height: none; padding-bottom: 0; }

.font-preview-dialog .fp-grid::-webkit-scrollbar,
.sound-preview-dialog .sp-grid::-webkit-scrollbar { width: 4px; }
.font-preview-dialog .fp-grid::-webkit-scrollbar-thumb,
.sound-preview-dialog .sp-grid::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.22); border-radius: 4px; }

/* Zeilen – gemeinsame Basis (voller Präfix schlägt die generische .settings button-Regel) */
#app-wrapper .cell .settings .font-preview-dialog .fp-row,
#app-wrapper .cell .settings .sound-preview-dialog .sp-row {
    all: unset;
    box-sizing: border-box;
    cursor: pointer;
    display: flex;
    align-items: center;
    width: 100%;
    height: auto;
    border: 1px solid rgba(255,255,255,0.12);
    border-radius: 12px;
    background: rgba(255,255,255,0.04);
    transition: background-color .15s ease, border-color .15s ease;
}
#app-wrapper .cell .settings .font-preview-dialog .fp-row:hover,
#app-wrapper .cell .settings .sound-preview-dialog .sp-row:hover { background: rgba(255,255,255,0.10); }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.active,
#app-wrapper .cell .settings .sound-preview-dialog .sp-row.active {
    border-color: var(--glow-color, #4fd4ff);
    background: rgba(255,255,255,0.13);
    box-shadow: 0 0 10px -2px var(--glow-color, #4fd4ff);
}
/* Zeilen-Varianten */
#app-wrapper .cell .settings .font-preview-dialog .fp-row  { flex-direction: column; gap: 2px; padding: 9px 13px; }
#app-wrapper .cell .settings .sound-preview-dialog .sp-row { justify-content: space-between; gap: 10px; padding: 11px 14px; font-size: 1rem; }

/* Sound-spezifisch */
.sound-preview-dialog .sp-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sound-preview-dialog .sp-play { flex: 0 0 auto; opacity: .6; font-size: .85rem; }
#app-wrapper .cell .settings .sound-preview-dialog .sp-row.active .sp-play { opacity: 1; color: var(--glow-color, #4fd4ff); }

/* Stumm-Schalter-Hinweis im Sound-Modal: NUR auf iPhone/iPad (body.ios-device), weil dort
   der Ton vom physischen Klingel-/Stumm-Schalter (oben links) abhängt. Der ↖-Pfeil „nudgt"
   dezent nach oben-links in Richtung dieses Schalters. */
.sound-preview-dialog .mute-hint { display: none; }
/* Voller `#app-wrapper`-Präfix NÖTIG: sonst überschreibt die generische `…settings dialog p
   { margin: 0 }`-Regel (id-Spezifität) den margin → der Hinweis klebte ohne Abstand an Nachbarn. */
body.ios-device #app-wrapper .cell .settings .sound-preview-dialog .mute-hint {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 16px 0 20px;
    padding: 13px 14px;
    border: 1px solid rgba(255, 200, 80, 0.4);
    border-radius: 12px;
    background: rgba(255, 200, 80, 0.08);
    font-size: .82rem;
    line-height: 1.45;
    text-align: left;
}
/* Ton an/aus-Zeile im Sound-Modal: gleiche Zeilen-Optik wie im Glow-Modal + Luft drumherum. */
#app-wrapper .cell .settings .sound-preview-dialog .glow-row {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    margin: 10px 0 16px;
}
.sound-preview-dialog .mute-hint .mute-arrow {
    flex: 0 0 auto;
    font-size: 1.6rem;
    line-height: 1;
    color: rgba(255, 200, 80, 0.95);
    animation: mute-nudge 1.4s ease-in-out infinite;
}
@keyframes mute-nudge { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(-3px, -3px); } }
@media (prefers-reduced-motion: reduce) { .sound-preview-dialog .mute-hint .mute-arrow { animation: none; } }

/* Schrift-spezifisch */
.font-preview-dialog .fp-name { font-size: 1.05rem; line-height: 1.15; opacity: .8; text-align: center; }
/* Ziffern wie in der App: 2 nebeneinander, 3 untereinander (HH / MM / SS), zentriert */
.font-preview-dialog .fp-sample {
    display: grid;
    grid-template-columns: repeat(2, auto);
    justify-content: center;
    column-gap: .4em;
    row-gap: 0;
    margin-top: 6px;
    /* ⚠️ GRÖSSER (Niels 2026-08-19: „Das Vorschaubild der einzelnen Schriften in der
       Schriftauswahl soll ein bisschen größer sein.") — 1.75rem → 2.1rem, also rund ein
       Fünftel. Bewusst nicht mehr: Die Vorschau ist eine LISTE mit einer Zeile je Schrift;
       jede Vergrößerung verlängert sie und man scrollt mehr. Bei einem Fünftel bleibt der
       Zuwachs je Zeile klein genug, dass auf dem iPhone SE noch dieselbe Zahl an Schriften
       ins Bild passt — nachgemessen. */
    font-size: 2.1rem;
    line-height: 1.05;
    text-align: center;
}
/* Vorschau-Ziffern spiegeln die LIVE-Stärke (--digit-weight/--digit-stroke, von
   applyGlobalSettings auf :root gesetzt) → der Weight-Regler im Dialog wirkt sofort
   sichtbar an jeder Schrift, genau wie an den echten Ziffern. */
.font-preview-dialog .fp-d {
    min-width: .62em;
    font-weight: var(--digit-weight, 100);
    color: var(--digit-color, #fff);          /* spiegelt die gewählte Schriftfarbe */
    -webkit-text-stroke: var(--digit-stroke, 0) currentColor;
    text-stroke: var(--digit-stroke, 0) currentColor;
    paint-order: stroke fill;
}
/* Schriftfarbe-Zeile im Schrift-Dialog (Label + Farb-Picker, wie .fp-weight) */
#app-wrapper .cell .settings .font-preview-dialog .fp-color {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 4px 0 8px;
}
#app-wrapper .cell .settings .font-preview-dialog .fp-color label { color: #fff; font-size: .9rem; }
#app-wrapper .cell .settings .font-preview-dialog .fp-color input[type="color"] {
    width: 46px; height: 28px; padding: 0; border: 1px solid rgba(255,255,255,0.25);
    border-radius: 8px; background: none; cursor: pointer;
}

/* Pro Schrift die Schriftart auf die Vorschau-Zeile legen (spiegelt den html.font-* Block) */
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-codystar { font-family: 'Codystar', system-ui; font-weight: 900; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-dotgothic16 { font-family: 'DotGothic16', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-inter { font-family: 'Inter', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-libre-baskerville { font-family: 'Libre Baskerville', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-marcellus { font-family: 'Marcellus', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-open-sans { font-family: 'Open Sans', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-orbitron { font-family: 'Orbitron', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-raleway-dots { font-family: 'Raleway Dots', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-roboto-mono { font-family: 'Roboto Mono', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-silkscreen { font-family: 'Silkscreen', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-teko { font-family: 'Teko', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-wdxl-lubrifont-tc { font-family: 'WDXL Lubrifont TC', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-audiowide { font-family: 'Audiowide', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-michroma { font-family: 'Michroma', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-major-mono-display { font-family: 'Major Mono Display', monospace; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-vt323 { font-family: 'VT323', monospace; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-share-tech-mono { font-family: 'Share Tech Mono', monospace; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-tektur { font-family: 'Tektur', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-monoton { font-family: 'Monoton', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-wallpoet { font-family: 'Wallpoet', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-syncopate { font-family: 'Syncopate', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-turret-road { font-family: 'Turret Road', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-zen-dots { font-family: 'Zen Dots', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-quantico { font-family: 'Quantico', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-rubik-glitch { font-family: 'Rubik Glitch', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-rubik-wet-paint { font-family: 'Rubik Wet Paint', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-rubik-iso { font-family: 'Rubik Iso', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-rubik-maze { font-family: 'Rubik Maze', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-rubik-burned { font-family: 'Rubik Burned', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-rubik-puddles { font-family: 'Rubik Puddles', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-rubik-vinyl { font-family: 'Rubik Vinyl', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-faster-one { font-family: 'Faster One', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-bungee-shade { font-family: 'Bungee Shade', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-nosifer { font-family: 'Nosifer', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-eater { font-family: 'Eater', system-ui; }
#app-wrapper .cell .settings .font-preview-dialog .fp-row.font-rubik-marker-hatch { font-family: 'Rubik Marker Hatch', system-ui; }
