/* ============================================================
   DullDayDudes Blog Preview — Standalone CSS
   OKLCH tokens mapped from original HSL palette
   ============================================================ */

/* Reihenfolge der Layer. Die ersten drei gehoeren dem Tailwind-Bundle des
   Backstage (dist/assets/backstage-*.css), das default.phtml auf JEDER Seite
   einbindet -- im Frontend wird es fuer die React-Insel SettingsModal gebraucht.
   Ohne diese Zeile sind properties/theme/base hier unbekannt und werden vom
   Browser ans ENDE der Reihenfolge gehaengt: Tailwinds Preflight
   (`*,::after,::before,::backdrop { margin: 0 }`) schlug dann trotz
   schwaechster Spezifitaet die komplette layout-Schicht, u.a. den seitlichen
   Rand von .mid-section. Hier deklariert, greifen sie vor den eigenen Layern. */
@layer properties, theme, base, reset, tokens, layout, components, utilities;

/* Recursive Variable Font -- Achsen: wght 300-1000, CASL 0-1, MONO 0-1,
   slnt -15..0, CRSV 0-1. Alle fuenf werden in dieser Datei aktiv gesteuert
   (font-variation-settings, u.a. Zeile 313, 340, 531, 701).

   Bis 07.08.2026 kam die Schrift von fonts.googleapis.com -- und damit ging die
   IP jedes Besuchers ohne Einwilligung an Google. Die identische Datei liegt in
   allen Schwesterprojekten lokal; ddd-web war der einzige Ausreisser.

   @font-face gehoert ausserhalb der @layer-Bloecke: die CSS-Spezifikation
   erlaubt sie dort nicht darin. Vorbild:
   in-hochfeld/assets/css/layers/typography.css:42-48 */
@font-face {
    font-family: "Recursive";
    src: url("/fonts/recursive_latin_basic.woff2") format("woff2");
    font-weight: 300 1000;
    font-style: oblique -15deg 0deg;
    font-display: swap;
}

/* --- Reset --- */
@layer reset {
    *, *::before, *::after {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
    }
    html { scroll-behavior: smooth; }
    body {
        overflow-x: hidden;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }
    a { text-decoration: none; color: inherit; }
    ul, ol { list-style: none; }
    img { max-width: 100%; display: block; }
    input[type="radio"], input[type="checkbox"] { appearance: none; position: absolute; opacity: 0; pointer-events: none; }
}

/* --- Tokens --- */
@layer tokens {
    :root {
        /* Semantic colors — Dark theme (default) */
        --base-color: oklch(25% 0.02 250);
        --contrast-color: oklch(90% 0.02 90);
        --abyss-color: oklch(15% 0.02 250);
        --burst-color: oklch(78% 0.18 60);
        --accent-color: oklch(60% 0.12 170);
        --spotlight-color: oklch(78% 0.08 230);
        --seam-color: oklch(40% 0.02 250);
        --tint-color: oklch(30% 0.02 250);
        /* Aufgesetzte Flaeche (Einstellungen-Fenster). Bis 07.08.2026 stand
           dort `var(--card, oklch(22% 0.015 260))` -- und --card ist im
           ausgelieferten CSS NIRGENDS definiert, es ist eine Backstage-
           Variable. Es griff also immer der Ersatzwert, und der ist dunkel.
           Im Dunkel-Modus faellt das nicht auf; im Hell-Modus stand die
           Schrift dunkel auf dunkel. Der Dunkel-Wert ist bewusst identisch
           zum alten Ersatzwert, damit sich hier nichts aendert. */
        --surface-raised: oklch(22% 0.015 260);
        --surface-border: oklch(40% 0.01 250);
        /* Schalter im Einstellungen-Fenster. Standen bis 07.08.2026 auf
           `var(--muted, …)` bzw. `var(--background, …)` -- beide nirgends
           definiert, also immer die dunklen Ersatzwerte. Diese hier sind mit
           ihnen identisch, der Dunkel-Modus bleibt unveraendert. */
        --switch-track: oklch(40% 0.01 250);
        --switch-thumb: oklch(15% 0 0);
        /* Fuehrungslinie zwischen Raute und Name in der Leiste. Sie lag auf
           --seam-color und kam damit auf 1.75:1 gegen den Leistengrund -- im
           HELLEN auf 1.61, also praktisch gleich blass. Design-Runde 3 nannte
           sie im Dunkeln "ein schoenes Detail" und nur im Hellen unsichtbar;
           gemessen war sie in beiden Themen kaum da. Deshalb ein eigener Wert
           statt einer Hell-Korrektur. */
        --leader-color: oklch(48% 0.02 250);
        /* Staerke des Bereichs-Scheins auf #grid-frame. Im Dunkeln braucht er
           mehr: dieselbe Farbe hebt sich von einem dunklen Grund schlechter ab
           als von einem hellen. */
        --wash-alpha: 0.35;
        /* 60% ergab auf der Karte (--tint-color, 30%) genau 3.43:1 und trug
         * damit Anrisstext, Datum und Pronomen unter AA -- Design-Review A1.
         * Geloest waeren 67% (4.52); 69% gibt Luft, damit Kantenglaettung es
         * nicht wieder darunterdrueckt. Auf der Seite: 5.63. */
        --muted-color: oklch(69% 0.02 250);
        --neutral-color: oklch(35% 0.02 250);
        --invert-color: oklch(96% 0.04 90);

        /* Category colors */
        --category-con-lang: oklch(50% 0.19 150);
        --category-tech-talk: oklch(45% 0.20 265);
        --category-world-building: oklch(45% 0.22 305);
        --category-indie-game-dev: oklch(78% 0.18 60);
        --category-quo-vadis: oklch(65% 0.06 325);

        /* Tag count colors */
        --tag-low: oklch(60% 0.02 250);
        --tag-mid: oklch(45% 0.20 265);
        --tag-high: oklch(50% 0.19 150);

        /* Copper/gold gradients */
        --copper-gradient: linear-gradient(
            oklch(80% 0.06 50),
            oklch(70% 0.02 60),
            oklch(55% 0.08 45),
            oklch(45% 0.06 40)
        );
        --gold-gradient: linear-gradient(
            oklch(30% 0.04 20),
            oklch(65% 0.12 70),
            oklch(30% 0.04 20)
        );

        /* Pie menu */
        --pie-from: var(--base-color);
        --pie-to: var(--tint-color);
        --pie-accent: oklch(50% 0.18 15);
        --size-pie: 6em;
        --size-inner-pie: 4.5em;

        /* Typography */
        --ff-body: 'Recursive', monospace, sans-serif;
        --ff-title: 'Recursive', monospace, sans-serif;
        --fw-body: 400;
        --fw-title: 700;
        --text-factor: 1;

        /* Fluid font sizes */
        --ft-size-200: clamp(0.65rem, 0.6rem + 0.2vw, 0.75rem);
        --ft-size-300: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
        --ft-size-400: clamp(0.875rem, 0.8rem + 0.3vw, 1rem);
        --ft-size-500: clamp(1rem, 0.9rem + 0.4vw, 1.2rem);
        --ft-size-600: clamp(1.15rem, 1rem + 0.5vw, 1.4rem);
        --ft-size-700: clamp(1.3rem, 1.1rem + 0.6vw, 1.7rem);
        --ft-size-800: clamp(1.6rem, 1.3rem + 0.8vw, 2rem);
        --ft-size-900: clamp(1.9rem, 1.5rem + 1vw, 2.4rem);

        /* Layout */
        --header-height: 6.3em;
    }

    /* Light theme override
     *
     * Two selectors per rule on purpose:
     *   html[data-theme="light"] is what the settings modal actually writes
     *     (SettingsModal.tsx -> localStorage 'ddd-theme' -> data attribute).
     *   #light:checked ~ #grid-frame is the legacy radio mirror the server
     *     still renders from the cookie. Kept so both paths land on the same
     *     look; it goes away once the radios do.
     *
     * The attribute sits on <html>, so the tokens inherit past #grid-frame and
     * also reach anything rendered outside it. Specificity inside @layer tokens:
     * html[data-theme] is (0,1,1) and beats the :root defaults (0,1,0) above.
     */
    html[data-theme="light"],
    #light:checked ~ #grid-frame {
        --base-color: oklch(95% 0.02 90);
        --contrast-color: oklch(20% 0.02 250);
        --invert-color: oklch(30% 0.02 250);
        --abyss-color: oklch(98% 0.01 90);
        --tint-color: oklch(90% 0.03 80);
        /* Gegenstueck zum Dunkel-Modus: die aufgesetzte Flaeche ist hier die
           hellste, bleibt aber in der Cremefamilie (0.02 Saettigung wie alle
           anderen Flaechen) statt auszubleichen. */
        --surface-raised: oklch(98% 0.02 90);
        --surface-border: oklch(80% 0.02 80);
        /* Heller Knauf auf gedaempfter Bahn -- im Dunkeln ist es umgekehrt.
           Vorher lag ein fast schwarzer Knauf (15%) auf dunkelgrauer Bahn
           (40%): auf hellem Grund sah man keinen Schalter, nur einen Klumpen. */
        --switch-track: oklch(72% 0.02 80);
        --switch-thumb: oklch(99% 0.01 90);
        --leader-color: oklch(72% 0.02 80);
        --wash-alpha: 0.20;
        --seam-color: oklch(80% 0.02 80);
        --muted-color: oklch(45% 0.02 250);
        --neutral-color: oklch(85% 0.02 90);
        --pie-from: oklch(90% 0.03 80);
        --pie-to: oklch(85% 0.04 70);

        /* Accents, re-lit for a light background.
         *
         * These three were missing from the light override, so they kept their
         * dark-theme lightness: burst measured 1.87:1 against the page and the
         * H1 was all but invisible. Hue and chroma are unchanged -- only
         * lightness moves -- so the brand reads the same, just inverted in
         * value. Each was solved for >= 4.5:1 against BOTH the page background
         * (--base-color) and the card background (--tint-color); the card is
         * the tighter constraint, hence the extra headroom on the page.
         *   burst     5.31 / 4.54
         *   accent    5.36 / 4.58
         *   spotlight 5.27 / 4.51
         */
        --burst-color: oklch(50.5% 0.18 60);
        --accent-color: oklch(47% 0.12 170);
        --spotlight-color: oklch(49% 0.08 230);

        color-scheme: light;

        /* Category colours, re-lit like the accents above.
         * con-lang, tech-talk and world-building are already dark enough to
         * carry on a light background; these two were not -- indie-game-dev
         * measured 1.87:1. Hue and chroma unchanged. */
        --category-indie-game-dev: oklch(50.5% 0.18 60);
        --category-quo-vadis: oklch(45% 0.06 325);
    }

    /* The surface overrides that used to live here moved out of the layers --
     * see "Light theme surfaces" below. @layer components sets .footer's
     * background, and components beats tokens no matter the specificity, so
     * they never applied from in here. */
}

/* --- Layout --- */
@layer layout {
    body {
        background: var(--base-color);
        color: var(--contrast-color);
        font-family: var(--ff-body);
        font-weight: var(--fw-body);
        font-size: var(--ft-size-400);
        line-height: 1.6;
        isolation: isolate;
    }

    html, body {
        height: 100%;
    }

    #grid-frame {
        display: grid;
        grid-auto-flow: row;
        grid-template-rows: auto 1fr auto;
        min-height: 100%;
        background-color: var(--base-color);
        /* Zwei Ebenen. Oben das Bereichs-Kolorit (--area-wash, weiter unten je
           body.area-* gesetzt), darunter der Seitenverlauf.

           Das Kolorit sass bis zum 07.08.2026 auf .area-intro::before und war
           damit an einen Block gebunden -- es bekam dessen Kante, sichtbar als
           heller Streifen zwischen der Spaltenlinie (x=308) und dem Blockanfang
           (x=333). Gemeint war es nie als Bauteil-Schmuck, sondern als
           Hintergrund-Interesse; deshalb liegt es jetzt eine Ebene tiefer, wo es
           keinen Rand hat, an dem es enden koennte. (Ole, 07.08.2026: „das soll
           ja eigentlich nur ein radial gradient fuer den BG sein… vielleicht
           auch wirklich auf die unterste Ebene".)

           Der Verlauf darunter steht bewusst NUR hier: er nutzt Token und ist
           dadurch von sich aus themenfaehig. Wer ihn fuer eine zweite Regel
           kopiert, baut die naechste Liste, die den Neuzugang nicht kennt. */
        background-image:
            var(--area-wash, none),
            radial-gradient(
                80% 50% at 60% -200px,
                var(--tint-color),
                var(--base-color)
            );
        background-size: 100% 1600px, 120% 2000px;
        background-repeat: no-repeat;
        background-position: top left, top 58px center;
    }

    /* Header grid */
    .header {
        display: grid;
        /* minmax(0, …) wie bei .mid-section: sonst haelt min-width:auto die
           mittlere Spalte auf der min-content-Breite des Menues -- gemessen
           1578px auf einem 1440er-Schirm, was den Header und damit die ganze
           Seite ueber den Viewport hinausschob. */
        grid-template-columns: auto minmax(0, 1fr) auto;
        align-items: center;
        background-color: var(--abyss-color);
        padding: 0 1em;
        position: sticky;
        top: 0;
        z-index: 9000;
        border-bottom: 1px solid var(--seam-color);
        min-height: var(--header-height);
    }

    /* Mid section — 3-column layout */
    .mid-section {
        display: grid;
        grid-auto-flow: row;
        grid-template-areas:
            "banner-menu"
            "page-content"
            "side-info"
            "tag-section";
        /* Eine Spalte: Menue, Beitraege, dann die Schlagwoerter --
           "bei 1 col unter den Blog-Panels" (Ole, 28.08.). */
        margin-inline: 1em;
        gap: 1.5em;
        /* 28.08.2026: Abstand nach oben wieder da, diesmal begruendet.
           Am 06.08. war `padding-block: 1.5em` entfernt worden, weil es "oben
           nur den Anfangszustand kaschiert" habe -- sobald man scrolle, klebe
           die Leiste ohnehin sticky und die 24px seien weg. Das Argument gilt
           der MENUELEISTE; fuer den Beitragstext stimmt es nicht: der scrollt
           mit, und sein Anfang ist genau der Moment, den man beim Aufrufen
           sieht. Ohne das Polster begann die Ueberschrift bei y=101 -- exakt
           auf der Unterkante des Kopfbereichs, Abstand null (Ole: "das
           Margin/Padding ueber der Ueberschrift ist zu gering").
           ⚠️ Der erste Versuch setzte das Polster HIER, auf .mid-section --
           also aussen. Damit rutschte der ganze Bereich nach unten: die
           Menueleiste bekam Platz, den sie nicht braucht (sie soll von der
           Kopfleiste ueberlagert werden), und der senkrechte Trennstrich
           (border-left auf .page-content) begann erst 40px tiefer -- ein
           alter, laengst behobener Fehler war wieder da. Das Polster gehoert
           INNEN auf .page-content: dann bleibt das Element samt Rahmen oben
           stehen und nur sein Inhalt rueckt ein. */
    }

    .banner-menu { grid-area: banner-menu; }
    .page-content {
        grid-area: page-content;
        /* Innen, nicht aussen -- siehe Begruendung bei .mid-section. */
        padding-block-start: 2.5rem;
    }
    .side-info { grid-area: side-info; }
    .tag-section { grid-area: tag-section; }

    @media (min-width: 60em) {
        .mid-section {
            max-width: clamp(78vw, 108em, 100%);
            /* minmax(0, 1fr) statt 1fr: Grid-Items haben min-width:auto und
               schrumpfen nicht unter ihren Inhalt. Ein Code-Block mit langen
               Zeilen blies die Spalte deshalb auf 1363px auf und die Seite
               auf 2155px Breite -- horizontaler Ueberlauf trotz max-width. */
            grid-template-columns: 16em minmax(0, 1fr);
            /* 28.08.2026: side-info lag hier in der MENUE-Spalte (zweite Zeile,
               erste Spalte) -- die Autoren-Karten standen dadurch unter der
               Navigation statt unter den Beitraegen. Der 85em-Fall wurde am
               06.08. repariert, dieser dazwischen blieb stehen. */
            /* Zwei Spalten: Menue links, Beitraege rechts, die Schlagwoerter
               klassisch unter dem Untermenue (Ole, 28.08.). */
            grid-template-areas:
                "banner-menu page-content"
                "tag-section page-content"
                "tag-section side-info";
            gap: 2.5em;
        }
        .banner-menu,
        .side-info {
            align-self: start;
        }
    }

    /* 85em statt 94em: bei 94em (1504px) blieb ein 1440er-Bildschirm zweispaltig,
       die Inhaltsspalte wurde 1129px breit (weit ueber 100 Zeichen je Zeile) und
       die Autoren-Karten hingen unter der Tag-Cloud -- mit rund 500px Leerraum
       dazwischen, weil banner-menu und side-info in zwei Grid-Zeilen liegen,
       deren Hoehe der lange Inhalt daneben aufspannt. Dreispaltig loest beides:
       Inhalt 925px, side-info wandert nach rechts oben. */
    @media (min-width: 85em) {
        .mid-section {
            margin-inline: auto;
            padding-inline: 1.5em;
            /* war max(80vw, 107em) -- max() nimmt den GROESSEREN Wert und
               erzwang damit mindestens 107em = 1712px. Auf einem 1440er-Schirm
               lief die Seite dadurch 1999px breit (horizontaler Ueberlauf).
               Unauffaellig geblieben war das nur, weil dieser Breakpoint frueher
               bei 94em lag und dort nie griff. Gemeint war eine Obergrenze: */
            max-width: min(100%, 107em);
            /* 28.08.2026: Ab hier steht die Tag-Wolke RECHTS (Ole). Die dritte
               Spalte gehoerte den Autorenkarten; die sind heute entfallen, weil
               sie eine Mitwirkung behaupteten, die es so nicht gab. Der Platz
               war frei, die Schlagwoerter passen dorthin -- Beiwerk neben dem
               Beitragsstrom statt darunter. */
            grid-template-columns: 15.25em minmax(0, 1fr) 18em;
            grid-template-areas:
                "banner-menu page-content tag-section"
                "side-info   page-content tag-section";
        }
        .side-info {
            position: sticky;
            top: 4em;
        }
    }


    /* 28.08.2026 nachgetragen aus public/css/layers/06-layout.css:
       .cluster stand 2x im Markup und hatte keine Regel.
       ⚠️ .prose-grid BEWUSST NICHT uebernommen -- siehe Coming Up. */
    .cluster {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-400);
    align-items: center;
    }
}

/* --- Components --- */
@layer components {

    /* ========== SKIP LINK ========== */
    .skip-nav-link,
    .skip-link {
        position: absolute;
        top: -100%;
        left: 1em;
        background: var(--burst-color);
        color: var(--abyss-color);
        padding: 0.5em 1em;
        border-radius: 0 0 0.3em 0.3em;
        z-index: 99999;
        font-weight: 700;
        transition: top 0.2s;
    }
    .skip-nav-link,
    .skip-link:focus {
        top: 0;
    }

    /* ========== HEADER / LOGO ========== */
    .logo a {
        display: flex;
        align-items: center;
        padding: 0.4em 0.5em;
        font-family: var(--ff-title);
        font-weight: 900;
        font-size: var(--ft-size-300);
        font-variation-settings: 'CASL' 1, 'MONO' 1, 'CRSV' 1;
        color: var(--burst-color);
        line-height: 1;
        letter-spacing: 0.05em;
        text-transform: uppercase;
    }
    .logo a span {
        display: flex;
        flex-direction: column;
        gap: 0;
        line-height: 1.1;
    }

    /* ========== MAIN NAV ========== */
    .main-menu {
        display: flex;
        align-items: center;
        gap: 0.5em;
    }
    .main-menu nav {
        display: flex;
        gap: 0.25em;
    }
    .main-menu nav a {
        padding: 0.4em 0.75em;
        color: var(--contrast-color);
        font-size: var(--ft-size-300);
        font-variation-settings: 'CASL' 0.5, 'MONO' 0.5;
        text-transform: uppercase;
        letter-spacing: 0.08em;
        border-radius: 0.25em;
        transition: background 0.2s, color 0.2s;
    }
    /* 28.08.2026: Statt einer Flaeche zwei radiale Verlaeufe, die von den
       Trennlinien nach innen wachsen -- die Rahmenlinien zwischen den
       Punkten wirken dadurch wie Lichtquellen, die angehen. Animiert wird
       background-size, nicht die Deckkraft: Licht breitet sich aus, es
       wird nicht als ganze Flaeche heller. Beim Klick wachsen beide bis
       zur vollen Breite. (Oles Idee.) */
    .main-menu nav a {
        /* Das Licht traegt --seam-color: die Farbe der Trennstriche, von denen
           es ausgeht. Zwei Sackgassen davor, beide gemessen:
           1. --tint-color ist oklch(90%) gegen 95% hellen Grund -- fuenf
              Prozentpunkte, als weicher Verlauf schlicht unsichtbar.
           2. --burst-color war sichtbar, drueckte den aktiven Punkt aber auf
              Kontrast 3.66 (WCAG AA braucht 4.5) -- Licht und Schrift hatten
              dieselbe Farbe, der Grund rueckte an die Schrift heran.
           ⚠️ --accent-color (tuerkis) half NICHT, obwohl komplementaer: 3.64.
           Nicht der Farbton entscheidet, sondern die Helligkeit -- beide
           liegen bei ~47-50%. --seam-color ist oklch(80%), toent also ohne
           abzudunkeln.
           📌 Der Verlauf ist ENG (transparent ab 62%) und dafuer am Rand
           voll deckend. Vorher lief er erst bei 78% aus, reichte also bis
           in die Textzone und musste deshalb schwach bleiben -- sichtbar
           war er dann nicht mehr (Ole: "das ist ehrlich gesagt zu wenig").
           Ein Streiflicht von der Seite ist am Rand kraeftig und in der
           Mitte fort; so darf es stark sein, ohne den Text zu bedrängen. */
        background-image:
            radial-gradient(ellipse at left  center,
                var(--seam-color) 0%,
                color-mix(in oklab, var(--seam-color) 55%, transparent) 30%,
                transparent 62%),
            radial-gradient(ellipse at right center,
                var(--seam-color) 0%,
                color-mix(in oklab, var(--seam-color) 55%, transparent) 30%,
                transparent 62%);
        background-repeat: no-repeat;
        background-position: left center, right center;
        background-size: 0% 100%, 0% 100%;
    }
    /* ⚠️ Nicht --burst-color pur: gegen das volle Licht ergibt die
       Kontrast 3.97, WCAG AA verlangt 4.5. Ein Stich Richtung
       --contrast-color bringt 5.14.
       ⚠️ NICHT mit `black` mischen: das half nur im hellen Theme. Dort ist
       die Akzentfarbe oklch(50.5%) und wird durch Abdunkeln kraeftiger --
       im dunklen ist sie oklch(78%), also hell auf dunklem Grund, und
       Abdunkeln schwaechte sie (gemessen 3.92). --contrast-color dreht
       sich mit dem Theme: hell im Dunkeln, dunkel im Hellen. Die Korrektur
       zeigt damit immer in die richtige Richtung. */
    .main-menu nav a:hover,
    .main-menu nav a:focus-visible {
        background-size: 62% 100%, 62% 100%;
        color: color-mix(in oklab, var(--burst-color) 82%, var(--contrast-color));
    }
    .main-menu nav a:active,
    .main-menu nav a.active {
        background-size: 100% 100%, 100% 100%;
        color: color-mix(in oklab, var(--burst-color) 82%, var(--contrast-color));
    }

    /* ========== OPTIONS BAR ========== */
    .options {
        display: flex;
        align-items: center;
        gap: 0.5em;
        justify-self: end;
    }
    .nice-box {
        display: inline-flex;
        position: relative;
        padding: 4px 9px;
        margin: 5px;
        min-height: 2.5em;
        justify-content: space-evenly;
        align-items: center;
        font-size: var(--ft-size-300);
    }
    .nice-box::before {
        content: "";
        display: inline-block;
        z-index: -3;
        position: absolute;
        background-color: var(--base-color);
        border: 1px solid var(--seam-color);
        box-shadow: 1px 1px 1px 1px oklch(0% 0 0 / 30%);
        border-radius: 3px;
        inset: 1px;
    }
    .nice-box::after {
        content: "";
        display: inline-block;
        z-index: -4;
        position: absolute;
        background: var(--tint-color);
        box-shadow: inset 1px 1px 1px 1px oklch(0% 0 0 / 20%);
        border-radius: 4px;
        inset: -4px;
    }
    .nice-box > * {
        margin: 4px 0;
        padding: 0 4px;
        display: inline-block;
        border-right: 1px solid var(--neutral-color);
        border-left: 1px solid var(--seam-color);
    }
    .nice-box > *:last-child { border-right: none; }
    .nice-box > *:first-child { border-left: none; }
    .nice-box label {
        cursor: pointer;
        padding: 0.2em 0.35em;
        border-radius: 0.2em;
        color: var(--muted-color);
        transition: color 0.2s, background 0.2s;
        line-height: 1;
    }
    .nice-box label:hover {
        color: var(--burst-color);
        background: var(--tint-color);
    }
    .nice-box select {
        background: transparent;
        border: none;
        color: var(--contrast-color);
        font-family: var(--ff-body);
        font-size: var(--ft-size-200);
        cursor: pointer;
    }
    .nice-box select option {
        background: var(--base-color);
        color: var(--contrast-color);
    }
    .nice-select {
        gap: 0.3em;
    }
    .select-wrapper {
        display: flex;
        align-items: center;
        gap: 0.3em;
        cursor: pointer;
    }
    .font-small { font-size: 0.7em; }
    .font-mid { font-size: 1em; }
    .font-big { font-size: 1.3em; }

    /* Mobile menu toggles — hidden on desktop */
    .mobile-menu-toggle {
        display: none;
        cursor: pointer;
        font-size: var(--ft-size-600);
        color: var(--muted-color);
        padding: 0.3em;
    }
    @media (max-width: 59.99em) {
        .mobile-menu-toggle { display: inline-flex; }
        .main-menu > div,
        .options > div { display: none; }
        #menu-toggle:checked ~ div { display: flex; flex-wrap: wrap; gap: 0.3em; }

        /* 28.08.2026: Bis hierher gab es auf dem Handy ZWEI Navigationen -- den
           Burger oben und die Menuespalte direkt darunter. Die Spalte klappt
           jetzt mit dem Burger auf, statt dauerhaft vor den Beitraegen zu stehen.
           :has() statt Geschwister-Auswahl, weil die Checkbox im <header> sitzt
           und die Menuespalte ausserhalb davon -- `~` kommt da nicht hinueber.
           Erste :has()-Nutzung im Projekt; Baseline seit 2023 in allen Engines. */
        .banner-menu { display: none; }
        body:has(#menu-toggle:checked) .banner-menu { display: block; }
        #option-toggle:checked ~ div { display: flex; flex-wrap: wrap; gap: 0.3em; }
        .header {
            grid-template-columns: auto auto 1fr;
            flex-wrap: wrap;
        }
        .main-menu > div,
        .options > div {
            position: absolute;
            top: var(--header-height);
            left: 0;
            right: 0;
            background: var(--abyss-color);
            padding: 0.5em 1em;
            border-bottom: 1px solid var(--seam-color);
            z-index: 8999;
        }
        .options > div { right: 0; left: auto; }
    }

    /* ========== BANNER MENU (Left Sidebar) ========== */
    .banner-menu {
        padding: 0;
        z-index: 100;
    }
    /* Kleben darf sie erst, wenn sie eine eigene Spalte hat. Unter 60em
       stapelt .mid-section sie ueber den Inhalt -- dort verdeckte sie als
       sticky mit z-index 100 die Beitragsliste: auf 390px ist die Leiste
       503px hoch, gemessen 323x503px Ueberdeckung, und ein Klick auf Hoehe
       200px oder 400px traf die Leiste statt der Karte darunter.
       .side-info macht es ab 85em genauso richtig (siehe oben). */
    @media (min-width: 60em) {
        .banner-menu {
            position: sticky;
            /* ⚠️ KEIN Fehler, sondern Absicht -- Korrektur vom 28.08.2026.
               Hier stand: "War top: 4em und damit 2.3em ZU WEIT OBEN; gemessen
               verschwanden bei jeder Scrollhoehe 37px der Leiste unter dem Kopf."
               Daraus wurde ein Defekt gemacht und auf calc(--header-height + 1em)
               "behoben".
               Oles Gestaltung war genau das: Die Leiste soll wie ein Flaggen-Banner
               an der Kopfleiste HAENGEN, also dort verankert sein -- dass ihre
               Oberkante teilweise verdeckt ist, IST die Verankerung. Ole am 28.08.:
               "das ist ein Trillian-Problem, wo du Fehler siehst, wo keine sind...
               das hast du im 'ich buegel mal automatisch alles glatt, was ich als
               Fehler sehe' ohne mich zu fragen, ob das eine Intention war."
               Ein Messwert, der von einer Erwartung abweicht, ist erst dann ein
               Fehler, wenn die Erwartung belegt ist. "Buendig" war meine Annahme,
               nicht die Vorgabe. */
            top: 4em;
        }
    }
    .menu-element {
        position: relative;
        display: flex;
        flex-direction: column;
        width: 100%;
        padding: 0.5rem 0 0;
        border-radius: 2px;
        background-color: var(--base-color);
        isolation: isolate;
    }
    /* Shadow frame behind menu */
    .menu-element::before {
        position: absolute;
        display: block;
        content: "";
        z-index: -2;
        inset: -2px;
        box-shadow: 5px 5px 15px 5px oklch(0% 0 0 / 40%);
        border-radius: 2px;
    }
    /* Copper gradient behind menu */
    .copper-border::before {
        background-image: var(--copper-gradient);
        background-size: 100% 300%;
        background-position: top;
        animation: copper-shimmer linear 12s alternate infinite;
    }
    @keyframes copper-shimmer {
        0% { background-position: top; }
        100% { background-position: bottom; }
    }

    .segment-subject {
        display: flex;
        justify-content: flex-start;
        color: var(--burst-color);
        font-weight: 700;
        font-size: var(--ft-size-400);
        font-variation-settings: 'CASL' 0.8, 'MONO' 0.5;
        padding: 0;
        margin: 0.8rem 0 0 0.25rem;
        text-transform: uppercase;
        text-decoration: none;
    }
    .sub-segment-subject {
        padding: 0;
        /* 0.75rem statt 1rem: fluchtet mit dem ♦ der Eintraege darueber,
           die ueber .link-element denselben Aussenabstand haben. */
        margin: 0.35rem 0 0 0.75rem;
        text-transform: uppercase;
        text-decoration: none;
        font-size: var(--ft-size-300);
        font-weight: 600;
        font-variation-settings: 'CASL' 0.6, 'MONO' 0.3;

        /* Kategoriefarbe als Text, nicht als Flaeche (Design-Review A1).
         *
         * Die --category-* Tokens sind fuer die Panels der Karten abgestimmt:
         * dort sind sie Hintergrund, und ab 62% Helligkeit kippt die
         * Panel-Schrift von weiss auf schwarz. Als Text auf dunklem Grund waren
         * drei von fuenf zu dunkel -- world-building 1.90, tech-talk 2.05,
         * con-lang 3.06. Sie einfach aufzuhellen haette die Panels umgebaut.
         *
         * Also hier ein Helligkeits-Boden statt eines zweiten Farbsatzes.
         * max() laesst die schon hellen in Ruhe (indie-game-dev bleibt 0.78),
         * hebt nur die zu dunklen an. Das Template setzt --kategorie-farbe,
         * eine neue Kategorie braucht damit weiterhin keine Code-Zeile.
         * Gemessen gegen --base-color: 5.94 / 5.74 / 6.98 / 6.30 / 7.41. */
        color: oklch(from var(--kategorie-farbe) max(l, 0.72) c h);
    }

    /* Auf hellem Grund andersherum: eine Decke statt eines Bodens. Greift
     * derzeit nirgends (alle fuenf liegen im Hell-Thema schon bei <= 0.5),
     * steht aber da, damit eine kuenftige helle Kategorie nicht durchfaellt. */
    html[data-theme="light"] .sub-segment-subject {
        color: oklch(from var(--kategorie-farbe) min(l, 0.5) c h);
    }
    .segment-element {
        margin-bottom: 0.25em;
    }
    .link-element {
        display: flex;
        justify-content: flex-end;
        margin: 0 0.75rem;
        color: var(--invert-color);
        letter-spacing: 0.1rem;
        font-size: var(--ft-size-300);
        text-transform: uppercase;
        text-decoration: none;
        padding: 0.15em 0;
        transition: color 0.2s;
    }
    .link-element:hover {
        color: var(--burst-color);
    }

    /* ========== TAG CLOUD ========== */
    .tag-cloud {
        margin: 1.25em 0 0 0;
        display: flex;
        justify-content: flex-start;
        align-content: flex-start;
        flex-wrap: wrap;
        gap: 0.4em;
        width: 100%;
    }
    /* Ueber die id statt ueber den Tag: die Ebene dieser Ueberschrift haengt
       an der Dokumentstruktur (06.08.2026 von h3 auf h2 gehoben, damit die
       Seite keine Ebene ueberspringt), ihre Gestaltung soll das nicht.
       Als `.tag-cloud h3` fiel sie beim Ebenenwechsel stumm aus und die
       Ueberschrift rutschte in die Pillen-Zeile. */
    #subject-tag-cloud {
        width: 100%;
        color: var(--burst-color);
        font-size: var(--ft-size-400);
        font-weight: 700;
        font-variation-settings: 'CASL' 0.8, 'MONO' 0.5;
        text-transform: uppercase;
        margin-bottom: 0.3em;
    }
    /* EIN Chip, zwei Orte. Bis zum 07.08.2026 hatten Leiste und Karte je eine
       eigene Regel, und die waren auseinandergedriftet: die Karte schrieb
       VERSALIEN, hatte einen anderen Grundton (--neutral statt --tint), einen
       anderen Radius (0.2 statt 0.25em), anderes Polster und eine andere
       Hover-Farbe. Dieselbe Entitaet in zwei Erscheinungsformen -- die
       Verbindung „das ist derselbe Filter" musste der Leser selbst herstellen
       (Design-Runde 3, Befunde 6 und 14).

       Zusammengelegt statt angeglichen: zwei Regeln mit gleichen Werten waeren
       nur die naechste Stelle, die beim uebernaechsten Mal wieder abweicht.
       Die Karte hat weiterhin keine Zahl -- dafuer braucht sie keine eigene
       Regel, sie hat schlicht kein <span> darin. */
    .tag-cloud a,
    .teaser-footer a {
        display: inline-flex;
        align-items: center;
        gap: 0.3em;
        padding: 0.2em 0.5em;
        font-size: var(--ft-size-200);
        color: var(--contrast-color);
        background: var(--tint-color);
        border-radius: 0.25em;
        transition: background 0.2s, color 0.2s;
        text-decoration: none;
    }
    .tag-cloud a:hover,
    .teaser-footer a:hover {
        background: var(--neutral-color);
        color: var(--burst-color);
    }
    .tag-cloud a span {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 1.4em;
        height: 1.4em;
        padding: 0 0.3em;
        border-radius: 0.75em;
        font-size: 0.8em;
        font-weight: 700;
        line-height: 1;
    }
    .tag-count-low {
        background-color: var(--tag-low);
        color: var(--abyss-color);
    }
    .tag-count-mid {
        background-color: var(--tag-mid);
        color: var(--invert-color);
    }
    .tag-count-high {
        background-color: var(--tag-high);
        color: var(--invert-color);
    }

    /* ========== MAIN CONTENT ========== */
    .markdown-body {
        line-height: 1.7;
    }
    .markdown-body section {
        display: grid;
        grid-template-columns: 1fr;
        gap: 0;
        margin-bottom: 0.5em;
    }
    @media (min-width: 60em) {
        .markdown-body section {
            grid-template-columns: 1fr 3fr;
        }
    }

    /* Sticky Headlines */
    .sticky-headline {
        display: none;
    }
    @media (min-width: 60em) {
        .sticky-headline {
            display: block;
            text-align: right;
            align-self: start;
            position: sticky;
            top: calc(var(--header-height) + 1em);
            font-size: var(--ft-size-300);
            font-variation-settings: 'CASL' 1, 'MONO' 1;
            color: var(--muted-color);
            padding-right: 1em;
            border-right: 2px solid var(--seam-color);
            line-height: 1.4;
            max-width: 12em;
        }
    }

    .text-block {
        padding: 0.5em 0 1.5em 0;
    }
    @media (min-width: 60em) {
        .text-block {
            padding: 0.5em 0 1.5em 1.5em;
        }
    }
    .text-block h1 {
        font-size: var(--ft-size-800);
        font-variation-settings: 'CASL' 1, 'MONO' 0.5, 'CRSV' 1;
        color: var(--burst-color);
        margin-bottom: 0.5em;
    }
    .text-block h2 {
        font-size: var(--ft-size-700);
        font-variation-settings: 'CASL' 1, 'MONO' 0.5;
        color: var(--accent-color);
        margin-bottom: 0.5em;
    }
    .text-block h3 {
        font-size: var(--ft-size-600);
        font-variation-settings: 'CASL' 0.8, 'MONO' 0.3;
        color: var(--spotlight-color);
        margin-bottom: 0.4em;
    }
    .text-block h4 {
        font-size: var(--ft-size-500);
        font-variation-settings: 'CASL' 0.6, 'MONO' 0.3;
        color: var(--accent-color);
        margin-bottom: 0.3em;
    }
    .text-block p {
        margin-bottom: 1em;
        color: var(--contrast-color);
    }
    .text-block code {
        background: var(--tint-color);
        padding: 0.15em 0.4em;
        border-radius: 0.2em;
        font-family: var(--ff-body);
        font-variation-settings: 'MONO' 1;
        font-size: 0.9em;
        color: var(--burst-color);
    }
    .text-block blockquote {
        border-left: 3px solid var(--accent-color);
        padding: 0.5em 1em;
        margin: 0.5em 0 1em;
        background: var(--tint-color);
        border-radius: 0 0.3em 0.3em 0;
        font-style: italic;
        color: var(--muted-color);
    }
    .text-block a {
        color: var(--accent-color);
        text-decoration: underline;
        text-decoration-color: oklch(60% 0.12 170 / 0.4);
        text-underline-offset: 0.2em;
        transition: color 0.2s, text-decoration-color 0.2s;
    }
    .text-block a:hover {
        color: var(--burst-color);
        text-decoration-color: var(--burst-color);
    }

    /* ========== TEASER LIST ========== */
    #teaser-subject {
        font-size: var(--ft-size-700);
        font-variation-settings: 'CASL' 1, 'MONO' 0.5;
        color: var(--burst-color);
        margin: 1.5em 0 0.75em;
        padding-bottom: 0.3em;
        border-bottom: 2px solid var(--seam-color);
    }
    .teaser-list {
        display: flex;
        flex-direction: column;
        gap: 2em;
    }

    /* ========== TEASER CARD ========== */
    .teaser-card {
        position: relative;
        display: grid;
        grid-auto-flow: row;
        width: 100%;
        border-radius: 0.5em;
        overflow: hidden;
        background: var(--tint-color);
        border: 1px solid var(--seam-color);
        transition: border-color 0.3s, box-shadow 0.3s;
    }
    .teaser-card:hover {
        border-color: var(--muted-color);
        box-shadow: 0 4px 20px oklch(0% 0 0 / 0.3);
    }

    .teaser-pre {
        padding: 0.6em 1em;
        text-align: center;
        text-transform: uppercase;
        font-size: var(--ft-size-300);
        font-family: var(--ff-title);
        font-weight: var(--fw-title);
        font-variation-settings: 'CASL' 0.8, 'MONO' 0.5;
        color: var(--invert-color);
        letter-spacing: 0.1em;
        position: relative;
        z-index: 1;
    }
    .teaser-pre .teaser-category {
        font-size: inherit;
        font-weight: inherit;
        margin: 0;
    }

    /* Category color bars */
    /* Kategoriefarbe nur noch als Variable setzen -- der Verlauf steht einmal
       darunter. Vorher war jeder Balken eine Vollflaeche ueber die ganze
       Kartenbreite; bei elf Beitraegen untereinander ergab das einen
       Farbstreifen-Teppich, der lauter war als die Titel. Jetzt greift die
       Farbe nur oben links und unten rechts an, die Mitte bleibt Kartenton. */
    .teaser-card[data-category="quo-vadis"]      { --kategorie-farbe: var(--category-quo-vadis); }
    .teaser-card[data-category="tech-talk"]      { --kategorie-farbe: var(--category-tech-talk); }
    .teaser-card[data-category="con-lang"]       { --kategorie-farbe: var(--category-con-lang); }
    .teaser-card[data-category="world-building"] { --kategorie-farbe: var(--category-world-building); }
    .teaser-card[data-category="indie-game-dev"] { --kategorie-farbe: var(--category-indie-game-dev); }
    /* Devlog-Beitraege tragen den Spielnamen als Kategorie, nicht eines der fuenf
       Blog-Themen -- 9 Beitraege allein bei obscured-by-distance fielen deshalb auf
       den grauen Rueckfallwert. Sie erben bewusst die vorhandene Indie-Game-Dev-Farbe,
       statt eine eigene zu bekommen: ein neues Token braeuchte einen Hell- UND einen
       Dunkel-Wert, und genau das Vergessen der Hell-Variante war der Fehler, den
       fcccf7d bei den Landing-Kacheln aufgedeckt hat. */
    .teaser-card[data-category="obscured-by-distance"],
    .teaser-card[data-category="concepts"]       { --kategorie-farbe: var(--category-indie-game-dev); }

    .teaser-pre[data-category] {
        background:
            radial-gradient(ellipse 60% 180% at 0% 0%,
                var(--kategorie-farbe) 0%,
                transparent 70%),
            radial-gradient(ellipse 60% 180% at 100% 100%,
                var(--kategorie-farbe) 0%,
                transparent 70%),
            var(--abyss-color);
        color: var(--contrast-color);
    }

    .teaser-body {
        position: relative;
        display: grid;
        grid-template-columns: 1fr;
        grid-template-areas:
            "image"
            "title"
            "description"
            "low"
            "footer";
        /* 1em wirkte bei 765px breiten Karten gedrueckt, der Text klebte an der
           Kante; 1.5em gibt der Karte Innenraum, ohne sie auseinanderzuziehen.
           Zeilenabstand entsprechend mit (0.5 -> 0.75em). */
        gap: 0.75em;
        padding: 1.5em;
    }
    @media (min-width: 40em) {
        .teaser-body {
            grid-template-columns: 6em 1fr;
            grid-template-areas:
                "image title"
                "image description"
                "image low"
                "image footer";
        }
    }

    .teaser-shape {
        grid-area: image;
        width: 5em;
        height: 5em;
        border-radius: 50%;
        overflow: hidden;
        border: 2px solid var(--seam-color);
        justify-self: center;
    }
    .teaser-shape-content {
        width: 100%;
        height: 100%;
        background-size: cover;
        background-position: center;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
    }
    /* Der Schleier lag bei 0.6 -- er musste das Platzhalter-Emoji lesbar halten.
       Seit die Karten echte Kategoriebilder tragen, verdeckt er nur noch das
       Motiv; 0.22 genuegt, um die Kreise farblich an die Karte zu binden. */
    .teaser-shape-content::after {
        content: "";
        position: absolute;
        inset: 0;
        background: linear-gradient(
            135deg,
            oklch(40% 0.15 150 / 0.22),
            oklch(30% 0.10 250 / 0.22)
        );
        z-index: 0;
    }
    /* Kein Bild, also traegt der Kreis selbst die Farbe statt leer zu wirken. */
    .teaser-shape-content--ohne-bild::after {
        background: linear-gradient(
            135deg,
            oklch(42% 0.15 150),
            oklch(32% 0.10 250)
        );
    }
    .teaser-image {
        width: 100%;
        height: 100%;
        object-fit: cover;
        position: relative;
        z-index: 1;
        opacity: 0;
    }
    /* Der Titel stand auf geerbten 400 -- damit unterschieden sich Titel,
       Teasertext und Datum nur in der Groesse (22/14/12px) und die Karte las
       sich flach. --fw-title existiert im System und war hier ungenutzt.
       Weniger Laufweite, weil fette Recursive sonst gesperrt wirkt. */
    .teaser-title {
        grid-area: title;
        font-size: var(--ft-size-600);
        font-weight: var(--fw-title);
        font-variation-settings: 'CASL' 0.8, 'MONO' 0.3;
        line-height: 1.25;
        letter-spacing: 0.2px;
    }
    .teaser-title a {
        color: var(--contrast-color);
        transition: color 0.2s;
    }
    .teaser-title a:hover {
        color: var(--burst-color);
    }
    /* Der Strich traegt seit dem 07.08.2026 die Kategoriefarbe.
       Vorher stand die Farbcodierung nur in der Leiste und auf dem Schild --
       im Raster sah man die Kategorie erst, wenn man das Schild las, und vier
       Karten mit demselben Motiv standen nebeneinander (Design-Runde 3,
       Befund 2). Kein neues Element: dieser Strich ist schon da, genau einmal
       je Karte, und liegt am Titel statt am Rand.

       Die Zuordnung data-category -> Farbe sitzt jetzt auf .teaser-card statt
       auf .teaser-pre (Zeile ~880) -- damit gibt es EINE Liste fuer die ganze
       Karte statt einer zweiten daneben, die den naechsten Neuzugang vergisst.

       2px statt 1px und volle Deckkraft: bei 1px und opacity 0.5 blieb von der
       Farbe nichts uebrig, was man als Farbe erkennen koennte. Der Rueckfall
       auf --seam-color greift fuer Karten ohne Kategorie. */
    .teaser-title::after {
        content: "";
        display: block;
        width: 2.5em;
        height: 2px;
        margin: 0.5em 0;
        /* Helligkeits-Boden wie bei .sub-segment-subject (Zeile ~627), aus
           demselben Grund: die --category-* Tokens sind fuer die PANELS
           abgestimmt, wo sie Hintergrund sind. Roh als Strich auf dunklem
           Kartenton waeren drei von fuenf zu dunkel -- gemessen am 07.08.
           world-building, tech-talk und con-lang standen im Dunkel-Thema auf
           demselben Wert wie im Hellen. max() laesst die schon hellen in Ruhe. */
        background: oklch(from var(--kategorie-farbe, var(--seam-color)) max(l, 0.72) c h);
        border-radius: 1px;
    }

    /* Auf hellem Grund andersherum -- Decke statt Boden, siehe Leiste. */
    html[data-theme="light"] .teaser-title::after,
    #light:checked ~ #grid-frame .teaser-title::after {
        background: oklch(from var(--kategorie-farbe, var(--seam-color)) min(l, 0.5) c h);
    }

    .teaser-info {
        grid-area: description;
        font-size: var(--ft-size-300);
        color: var(--muted-color);
        line-height: 1.6;
    }

    .teaser-low-info {
        grid-area: low;
        font-size: var(--ft-size-200);
        color: var(--muted-color);
        font-variation-settings: 'MONO' 1;
        /* Datumsangaben stehen untereinander im Raster -- gleich breite
           Ziffern lassen die Spalte ruhiger wirken. */
        font-variant-numeric: tabular-nums;
        letter-spacing: 0.04em;
    }

    .teaser-footer {
        grid-area: footer;
        display: flex;
        flex-wrap: wrap;
        gap: 0.4em;
        /* `text-transform: uppercase` ist am 07.08.2026 entfallen: derselbe Tag
           las sich in der Leiste „meinung", auf der Karte „MEINUNG". Das
           Aussehen der Chips selbst steht jetzt bei .tag-cloud a (Zeile ~679),
           gemeinsam fuer beide Orte. */
    }

    /* ========== PAGINATION ========== */
    .pagination {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        justify-content: space-between;
        gap: 1em;
        margin: 2em 0;
        padding: 1em 0;
        border-top: 1px solid var(--seam-color);
        font-size: var(--ft-size-300);
        color: var(--muted-color);
    }
    .pagination-pages {
        display: flex;
        gap: 0.5em;
    }
    .pagination-pages span,
    .pagination-pages a {
        padding: 0.3em 0.7em;
        border-radius: 0.25em;
        border: 1px solid var(--seam-color);
    }
    .pagination-pages span.disabled {
        opacity: 0.4;
        cursor: default;
    }
    .pagination-pages a {
        color: var(--contrast-color);
        transition: background 0.2s;
    }
    .pagination-pages a:hover {
        background: var(--tint-color);
    }
    .pagination-options select {
        background: var(--tint-color);
        border: 1px solid var(--seam-color);
        color: var(--contrast-color);
        font-family: var(--ff-body);
        font-size: var(--ft-size-200);
        padding: 0.3em 0.5em;
        border-radius: 0.25em;
    }

    /* ========== SIDE INFO (Right Sidebar) ========== */
    /* Wie bei #subject-tag-cloud: an die id gebunden, nicht an die Ebene.
       Als `.side-info h4` verlor die Ueberschrift beim Heben auf h2 ihre
       Gestaltung. */
    #section-author-subject {
        width: 100%;
        margin: 1rem 0 0.5rem;
        font-size: var(--ft-size-500);
        font-variation-settings: 'CASL' 0.8, 'MONO' 0.5;
        color: var(--burst-color);
        text-transform: uppercase;
    }

    /* ========== AUTHOR CARD ========== */
    .author-card {
        position: relative;
        display: grid;
        grid-auto-flow: row;
        width: 100%;
        margin-bottom: 1.5em;
    }
    .author-body {
        display: grid;
        grid-template-areas:
            "image"
            "title"
            "links"
            "note"
            "info"
            "low";
        gap: 0.5em;
        padding: 1em;
        background: var(--tint-color);
        border: 1px solid var(--seam-color);
        border-radius: 0.5em;
    }
    .author-title {
        grid-area: title;
        font-size: var(--ft-size-500);
        font-variation-settings: 'CASL' 1, 'MONO' 0.5;
        text-align: center;
    }
    .author-title a {
        color: var(--accent-color);
        transition: color 0.2s;
    }
    .author-title a:hover { color: var(--burst-color); }

    .author-shape {
        grid-area: image;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 0.75em;
    }
    .author-shape-content {
        width: 5em;
        height: 5em;
        border-radius: 50%;
        overflow: hidden;
        border: 2px solid var(--seam-color);
        background-size: cover;
        background-position: center;
        position: relative;
    }
    .author-shape-content::after {
        content: "";
        position: absolute;
        inset: 0;
        background: linear-gradient(
            135deg,
            oklch(40% 0.15 150 / 0.2),
            oklch(30% 0.10 250 / 0.2)
        );
    }
    /* Muss nach der Basis-Regel stehen: gleiche Spezifitaet, spaeter gewinnt. */
    .author-shape-content--ohne-bild::after {
        background: linear-gradient(
            135deg,
            oklch(42% 0.15 150),
            oklch(32% 0.10 250)
        );
    }
    .author-image {
        width: 100%;
        height: 100%;
        object-fit: cover;
        position: relative;
        z-index: 2;
        opacity: 0;
    }

    .author-link-list {
        grid-area: links;
        display: flex;
        justify-content: center;
        gap: 0.5em;
    }
    .link-promo {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 2.2em;
        height: 2.2em;
        border-radius: 50%;
        background: var(--neutral-color);
        color: var(--contrast-color);
        font-size: var(--ft-size-400);
        transition: background 0.2s, color 0.2s;
    }
    .link-promo:hover {
        background: var(--accent-color);
        color: var(--abyss-color);
    }

    .author-note {
        grid-area: note;
    }
    .author-info {
        grid-area: info;
        text-align: center;
    }
    .author-promote {
        display: inline-block;
        padding: 0.3em 0.7em;
        margin: 0.2em;
        font-size: var(--ft-size-200);
        background: var(--neutral-color);
        color: var(--burst-color);
        border-radius: 0.25em;
        transition: background 0.2s;
    }
    .author-promote:hover {
        background: var(--burst-color);
        color: var(--abyss-color);
    }

    .author-low-info {
        grid-area: low;
        text-align: center;
        font-size: var(--ft-size-200);
        /* War --muted-color und damit das schwaechste Element der Karte:
           gemessen 5.88 hell / 7.61 dunkel, gegen Ko-fi-Knopf 6.16 / 9.69 und
           Namen 14.25 / 15.62. Beide Werte bestehen zwar AA -- es ging um die
           Rangfolge, nicht um Lesbarkeit. Bei klein gesetzten Versalien mit
           0.15em Sperrung ist "gedaempft" die falsche Rolle: die Angabe steht
           bewusst dort (Design-Runde 3, Befund 16).
           Die Unterordnung traegt jetzt die Groesse, nicht der Kontrast --
           dieselbe Farbe wie der Name, aber ein Bruchteil der Schriftgroesse. */
        color: var(--contrast-color);
        font-variation-settings: 'MONO' 1;
        text-transform: uppercase;
        letter-spacing: 0.15em;
    }

    /* ========== FOOTER ========== */
    .footer {
        margin-top: 3em;
        background: oklch(12% 0.05 265);
        border-top: 3px solid var(--burst-color);
        box-shadow: 0 -2px 20px oklch(0% 0 0 / 30%);
        position: relative;
    }
    .footer::before {
        content: "";
        display: block;
        height: 3px;
        background-image: var(--copper-gradient);
        background-size: 100% 300%;
        animation: copper-shimmer linear 12s alternate infinite;
    }

    .footer--copy__date {
        text-align: center;
        padding: 1.5em 1em 1em;
        font-size: var(--ft-size-300);
        color: oklch(65% 0.02 250);
        font-variation-settings: 'MONO' 1;
    }

    .footer--container {
        display: grid;
        grid-template-columns: 1fr;
        gap: 1.5em;
        padding: 1em 2em 2em;
        max-width: 80em;
        margin: 0 auto;
    }
    @media (min-width: 40em) {
        .footer--container {
            grid-template-columns: repeat(3, 1fr);
        }
    }

    .footer--column {
        display: flex;
        flex-direction: column;
        gap: 0.3em;
    }
    .footer--subject {
        font-size: var(--ft-size-400);
        font-variation-settings: 'CASL' 0.8, 'MONO' 0.5;
        color: var(--burst-color);
        text-transform: uppercase;
        margin-bottom: 0.5em;
    }
    .footer--link {
        font-size: var(--ft-size-300);
        color: oklch(75% 0.03 250);
        padding: 0.15em 0;
        transition: color 0.2s;
        display: flex;
        align-items: center;
        gap: 0.4em;
    }
    .footer--link:hover {
        color: var(--contrast-color);
    }
    .footer--link .icon {
        width: 1.1em;
        display: inline-block;
        text-align: center;
    }

    .footer--technics {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        gap: 0.75em;
        padding: 1.5em 1em;
        border-top: 1px solid var(--seam-color);
    }
    .footer--technics-link {
        display: inline-flex;
        align-items: center;
        gap: 0.3em;
        padding: 0.3em 0.6em;
        font-size: var(--ft-size-200);
        color: var(--muted-color);
        border: 1px solid var(--seam-color);
        border-radius: 0.25em;
        transition: color 0.2s, border-color 0.2s;
    }
    .footer--technics-link:hover {
        color: var(--contrast-color);
        border-color: var(--muted-color);
    }
    .footer--technics-info {
        display: none;
    }
    @media (min-width: 60em) {
        .footer--technics-link:hover .footer--technics-info {
            display: inline;
        }
    }

    /* ========== PIE MENU ========== */
    .pie-menu {
        position: fixed;
        top: 0;
        right: 0;
        z-index: 99999;
    }
    .pie-trigger-label {
        cursor: pointer;
    }
    .pie-trigger {
        width: 3.5em;
        height: 3.5em;
        border-radius: 0 0 0 100%;
        background: radial-gradient(
            at right top,
            var(--tint-color) 0%,
            var(--tint-color) 45%,
            var(--base-color) 66%
        );
        color: var(--contrast-color);
        display: flex;
        align-items: flex-start;
        justify-content: flex-end;
        padding: 0.3em 0.4em 0 0;
        font-size: var(--ft-size-600);
        box-shadow: 0 0 5px 5px oklch(0% 0 0 / 0.3);
        transition: transform 0.3s;
        position: fixed;
        top: 0;
        right: 0;
        z-index: 50000;
        cursor: pointer;
    }
    .pie-trigger:hover {
        transform: scale(1.1);
    }
    .pie-closed, .pie-open {
        position: absolute;
        transition: opacity 0.3s;
    }
    .pie-open { opacity: 0; }
    .pie-closed { opacity: 1; }

    #pie-opener:checked ~ .pie-menu .pie-open { opacity: 1; }
    #pie-opener:checked ~ .pie-menu .pie-closed { opacity: 0; }
    /* Trigger stays in place — no rotation */

    .pie--parts a {
        display: inline-block;
        position: fixed;
        right: 0;
        top: 0;
        width: 6em;
        height: 6em;
        border-radius: 0 0 0 100%;
        background-position: top right;
        box-shadow: 0 0 5px 5px oklch(0% 0 0 / 0.3);
        color: oklch(96% 0.04 90);
        font-size: var(--ft-size-500);
        transform-origin: 100% 0;
        transform: rotate(-90deg);
        transition: width 1s, height 1s, transform 1s;
        transition-timing-function: ease-in;
        pointer-events: auto;
        display: flex;
        align-items: center;
        justify-content: center;
        padding-bottom: 1.5em;
        padding-left: 1.5em;
    }
    .pie--parts a:nth-child(n+2) {
        box-shadow: 0 -5px 5px 0 oklch(0% 0 0 / 0.3);
    }

    #pie-opener:checked ~ .pie-menu .pie--parts a:first-child {
        transition: transform 1s;
        transition-delay: 0.1s;
        transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
        transform: rotate(0deg);
    }
    #pie-opener:checked ~ .pie-menu .pie--parts a:nth-child(2) {
        transition: transform 1s;
        transition-delay: 0.3s;
        transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
        transform: rotate(-28deg);
    }
    #pie-opener:checked ~ .pie-menu .pie--parts a:nth-child(3) {
        transition: transform 1s;
        transition-delay: 0.5s;
        transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
        transform: rotate(-60deg);
    }

    /* Pie text trigger (inline) */
    .pie-text-trigger {
        color: var(--accent-color);
        cursor: pointer;
        text-decoration: underline;
        text-decoration-style: dotted;
        text-underline-offset: 0.2em;
    }
    .pie-text-trigger:hover {
        color: var(--burst-color);
    }
}

/* --- Utilities --- */
@layer utilities {
    .visually-hidden {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        border: 0;
    }


    /* 28.08.2026 nachgetragen aus public/css/layers/08-utilities.css --
       diese fuenf stehen im Markup und taten bisher nichts. */
    .mb-4 { margin-bottom: var(--spacing-400); }
    .mt-6 { margin-top: var(--spacing-600); }
    .text-accent { color: var(--accent-color); }
    .text-muted  { color: var(--muted-color); }
    .text-sm   { font-size: calc(var(--font-size-200) * var(--ux-scale-typography, 1)); }
}

/* ============================================================
   Visual Polish — Details from old blog SCSS
   Added on top of preview CSS base
   ============================================================ */

/* --- Header: shadows + sticky z-index fix --- */
.header {
    box-shadow: 0 3px 8px 0 oklch(0% 0 0 / 40%);
}

/* Sticky headlines should go BEHIND the header */
.sticky-headline {
    z-index: 1;
}

/* --- Sidebar: box-shadow floating effect --- */
@media (min-width: 60em) {
    .page-content {
        border-left: 1px solid var(--seam-color);
        box-shadow: -8px 0 0 0 var(--tint-color);
        /* Der Text sass 1 px neben der Linie -- also genau auf ihr, denn das
           eine Pixel IST der Rand. Als Innenabstand, nicht als Aussenabstand:
           die Linie haengt an diesem Element, ein margin haette sie mitgenommen
           statt Luft zu schaffen. Gilt erst ab 60em, weil es die Linie darunter
           gar nicht gibt -- dort stapelt .mid-section die Spalten. */
        padding-inline-start: 1.5em;
    }
}

/* --- Footer: top shadow --- */
.footer {
    box-shadow: 0 -3px 8px 0 oklch(0% 0 0 / 30%);
}

/* --- Nice Box: centered nav items --- */
.main-menu {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1;
}
.main-menu .nice-box {
    display: flex;
    gap: 0;
}
/* 28.08.2026: `button.nice-box` ergaenzt. Der Zahnrad-Knopf IST selbst
   eine nice-box statt in einer zu sitzen -- der Nachfahren-Kombinator
   meint aber nie das Element selbst. Er fiel damit durch genau die Regel,
   die fuer ihn geschrieben wurde, und trug nur die 9px der .nice-box:
   28px breit bei 52px Hoehe, waehrend die Nav-Links 16.8px Polster haben.
   Ole: "der sieht gerade ein wenig gedrungen aus". */
.main-menu .nice-box a,
.main-menu .nice-box button,
.main-menu button.nice-box {
    padding: 0.5em 1.2em;
}

button[data-open-settings] {
    background: transparent;
    border: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

/* --- Options: centered, not side-aligned --- */
.options {
    display: flex;
    align-items: center;
    gap: 0.5em;
}
.config-link {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.5em;
    height: 2.5em;
    border-radius: 50%;
    background: var(--tint-color);
    color: var(--contrast-color);
    font-size: 1.2em;
    transition: background 0.2s;
}
.config-link:hover {
    background: var(--accent-color);
}

/* --- Tag Cloud: more padding, visible count badges --- */
/* Das Polster gilt fuer BEIDE Chip-Orte -- sonst waere die Karte wieder die
   Ausnahme, nur diesmal 1,2 px schmaler statt in Versalien. Der margin bleibt
   der Leiste vorbehalten: die Karten stehen in einem Flex mit `gap: 0.4em`,
   dort kaeme er obendrauf. (07.08.2026, Design-Runde 3, Befund 6.) */
.tag-cloud a,
.teaser-footer a {
    padding: 0.3em 0.6em;
}
.tag-cloud a {
    margin: 0.2em;
}
.tag-cloud a span {
    min-width: 1.4em;
    height: 1.4em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 27.08.2026 stillgelegt: Dublette zur Regel im @layer components,
       die dort --ux-scale-typography beruecksichtigt. Ungelayertes CSS schlaegt
       jeden Layer, dadurch war der Schriftgroessen-Regler hier wirkungslos. */
    /* font-size: 0.75em; */
    border-radius: 50%;
    background: oklch(40% 0.02 250);
    color: oklch(90% 0.02 90);
    margin-left: 0.3em;
}

/* --- Pie Menu: Ruby/Sapphire/Emerald slices, BG-blend trigger --- */
:root {
    --pie-ruby-from: oklch(55% 0.24 20);
    --pie-ruby-to: oklch(25% 0.12 20);
    --pie-sapphire-from: oklch(45% 0.20 265);
    --pie-sapphire-to: oklch(20% 0.10 265);
    --pie-emerald-from: oklch(50% 0.19 150);
    --pie-emerald-to: oklch(22% 0.10 150);
}

/* Pie slice colors: Ruby / Sapphire / Emerald */
.pie--parts a:first-child {
    background: radial-gradient(at right top, var(--pie-ruby-from) 0%, var(--pie-ruby-from) 33%, var(--pie-ruby-to) 100%);
    z-index: 10000;
}
.pie--parts a:nth-child(2) {
    background: radial-gradient(at right top, var(--pie-sapphire-from) 0%, var(--pie-sapphire-from) 33%, var(--pie-sapphire-to) 100%);
    z-index: 20000;
}
.pie--parts a:nth-child(3) {
    background: radial-gradient(at right top, var(--pie-emerald-from) 0%, var(--pie-emerald-from) 33%, var(--pie-emerald-to) 100%);
    z-index: 30000;
}

/* --- Author Cards: constrain to sidebar --- */
.author-card {
    max-width: 100%;
    overflow: hidden;
}
.author-shape {
    max-width: 100%;
}
.author-shape-content {
    max-width: 100%;
}

/* --- Submenu: handled by @layer above --- */

/* --- Link elements: ♦ diamond + dotted leader ---
   Die Punktlinie lag als border-bottom unter dem ganzen Element und lief
   damit auch unter dem Eintrag durch. Im alten Blog fuehrte sie wie in
   einem Inhaltsverzeichnis vom Punkt BIS zum Eintrag und hoerte dort auf.
   Umgesetzt als drittes Flex-Element zwischen beiden: order sortiert
   ::before (Punkt), ::after (Linie) und den Text, flex:1 laesst die Linie
   genau den Zwischenraum fuellen. */
.link-element {
    border-bottom: 0;
    align-items: baseline;
}
.link-element::before {
    content: "\2666";
    order: -2;
    color: var(--burst-color);
    flex-shrink: 0;
}
.link-element::after {
    content: "";
    order: -1;
    flex: 1 1 auto;
    min-width: 1em;
    margin: 0 0.5em;
    border-bottom: 1px dotted var(--leader-color);
}

/* Die Kategorien bekommen dieselbe Zeile (06.08.2026).
 *
 * Vorher fing zwar beides bei 12 px an -- gemessen, die Kaesten fluchteten --,
 * aber "Neuste" schob seinen Text hinter die Raute, waehrend die Kategorien
 * direkt an der Kante begannen. Dadurch sah die untere Haelfte der Leiste
 * nach links verrutscht aus, obwohl die Aussenabstaende gleich waren.
 * Der alte Kommentar bei .sub-segment-subject nennt genau das als Absicht:
 * fluchten mit dem ♦ statt mit dessen Text. Das war die falsche Bezugslinie.
 *
 * Sie erben deshalb Punkt, Punktlinie und rechtsbuendigen Text. Was sie
 * behalten: ihre Kategoriefarbe und das hoehere Schriftgewicht -- der Punkt
 * traegt wie oben die Akzentfarbe, damit die Spalte der Rauten eine Linie
 * bildet und nicht fuenf verschiedene Farben zeigt.
 */
.sub-segment-subject {
    display: flex;
    justify-content: flex-end;
    align-items: baseline;
    border-bottom: 0;
    /* Rechts klebten sie am Rahmen: .link-element hat mit margin: 0 0.75rem
       auf BEIDEN Seiten Luft, .sub-segment-subject nur links
       (margin: 0.35rem 0 0 0.75rem). Als Innenabstand statt Aussenabstand,
       damit die Fuehrungslinie an derselben Stelle endet wie oben -- das
       Ergebnis ist dasselbe: der Text hoert 0.75rem vor dem Rahmen auf. */
    padding-inline-end: 0.75rem;
}
.sub-segment-subject::before {
    content: "\2666";
    order: -2;
    color: var(--burst-color);
    flex-shrink: 0;
}
.sub-segment-subject::after {
    content: "";
    order: -1;
    flex: 1 1 auto;
    min-width: 1em;
    margin: 0 0.5em;
    border-bottom: 1px dotted var(--leader-color);
}

/* --- Light theme surfaces (outside @layer, same reason as the copper frame) ---
 *
 * These four used to sit inside @layer tokens next to the light token block,
 * where they were dead: @layer components paints .footer's background and the
 * declared order is "... tokens, layout, components, utilities", so components
 * won regardless of specificity. The dark footer then dragged two accents under
 * AA in light mode (burst 3.31, muted 2.73) because the text was re-lit for a
 * light surface that never arrived.
 *
 * Out here they land after every layer and actually apply.
 * Both selectors again: the attribute is what the settings modal writes, the
 * radio is the legacy server-rendered mirror.
 */
html[data-theme="light"] .header,
#light:checked ~ #grid-frame .header {
    /* War oklch(98% 0.01 90) und las sich als Weiss statt als helles Creme:
       die Kopfleiste war die EINZIGE Hell-Modus-Flaeche mit halber Saettigung.
       Teaser-Karten, Fusszeile und Autorenkarten stehen auf 0.02, der
       Seitengrund ebenfalls -- bei 0.01 faellt sie aus der Farbfamilie heraus.
       97% statt 98%, damit sie die hellste Flaeche bleibt, ohne auszubleichen.
       (Ole, 07.08.2026: „im light mode ist die Head nav weiss".) */
    background-color: oklch(97% 0.02 90);
    border-bottom-color: oklch(85% 0.02 80);
}
html[data-theme="light"] .teaser-card,
#light:checked ~ #grid-frame .teaser-card {
    background: oklch(92% 0.02 90);
}
html[data-theme="light"] .footer,
#light:checked ~ #grid-frame .footer {
    background: oklch(92% 0.02 80);
    border-top-color: var(--burst-color);
}
html[data-theme="light"] .author-card,
#light:checked ~ #grid-frame .author-card {
    background: oklch(92% 0.02 90);
}

/* Area headings carry literal colours tuned for the dark surface, so no token
 * can reach them. Same hue, lightness solved for the light background. */
html[data-theme="light"] .area-intro--blog h1 {
    color: oklch(45% 0.06 55);
}
html[data-theme="light"] .area-intro--blog .area-intro-tagline {
    color: oklch(42% 0.04 55);
}
html[data-theme="light"] .area-intro--games h1 {
    color: oklch(42% 0.1 165);
}
html[data-theme="light"] .area-intro--games .area-intro-tagline {
    color: oklch(40% 0.06 165);
}
html[data-theme="light"] .area-intro--mirrorworld h1 {
    color: oklch(43% 0.11 285);
}
html[data-theme="light"] .area-intro--mirrorworld .area-intro-tagline {
    color: oklch(41% 0.07 285);
}

/* Footer text was written for the dark band. Now that the band turns light in
 * this theme, its own greys had to come down with it: the links measured
 * 1.74:1 against the light surface. Solved values were 51%; taken to 48% so
 * antialiasing cannot nudge them back under 4.5. */
html[data-theme="light"] .footer--copy__date {
    color: oklch(48% 0.02 250);
}
html[data-theme="light"] .footer--link {
    color: oklch(48% 0.03 250);
}

/* burst on --neutral-color is a tighter pairing than burst on the page, so the
 * shared accent lands at 3.88 here. Only this chip needs the extra step down. */
html[data-theme="light"] .author-promote {
    color: oklch(45% 0.18 60);
}

/* --- Sidebar copper frame (outside @layer for highest priority) --- */
.menu-element {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 100%;
    padding: 0.5rem 0 0;
    border-radius: 2px;
}
.copper-border {
    isolation: isolate;
    position: relative;
    border-radius: 2px;
}
.copper-border > * {
    position: relative;
    z-index: 2;
}
/* Layer 0: Copper gradient frame (2px larger than element) */
.copper-border::before {
    position: absolute;
    display: block;
    content: "";
    z-index: 0;
    inset: -2px;
    background-image: var(--copper-gradient);
    background-size: 100% 300%;
    background-position: top;
    animation: copper-shimmer linear 12s alternate infinite;
    box-shadow: 5px 5px 15px 5px oklch(0% 0 0 / 40%);
    border-radius: 2px;
}
/* Layer 1: Opaque cover (same size as element, hides gradient center) */
.copper-border::after {
    position: absolute;
    display: block;
    content: "";
    z-index: 1;
    inset: 0;
    background-color: var(--base-color);
    border-radius: 1px;
}

/* ============================================================
   Area Intro Sections — Blog vs Games differentiation
   ============================================================ */

.area-intro {
    padding: 3rem 1.5rem 2.5rem;
    margin-bottom: 2rem;
    border-bottom: 1px solid var(--border-color, oklch(30% 0.02 260));
    position: relative;
    overflow: hidden;
}

/* Hier sass bis zum 07.08.2026 `.area-intro::before` mit `inset: 0` und
   `opacity: 0.06` -- das Bereichs-Kolorit als Auflage auf dem Intro-Block.
   Es ist nach #grid-frame gewandert (siehe --area-wash dort): als
   Hintergrund-Interesse gedacht, hatte es an einen Block gebunden eine harte
   Kante, die 24 px neben der Spaltenlinie eine zweite senkrechte Kante ergab.

   ⚠️ Der Fund war schwer zu messen und kostete einen falschen Fix: Ein
   Pseudo-Element taucht weder in `elementsFromPoint` noch in
   `getComputedStyle(el)` auf -- man muss `getComputedStyle(el, '::before')`
   ausdruecklich fragen. Das Abtasten meldete deshalb korrekt „kein Element mit
   Hintergrund" und war trotzdem irrefuehrend.

   `position: relative` und `overflow: hidden` an .area-intro bleiben: daran
   haengt die z-index-Staffelung von .area-intro-content. */

.area-intro-content {
    position: relative;
    z-index: 1;
    max-width: 42rem;
}

/* Der schreibende Drache ist das Logo des Blogs -- er steht im Seitenkopf
   neben Titel und Taglines, nicht auf den Beitragskarten. */
.area-intro--blog {
    display: flex;
    align-items: center;
    gap: 1.75rem;
}

.area-intro-logo {
    width: 120px;
    height: auto;
    flex-shrink: 0;
    position: relative;
    z-index: 1;
    filter: drop-shadow(0 4px 18px oklch(45% 0.14 150 / 55%));
}

@media (max-width: 40rem) {
    .area-intro--blog {
        gap: 1rem;
    }
    .area-intro-logo {
        width: 76px;
    }
}

.area-intro h1 {
    font-size: clamp(1.8rem, 4vw, 2.8rem);
    font-weight: 800;
    letter-spacing: -0.02em;
    margin-bottom: 0.5rem;
    line-height: 1.1;
}

.area-intro-tagline {
    font-size: clamp(1rem, 2vw, 1.2rem);
    line-height: 1.5;
    margin-bottom: 0.5rem;
}

.area-intro-sub {
    font-size: 0.875rem;
    opacity: 0.6;
    font-style: italic;
}

/* Blog Area — warm, editorial feel */
.area-intro--blog {
    border-bottom-color: oklch(50% 0.08 55);
}

/* Bereichs-Kolorit -- der eigentliche Hintergrund-Schein der Seite.
   Gesetzt am body, gelesen von #grid-frame (erbt). Die Klasse traegt nur die
   Uebersichtsseite (`page-list area-*`); Beitrag und Reihe haben sie nicht --
   dort gab es den Intro-Block und damit das Kolorit auch vorher nicht.

   Deckkraft 0.20, nicht die frueheren 0.06. Der alte Aufbau kaschierte, wie
   schwach der Wert ist: Auf dem Intro-Block (740 x 233 px) reichten 0.06 fuer
   einen sichtbaren Effekt, auf der ganzen Seite (1425 x 3372 px) verteilt sich
   dieselbe Farbe auf das Zwanzigfache der Flaeche und verschwindet. Beim
   Verschieben einer Dekoration auf eine groessere Ebene muss die Intensitaet
   mitwandern, sonst verliert man sie unbemerkt -- genau das war beim ersten
   Anlauf passiert (Ole: „jetzt sehe ich aber kein radial-gradient mehr").

   Geometrie: Ursprung bei 75% liegt ueber der dritten Spalte, nicht mittig.
   Der Radius ist knapp genug, dass der Schein die linke Spaltenkante nicht
   erreicht -- sichtbar bis x=428 bei einer Kante auf x=308 (1425 px Fenster).
   `radial-gradient`, nicht linear: es soll den Grund interessanter machen,
   nicht eine Richtung behaupten. */
body.area-blog {
    --area-wash: radial-gradient(60% 55% at 75% -100px, oklch(50% 0.12 55 / var(--wash-alpha)) 0%, transparent 75%);
}

.area-intro--blog h1 {
    color: oklch(85% 0.06 55);
}

.area-intro--blog .area-intro-tagline {
    color: oklch(75% 0.04 55);
}

/* Games Area — cool, technical feel */
.area-intro--games {
    border-bottom-color: oklch(50% 0.12 165);
}

body.area-games {
    --area-wash: radial-gradient(60% 55% at 75% -100px, oklch(45% 0.15 165 / var(--wash-alpha)) 0%, oklch(35% 0.1 220 / calc(var(--wash-alpha) * 0.6)) 45%, transparent 75%);
}

.area-intro--games h1 {
    color: oklch(80% 0.1 165);
}

.area-intro--games .area-intro-tagline {
    color: oklch(72% 0.06 165);
}

/* Mirrorworld Area — Kristall-Violett. Blog liegt auf Farbton 55, Games auf 165;
   285 ist von beiden weit genug weg, um den Bereich am Kolorit zu erkennen. */
.area-intro--mirrorworld {
    border-bottom-color: oklch(50% 0.12 285);
}

body.area-mirrorworld {
    --area-wash: radial-gradient(60% 55% at 75% -100px, oklch(45% 0.14 285 / var(--wash-alpha)) 0%, oklch(38% 0.1 255 / calc(var(--wash-alpha) * 0.6)) 45%, transparent 75%);
}

.area-intro--mirrorworld h1 {
    color: oklch(82% 0.09 285);
}

.area-intro--mirrorworld .area-intro-tagline {
    color: oklch(74% 0.06 285);
}

/* Teaser card area variants */
.teaser-card--games .teaser-pre {
    border-left-color: oklch(50% 0.12 165);
}

/* Filter info bar */
.filter-info {
    padding: 0.5rem 1rem;
    font-size: 0.875rem;
    color: var(--muted-text, oklch(65% 0.01 250));
    margin-bottom: 1rem;
}

.filter-clear {
    margin-left: 0.5rem;
    opacity: 0.6;
    font-size: 0.8rem;
}

.filter-clear:hover {
    opacity: 1;
}

/* Empty area state */
.empty-area {
    text-align: center;
    padding: 4rem 1rem;
    color: var(--muted-text, oklch(65% 0.01 250));
}

.empty-area p {
    font-size: 1.1rem;
    margin-bottom: 0.5rem;
}

.empty-area-hint {
    /* 27.08.2026 stillgelegt: Dublette zur Regel im @layer components,
       die dort --ux-scale-typography beruecksichtigt. Ungelayertes CSS schlaegt
       jeden Layer, dadurch war der Schriftgroessen-Regler hier wirkungslos. */
    /* font-size: 0.875rem; */
    opacity: 0.6;
    font-style: italic;
}

/* ============================================================
   Landing Page — www.dulldaydudes.*
   ============================================================ */

.landing {
    display: flex;
    flex-direction: column;
    align-items: center;
    position: relative;
    padding: 4rem 1.5rem 3rem;
    min-height: 60vh;
}

.landing-hero {
    text-align: center;
    margin-bottom: 3rem;
    max-width: 36rem;
    position: relative;
    z-index: 1;
}



.landing-title {
    font-size: clamp(2.4rem, 6vw, 4rem);
    font-weight: 900;
    letter-spacing: -0.03em;
    line-height: 1;
    margin-bottom: 0.75rem;
    background: linear-gradient(135deg, oklch(80% 0.08 55), oklch(75% 0.1 165));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.landing-tagline {
    font-size: clamp(1.1rem, 2.5vw, 1.4rem);
    color: var(--text-color, oklch(85% 0.01 80));
    margin-bottom: 0.5rem;
}

.landing-quote {
    font-size: 0.875rem;
    opacity: 0.45;
}

/* Hex halftone — base-color hexagons let the radial gradient shine through gaps */
.landing-hex {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    pointer-events: none;
    z-index: 0;
}

.landing-hex .hex {
    fill: var(--base-color, oklch(25% 0.02 250));
}

@media (prefers-reduced-motion: reduce) {
    .landing-hex { display: none; }
}

.landing-areas {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1.5rem;
    width: 100%;
    /* 18.08.2026 von 44rem auf 62rem: bei 44rem passten nur zwei Spalten, die
       drei Karten standen als 2+1 mit einer Luecke rechts unten. 62rem traegt
       genau drei (3x16rem + 2x1.5rem Abstand = 51rem Minimum, eine vierte
       braeuchte 68.5rem) und fluchtet mit der Werk-Sektion darunter.
       min(): .landing ist ein Flex-Container ohne eigene Breitenvorgabe, deshalb
       richtet sich das Raster nach seinem Inhalt und nicht nach dem Fenster —
       ein reines max-width: 62rem erzwingt drei Spalten auch auf dem Handy und
       schiebt die Seite seitlich raus (gemessen: 816 px bei 405 px Fenster). */
    max-width: min(62rem, 100vw - 3rem);
}

.landing-card {
    display: block;
    padding: 2rem 1.5rem;
    border: 1px solid var(--border-color, oklch(30% 0.02 260));
    border-radius: 0.75rem;
    text-decoration: none;
    color: inherit;
    transition: border-color 0.2s, transform 0.2s, box-shadow 0.2s;
    position: relative;
    overflow: hidden;
}

.landing-card::before {
    content: "";
    position: absolute;
    inset: 0;
    opacity: 0.04;
    pointer-events: none;
    transition: opacity 0.2s;
}

.landing-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 20px oklch(0% 0 0 / 0.3);
}

.landing-card:hover::before {
    opacity: 0.08;
}

.landing-card-icon {
    font-size: 2rem;
    margin-bottom: 0.75rem;
    line-height: 1;
}

.landing-card h2 {
    font-size: 1.4rem;
    font-weight: 700;
    margin-bottom: 0.5rem;
}

.landing-card p {
    font-size: 0.9rem;
    opacity: 0.7;
    line-height: 1.5;
}

/* Blog card — warm */
.landing-card--blog {
    border-color: oklch(40% 0.06 55);
}

.landing-card--blog::before {
    background: linear-gradient(135deg, oklch(50% 0.12 55), transparent);
}

.landing-card--blog:hover {
    border-color: oklch(55% 0.1 55);
}

.landing-card--blog h2 {
    color: oklch(80% 0.06 55);
}

/* Games card — cool */
.landing-card--games {
    border-color: oklch(40% 0.08 165);
}

.landing-card--games::before {
    background: linear-gradient(135deg, oklch(45% 0.15 165), oklch(35% 0.1 220));
}

.landing-card--games:hover {
    border-color: oklch(55% 0.12 165);
}

.landing-card--games h2 {
    color: oklch(75% 0.1 165);
}

/* Mirrorworld card — der Farbton kommt aus dem Bestand, nicht neu erfunden:
   --category-world-building steht auf 305, und Mirrorworld IST der Weltenbau.
   Damit unterscheidet sie sich zugleich klar von Blog (55) und Games (165). */
.landing-card--mirrorworld {
    border-color: oklch(40% 0.1 305);
}

.landing-card--mirrorworld::before {
    background: linear-gradient(135deg, oklch(45% 0.18 305), transparent);
}

.landing-card--mirrorworld:hover {
    border-color: oklch(55% 0.14 305);
}

.landing-card--mirrorworld h2 {
    color: oklch(75% 0.12 305);
}

/* ------------------------------------------------------------
   Landing-Kacheln im Hell-Modus (06.08.2026).

   ⚠️ Alle drei Ueberschriften fielen dort durch -- Blog 1.64,
   Dev Diaries 1.84, Mirrorworld 2.00. Fest verdrahtete, fuer
   Dunkel abgestimmte Farben ohne Hell-Gegenstueck; derselbe
   Fehler wie bei den Bereichs-Ueberschriften heute frueh.

   Die Landingpage war bei den bisherigen Kontrastlaeufen nie
   dabei -- gemessen wurden Blog-Start und Beitragsseite.

   Farbton und Saettigung bleiben, nur die Helligkeit wandert.
   Geloest waeren 53.5 / 52 / 54.5 gewesen; etwas darunter, damit
   Kantenglaettung es nicht wieder unter 4.5 drueckt.
   ------------------------------------------------------------ */
html[data-theme="light"] .landing-card--blog h2 {
    color: oklch(50% 0.06 55);
}

html[data-theme="light"] .landing-card--games h2 {
    color: oklch(49% 0.1 165);
}

html[data-theme="light"] .landing-card--mirrorworld h2 {
    color: oklch(51% 0.12 305);
}

/* ============================================================
   Werk-Sektionen auf der Landingpage — „Gebaut — laeuft anderswo"
   (18.08.2026)

   Bewusst UNGELAYERT wie die Game-Bloecke darunter: die zweite,
   gleichlautende Fassung dieser Block-Regeln steht in
   @layer components (ab ~3828) und wuerde sonst gewinnen.

   Die Bildbloecke selbst sind die Bestandsklassen der Spiele-Seiten
   (.game-block--text-image / .game-cta-btn). Neu ist nur der
   SubHero-Kopf und die Breiten-Angleichung.
   ============================================================ */

.works {
    position: relative;
    z-index: 1;
    width: 100%;
    /* Dieselbe Deckelung wie bei .landing-areas, aus demselben Grund — die
       3rem sind das seitliche Polster von .landing. */
    max-width: min(62rem, 100vw - 3rem);
    margin-top: 4rem;
}

.works-head {
    border-top: 1px solid oklch(from var(--seam-color, oklch(55% 0.02 260)) l c h / 30%);
    padding-top: 1.75rem;
}

.works-head h2 {
    font-size: 1.35rem;
    font-weight: 800;
    margin-bottom: 0.35rem;
}

.works-head p {
    font-size: 0.92rem;
    opacity: 0.62;
    max-width: 40rem;
    line-height: 1.55;
}

.work-section {
    padding: 2.75rem 0;
    border-top: 1px solid oklch(from var(--seam-color, oklch(55% 0.02 260)) l c h / 22%);
}

.work-section:first-of-type {
    border-top: 0;
    padding-top: 2rem;
}

.work-subhero {
    margin-bottom: 1.5rem;
}

.work-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.75rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    opacity: 0.7;
    margin-bottom: 0.55rem;
}

.work-dot {
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    flex: none;
}

/* Zustand des Projekts — gruen laeuft, gelb ist noch nicht fertig. */
.work-dot--live { background: oklch(70% 0.17 150); }
.work-dot--beta { background: oklch(75% 0.15 85); }

.work-subhero h3 {
    font-size: clamp(1.6rem, 1.1rem + 1.6vw, 2.2rem);
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.15;
    margin-bottom: 0.4rem;
}

.work-lead {
    font-size: 1.05rem;
    opacity: 0.72;
    max-width: 42rem;
    line-height: 1.55;
}

.work-btn-row {
    margin-top: 1.25rem;
}

/* Der Bestandsblock zentriert sich auf 48rem (siehe .game-block-content weiter
   oben) und stuende dadurch schmaler als der SubHero-Kopf darueber. In der
   Werk-Sektion fluchten beide Kanten. Ebenso oben buendig statt vertikal
   zentriert, weil die Texte hier kuerzer sind als das Bild hoch ist. */
.work-section .game-block--text-image {
    padding: 0;
}

/* Zwei Klassen tief reicht hier NICHT: .game-block--text-image .game-block-content
   (weiter unten, Zeile ~2581) hat dieselbe Spezifitaet und steht spaeter in der
   Datei — bei Gleichstand gewinnt die spaetere. Deshalb drei Klassen. Gemessen
   am 18.08.: mit zwei Klassen blieb align-items auf center und die Spalten auf
   480px/480px, nur max-width griff (dort ist der Gegner eine Klasse schwaecher). */
.work-section .game-block--text-image .game-block-content {
    max-width: none;
    align-items: start;
    grid-template-columns: 1.05fr 0.95fr;
}

.work-section .game-text-image-media img {
    border: 1px solid oklch(from var(--seam-color, oklch(55% 0.02 260)) l c h / 35%);
    max-width: 100%;
    height: auto;
}

/* Grid-Spalten haben als Mindestbreite die Breite ihres Inhalts (1fr = minmax(auto, 1fr)).
   Ohne min-width: 0 zieht das 960 px breite Vorschaubild die Spalte auf und die Seite
   laeuft seitlich ueber. Gemessen am 18.08. bei 420 px Fensterbreite: scrollWidth 816 statt
   405. ⚠️ Die Seite laeuft dort auch OHNE diese Sektion ueber (live 584) — Header, Footer
   und .landing-areas sind zu breit. Das ist ein aelterer, eigener Befund. */
.work-section .game-block--text-image .game-block-content > * {
    min-width: 0;
}

/* Farbton je Projekt. NICHT die Hausfarben der Projekte: die von
   „Muss Mama fragen" (150) laege auf der Games-Karte (165), die von
   in-hochfeld (60) auf der Blog-Karte (55). Gewaehlt mit Abstand zum
   Bestand — die Reihe lautet 25 · 55 · 95 · 165 · 260 · 305. */
.work-section--mutter   .work-subhero h3 { color: oklch(76% 0.11 25); }
.work-section--hochfeld .work-subhero h3 { color: oklch(78% 0.10 95); }
.work-section--bgc      .work-subhero h3 { color: oklch(76% 0.11 260); }

.work-section--mutter   .work-eyebrow { color: oklch(70% 0.08 25); }
.work-section--hochfeld .work-eyebrow { color: oklch(72% 0.08 95); }
.work-section--bgc      .work-eyebrow { color: oklch(70% 0.08 260); }

/* Heller Modus: gleiches Vorgehen wie bei den Landing-Karten weiter oben —
   Farbton und Saettigung bleiben, nur die Helligkeit wandert unter den Wert,
   ab dem der Kontrast 4.5 reisst. */
html[data-theme="light"] .work-section--mutter   .work-subhero h3 { color: oklch(48% 0.13 25); }
html[data-theme="light"] .work-section--hochfeld .work-subhero h3 { color: oklch(47% 0.10 95); }
html[data-theme="light"] .work-section--bgc      .work-subhero h3 { color: oklch(48% 0.14 260); }

html[data-theme="light"] .work-section--mutter   .work-eyebrow { color: oklch(45% 0.10 25); }
html[data-theme="light"] .work-section--hochfeld .work-eyebrow { color: oklch(44% 0.08 95); }
html[data-theme="light"] .work-section--bgc      .work-eyebrow { color: oklch(45% 0.11 260); }

@media (max-width: 48rem) {
    .work-section .game-block--text-image .game-block-content {
        grid-template-columns: 1fr;
    }

    .work-section {
        padding: 2rem 0;
    }
}

/* ============================================================
   Games Startpage + Detail Page
   ============================================================ */

.games-startpage {
    max-width: 60rem;
    margin: 0 auto;
    padding: 0 1rem;
}

.games-hero-header {
    position: relative;
    text-align: center;
    padding: 6rem 2rem 5rem;
    min-height: 33vh;
    overflow: hidden;
    /* Break out of parent max-width for full-bleed background */
    width: 100vw;
    margin-left: calc(50% - 50vw);
    display: flex;
    align-items: center;
    justify-content: center;
}

.games-hero-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    z-index: 0;
    /* 27.08.2026: Die harte Unterkante war die Kante des Canvas selbst - der Hero
       setzt kein eigenes background. Statt einen Verlauf in der Seitenfarbe
       darueberzulegen (die je Theme anders ist), blenden wir den Canvas aus;
       was durchscheint, ist immer der echte Seitengrund. */
    -webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
}

.games-hero-header__content {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 2rem;
    max-width: 48rem;
}

.games-hero-logo {
    width: 160px;
    height: 160px;
    flex-shrink: 0;
    filter: drop-shadow(0 4px 20px oklch(30% 0.15 260 / 60%));
}

.games-hero-header__text {
    text-align: left;
}

.games-hero-header h1 {
    /* 27.08.2026 stillgelegt: Dublette zur Regel im @layer components,
       die dort --ux-scale-typography beruecksichtigt. Ungelayertes CSS schlaegt
       jeden Layer, dadurch war der Schriftgroessen-Regler hier wirkungslos. */
    /* font-size: clamp(2rem, 4vw, 3.2rem); */
    font-weight: 800;
    letter-spacing: -0.02em;
    margin-bottom: 0.5rem;
    color: var(--burst-color, oklch(78% 0.18 60));
    text-shadow: 0 0 30px oklch(78% 0.18 60 / 30%), 0 2px 4px oklch(0% 0 0 / 40%);
}

.games-hero-header p {
    /* 27.08.2026 stillgelegt: Dublette zur Regel im @layer components,
       die dort --ux-scale-typography beruecksichtigt. Ungelayertes CSS schlaegt
       jeden Layer, dadurch war der Schriftgroessen-Regler hier wirkungslos. */
    /* font-size: 1.1rem; */
    color: oklch(75% 0.04 200);
    text-shadow: 0 1px 3px oklch(0% 0 0 / 50%);
}

/* Game Section — one per game */
.game-section {
    margin-bottom: 3rem;
    padding-bottom: 3rem;
    border-bottom: 1px solid var(--seam-color, oklch(25% 0.04 150));
}

.game-section:last-child {
    border-bottom: none;
}

/* Generic block */
.game-block {
    margin-bottom: 2rem;
}

.game-block:last-child {
    margin-bottom: 0;
}

.game-block-content {
    max-width: 48rem;
    margin: 0 auto;
}

/* Hero block — full-width breakout */
.game-block--hero {
    position: relative;
    min-height: 45vh;
    display: flex;
    align-items: flex-end;
    padding: 3rem 2rem;
    overflow: hidden;
    background-color: var(--neutral-color, oklch(25% 0.05 50));
    background-size: cover;
    background-position: center;
    /* Break out of parent max-width */
    width: 100vw;
    margin-left: calc(50% - 50vw);
}

/* Blur edges: strong blur at top and bottom of hero image */
.game-block--hero::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    pointer-events: none;
    mask-image: linear-gradient(
        to bottom,
        black 0%, transparent 5vh,
        transparent calc(100% - 5vh),
        black 100%
    );
    -webkit-mask-image: linear-gradient(
        to bottom,
        black 0%, transparent 5vh,
        transparent calc(100% - 5vh),
        black 100%
    );
}

/* Vignette: gradient to background color so edges dissolve completely */
.game-block--hero::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        /* Bottom: dissolve into page background */
        linear-gradient(to top,
            var(--base-color, oklch(25% 0.02 250)) 0%,
            oklch(from var(--base-color, oklch(25% 0.02 250)) l c h / 80%) 8%,
            oklch(from var(--base-color, oklch(25% 0.02 250)) l c h / 40%) 25%,
            transparent 55%
        ),
        /* Top: dissolve into page background */
        linear-gradient(to bottom,
            var(--base-color, oklch(25% 0.02 250)) 0%,
            oklch(from var(--base-color, oklch(25% 0.02 250)) l c h / 70%) 8%,
            oklch(from var(--base-color, oklch(25% 0.02 250)) l c h / 30%) 20%,
            transparent 40%
        ),
        /* Left edge */
        linear-gradient(to right,
            oklch(from var(--base-color, oklch(25% 0.02 250)) l c h / 60%) 0%,
            transparent 15%
        ),
        /* Right edge */
        linear-gradient(to left,
            oklch(from var(--base-color, oklch(25% 0.02 250)) l c h / 60%) 0%,
            transparent 15%
        );
    pointer-events: none;
    z-index: 1;
}

.game-block--hero .game-block-content {
    position: relative;
    z-index: 2;
    max-width: 60rem;
}

.game-hero-title {
    /* 27.08.2026 stillgelegt: Dublette zur Regel im @layer components,
       die dort --ux-scale-typography beruecksichtigt. Ungelayertes CSS schlaegt
       jeden Layer, dadurch war der Schriftgroessen-Regler hier wirkungslos. */
    /* font-size: clamp(2rem, 1.5rem + 3vw, 4rem); */
    font-weight: 700;
    color: var(--color-cornsilk, oklch(96% 0.04 90));
    margin-bottom: 0.5rem;
    /* Cremeweiss auf hellem Bild: "Alley Racer" ging in seinem Pastellmotiv
       fast unter (Rundgang 06.08.2026, D7). Der alte, einzelne Schatten
       0 2px 8px / 50% verschwimmt ueber hellen Motiven, statt zu tragen --
       weiche Schatten brauchen einen dunklen Untergrund, um zu wirken.
       Jetzt drei Lagen: eine enge, fast deckende zeichnet die Kante nach und
       traegt die Lesbarkeit; die beiden weiten geben die Tiefe wie bisher.
       Bewusst kein zusaetzlicher Schleier ueber dem Bild -- ::before und
       ::after sind am Hero-Block schon vergeben, ein dritter Layer waere
       Gefrickel, und ein Schleier verdeckt Bildinhalt, den der Schatten
       stehen laesst.
       ⚠️ Diese Regel steht ungelayert und schlaegt damit die gleichnamige in
       @layer components (Zeile ~3523); geaendert werden muss also diese. */
    text-shadow:
        0 1px 2px oklch(0% 0 0 / 85%),
        0 2px 8px oklch(0% 0 0 / 55%),
        0 0 24px oklch(0% 0 0 / 35%);
}

.game-hero-link {
    text-decoration: none;
    color: inherit;
    display: block;
}

.game-hero-link .game-hero-title {
    transition: color 0.15s ease-out;
}

.game-hero-link:hover .game-hero-title,
.game-hero-link:focus-visible .game-hero-title {
    color: var(--burst-color, oklch(78% 0.18 60));
}

.game-hero-link:hover .game-hero-subtitle {
    color: var(--color-cornsilk, oklch(96% 0.04 90));
}

.game-hero-subtitle {
    /* 27.08.2026 stillgelegt: Dublette zur Regel im @layer components,
       die dort --ux-scale-typography beruecksichtigt. Ungelayertes CSS schlaegt
       jeden Layer, dadurch war der Schriftgroessen-Regler hier wirkungslos. */
    /* font-size: clamp(1rem, 0.8rem + 1vw, 1.5rem); */
    color: oklch(96% 0.04 90 / 80%);
    text-shadow: 0 1px 4px oklch(0% 0 0 / 40%);
}

/* Text + Image block */
.game-block--text-image {
    padding: 2rem 0;
}

.game-block--text-image .game-block-content {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2rem;
    align-items: center;
}

.game-block--image-left .game-block-content {
    direction: rtl;
}

.game-block--image-left .game-block-content > * {
    direction: ltr;
}

.game-text-image-text h3 {
    /* 27.08.2026 stillgelegt: Dublette zur Regel im @layer components,
       die dort --ux-scale-typography beruecksichtigt. Ungelayertes CSS schlaegt
       jeden Layer, dadurch war der Schriftgroessen-Regler hier wirkungslos. */
    /* font-size: 1.25rem; */
    font-weight: 600;
    margin-bottom: 0.75rem;
}

.game-text-image-body {
    line-height: 1.75;
    color: var(--muted-color, oklch(55% 0.02 250));
}

.game-text-image-media img {
    border-radius: 0.5rem;
    width: 100%;
    height: auto;
    box-shadow: 0 4px 16px oklch(0% 0 0 / 20%);
}

/* Description block */
.game-block--description {
    padding: 1.5rem 0;
}

.game-block--description h3 {
    /* 27.08.2026 stillgelegt: Dublette zur Regel im @layer components,
       die dort --ux-scale-typography beruecksichtigt. Ungelayertes CSS schlaegt
       jeden Layer, dadurch war der Schriftgroessen-Regler hier wirkungslos. */
    /* font-size: 1.25rem; */
    font-weight: 600;
    margin-bottom: 0.75rem;
}

.game-description-body {
    line-height: 1.75;
    color: var(--muted-color, oklch(55% 0.02 250));
}

.game-description-body p,
.game-text-image-body p {
    margin-block-end: 1.5rem;
}

.game-description-body p:last-child,
.game-text-image-body p:last-child {
    margin-block-end: 0;
}

/* CTA block */
.game-block--cta {
    text-align: center;
    padding: 3rem 1rem;
    margin-top: 1.5rem;
    background-color: oklch(from var(--neutral-color, oklch(25% 0.05 50)) l c h / 60%);
    border-radius: 1rem;
}

.game-block--cta h3 {
    /* 27.08.2026 stillgelegt: Dublette zur Regel im @layer components,
       die dort --ux-scale-typography beruecksichtigt. Ungelayertes CSS schlaegt
       jeden Layer, dadurch war der Schriftgroessen-Regler hier wirkungslos. */
    /* font-size: 1.25rem; */
    font-weight: 600;
    margin-bottom: 0.5rem;
}

.game-cta-body {
    color: var(--muted-color, oklch(55% 0.02 250));
    margin-bottom: 1rem;
}

.game-cta-btn {
    display: inline-block;
    padding: 0.75rem 2rem;
    background-color: var(--accent-color, oklch(60% 0.12 170));
    color: var(--abyss-color, oklch(15% 0.02 250));
    text-decoration: none;
    font-weight: 600;
    border-radius: 0.5rem;
    transition: background-color 0.15s ease-out, transform 0.15s ease-out;
}

.game-cta-btn:hover {
    background-color: var(--burst-color, oklch(78% 0.18 60));
    transform: translateY(-1px);
}

.game-cta-btn--secondary {
    background-color: transparent;
    color: var(--accent-color, oklch(60% 0.12 170));
    border: 1px solid var(--accent-color, oklch(60% 0.12 170));
    margin-left: 0.75rem;
}

.game-cta-btn--secondary:hover {
    background-color: oklch(from var(--accent-color, oklch(60% 0.12 170)) l c h / 15%);
    color: var(--burst-color, oklch(78% 0.18 60));
    border-color: var(--burst-color, oklch(78% 0.18 60));
}

/* Game Detail Page */
.game-detail {
    max-width: 60rem;
    margin: 0 auto;
    padding: 1.5rem 1rem;
}

.game-detail .game-block--hero {
    min-height: 55vh;
}

.game-detail__empty {
    text-align: center;
    padding: 6rem 1rem;
}

.game-detail__empty h1 {
    /* 27.08.2026 stillgelegt: Dublette zur Regel im @layer components,
       die dort --ux-scale-typography beruecksichtigt. Ungelayertes CSS schlaegt
       jeden Layer, dadurch war der Schriftgroessen-Regler hier wirkungslos. */
    /* font-size: clamp(1.5rem, 1rem + 2.5vw, 3rem); */
    font-weight: 700;
    margin-bottom: 1rem;
}

.game-detail__nav {
    display: flex;
    justify-content: space-between;
    padding: 1.5rem 0;
    margin-top: 2rem;
    border-top: 1px solid var(--seam-color, oklch(25% 0.04 150));
}

.game-detail__nav a {
    color: var(--accent-color, oklch(60% 0.12 170));
    text-decoration: none;
    transition: color 0.15s ease-out;
}

.game-detail__nav a:hover {
    color: var(--burst-color, oklch(78% 0.18 60));
}

/* Empty area */
.empty-area {
    text-align: center;
    padding: 6rem 1rem;
    color: var(--muted-color, oklch(55% 0.02 250));
}

.empty-area-hint {
    color: var(--muted-color, oklch(55% 0.02 250));
    margin-top: 0.5rem;
}

/* Responsive: stack text+image on mobile */
@media (max-width: 48em) {
    .game-block--hero {
        min-height: 16rem;
        padding: 2rem 1rem;
    }

    .game-detail .game-block--hero {
        min-height: 20rem;
    }

    .game-block--text-image .game-block-content {
        grid-template-columns: 1fr;
    }

    .game-block--image-left .game-block-content {
        direction: ltr;
    }
}

@media (prefers-reduced-motion: reduce) {
    .game-cta-btn:hover {
        transform: none;
    }
}

/* ============================================================
   Devlog Page
   ============================================================ */

.devlog-page {
    max-width: 60rem;
    margin: 0 auto;
    padding: 0 1rem;
}

.devlog-header {
    padding: 2rem 0;
    margin-bottom: 1.5rem;
    border-bottom: 1px solid oklch(35% 0.06 165 / 0.3);
}

.devlog-back {
    font-size: 0.85rem;
    opacity: 0.6;
    margin-bottom: 0.5rem;
    display: inline-block;
}

.devlog-back:hover {
    opacity: 1;
}

.devlog-header h1 {
    font-size: clamp(1.4rem, 3vw, 2rem);
    font-weight: 700;
    color: oklch(80% 0.08 165);
    margin-top: 0.5rem;
}

.devlog-description {
    font-size: 0.95rem;
    opacity: 0.6;
    margin-top: 0.5rem;
}

/* ===========================================
 * Settings Modal
 * =========================================== */

.settings-modal__overlay {
    position: fixed;
    inset: 0;
    background: oklch(0% 0 0 / 0.6);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 9999;
    backdrop-filter: blur(4px);
}

.settings-modal {
    background: var(--surface-raised);
    border: 1px solid var(--surface-border);
    border-radius: 0.75rem;
    width: min(90vw, 520px);
    max-height: 85vh;
    overflow-y: auto;
    box-shadow: 0 25px 50px oklch(0% 0 0 / 0.4);
}

.settings-modal__header {
    display: flex;
    justify-content: flex-end;
    padding: 0.75rem 1rem 0;
}

.settings-modal__close {
    background: transparent;
    border: none;
    color: var(--muted-color);
    cursor: pointer;
    padding: 0.25rem;
    border-radius: 0.25rem;
    transition: color 0.15s;
}

.settings-modal__close:hover {
    color: var(--contrast-color);
}

.settings-modal__body {
    padding: 0 1.5rem 1.5rem;
}

/* Settings Panel (shared between modal and standalone) */

.settings-panel__title {
    font-size: 1.5rem;
    font-weight: 700;
    margin-bottom: 0.25rem;
}

.settings-panel__desc {
    color: var(--muted-color);
    font-size: 0.875rem;
    margin-bottom: 1.5rem;
}

.settings-group {
    border: none;
    padding: 0;
    margin-bottom: 1.5rem;
}

.settings-group__title {
    font-size: 0.95rem;
    font-weight: 600;
    margin-bottom: 0.5rem;
}

.settings-group__body {
    border: 1px solid var(--surface-border);
    border-radius: 0.5rem;
    padding: 0.75rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.settings-toggle,
.settings-cycle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.375rem 0;
    font-size: 0.875rem;
}

.settings-switch {
    position: relative;
    display: inline-flex;
    width: 2.75rem;
    height: 1.5rem;
    border-radius: 9999px;
    border: 2px solid transparent;
    background: var(--switch-track);
    cursor: pointer;
    transition: background 0.2s;
    flex-shrink: 0;
}

.settings-switch--on {
    /* --burst-color statt eines eigenen Werts: die Markenfarbe gilt in beiden
       Themen gleich und ist damit die einzige Stelle, die sie kennen muss. */
    background: var(--burst-color);
}

.settings-switch__thumb {
    display: block;
    width: 1.125rem;
    height: 1.125rem;
    border-radius: 9999px;
    background: var(--switch-thumb);
    box-shadow: 0 1px 3px oklch(0% 0 0 / 0.3);
    transform: translateX(0.125rem);
    transition: transform 0.2s;
}

.settings-switch--on .settings-switch__thumb {
    transform: translateX(1.375rem);
}

.settings-cycle__controls {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.settings-cycle__controls button {
    background: transparent;
    border: none;
    padding: 0.25rem 0.5rem;
    cursor: pointer;
    color: var(--muted-color);
    border-radius: 0.25rem;
    transition: color 0.15s, background 0.15s;
}

.settings-cycle__controls button:hover {
    color: var(--contrast-color);
    background: var(--tint-color);
}

.settings-cycle__value {
    min-width: 7rem;
    text-align: center;
    font-weight: 500;
    font-size: 0.85rem;
}

.settings-panel__reset {
    border-top: 1px solid var(--surface-border);
    padding-top: 1rem;
}

.settings-reset-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    border: 1px solid var(--surface-border);
    border-radius: 0.5rem;
    background: transparent;
    color: var(--muted-color);
    cursor: pointer;
    font-size: 0.85rem;
    transition: color 0.15s, border-color 0.15s;
}

.settings-reset-btn:hover {
    color: var(--contrast-color);
    border-color: var(--contrast-color);
}

/* ============================================================
   Asset Picker
   ============================================================ */

.asset-modal-overlay {
    position: fixed;
    inset: 0;
    background: oklch(0% 0 0 / 70%);
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem;
}

.asset-modal {
    background: var(--base-color, oklch(20% 0.01 250));
    border: 1px solid var(--seam-color, oklch(25% 0.04 150));
    border-radius: 1rem;
    max-width: 64rem;
    width: 100%;
    max-height: 85vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.asset-modal__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 1.5rem;
    border-bottom: 1px solid var(--seam-color, oklch(25% 0.04 150));
}

.asset-modal__header h2 {
    font-size: 1.25rem;
    font-weight: 600;
    margin: 0;
}

.asset-modal__close {
    background: none;
    border: none;
    font-size: 1.5rem;
    color: var(--muted-color, oklch(55% 0.02 250));
    cursor: pointer;
    padding: 0.25rem 0.5rem;
}

.asset-modal__close:hover {
    color: var(--contrast-color, oklch(90% 0 0));
}

.asset-modal__error {
    padding: 0.75rem 1.5rem;
    background: oklch(40% 0.15 25);
    color: oklch(90% 0.05 25);
    font-size: 0.875rem;
}

.asset-modal__body {
    display: flex;
    flex: 1;
    overflow: hidden;
    min-height: 0;
}

/* Upload Dropzone */
.asset-dropzone {
    padding: 1rem 1.5rem;
    text-align: center;
    color: var(--muted-color, oklch(55% 0.02 250));
    font-size: 0.875rem;
    cursor: pointer;
    border-bottom: 1px solid var(--seam-color, oklch(25% 0.04 150));
    transition: background 0.15s, color 0.15s;
}

.asset-dropzone:hover,
.asset-dropzone--active {
    background: oklch(from var(--accent-color, oklch(60% 0.12 170)) l c h / 10%);
    color: var(--accent-color, oklch(60% 0.12 170));
}

.asset-dropzone--uploading {
    opacity: 0.6;
    pointer-events: none;
}

/* Thumbnail Grid */
.asset-grid {
    flex: 1;
    overflow-y: auto;
    padding: 1rem;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
    gap: 0.75rem;
    align-content: start;
}

.asset-grid__loading,
.asset-grid__empty {
    grid-column: 1 / -1;
    text-align: center;
    color: var(--muted-color, oklch(55% 0.02 250));
    padding: 3rem 1rem;
}

.asset-card {
    aspect-ratio: 1;
    border-radius: 0.5rem;
    overflow: hidden;
    border: 2px solid transparent;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    background: var(--neutral-color, oklch(25% 0.05 50));
    transition: border-color 0.15s;
    padding: 0;
}

.asset-card:hover {
    border-color: var(--muted-color, oklch(55% 0.02 250));
}

.asset-card--selected {
    border-color: var(--accent-color, oklch(60% 0.12 170));
}

.asset-card__img {
    flex: 1;
    width: 100%;
    object-fit: cover;
    min-height: 0;
}

.asset-card__name {
    display: block;
    font-size: 0.65rem;
    padding: 0.25rem 0.4rem;
    color: var(--muted-color, oklch(55% 0.02 250));
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Metadata Sidebar */
.asset-meta {
    width: 18rem;
    border-left: 1px solid var(--seam-color, oklch(25% 0.04 150));
    padding: 1rem;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.asset-meta__preview {
    border-radius: 0.5rem;
    overflow: hidden;
    aspect-ratio: 16 / 9;
}

.asset-meta__preview img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.asset-meta__info {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-size: 0.75rem;
    color: var(--muted-color, oklch(55% 0.02 250));
}

.asset-meta__color {
    display: inline-block;
    padding: 0.1rem 0.4rem;
    border-radius: 0.25rem;
    font-family: monospace;
    font-size: 0.7rem;
}

.asset-meta__field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.asset-meta__field span {
    font-size: 0.75rem;
    color: var(--muted-color, oklch(55% 0.02 250));
}

.asset-meta__field input {
    background: var(--neutral-color, oklch(25% 0.05 50));
    border: 1px solid var(--seam-color, oklch(25% 0.04 150));
    border-radius: 0.375rem;
    padding: 0.4rem 0.6rem;
    color: var(--contrast-color, oklch(90% 0 0));
    font-size: 0.85rem;
}

.asset-meta__check {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
}

.asset-meta__check input[type="checkbox"] {
    appearance: auto;
    position: static;
    opacity: 1;
    pointer-events: auto;
    width: 1rem;
    height: 1rem;
}

.asset-meta__actions {
    display: flex;
    gap: 0.5rem;
    margin-top: auto;
    padding-top: 0.75rem;
    border-top: 1px solid var(--seam-color, oklch(25% 0.04 150));
}

/* Asset Buttons */
.asset-btn {
    padding: 0.5rem 1rem;
    border-radius: 0.375rem;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid var(--seam-color, oklch(25% 0.04 150));
    background: var(--neutral-color, oklch(25% 0.05 50));
    color: var(--contrast-color, oklch(90% 0 0));
    transition: background 0.15s;
}

.asset-btn:hover {
    background: oklch(from var(--accent-color, oklch(60% 0.12 170)) l c h / 15%);
}

.asset-btn--primary {
    background: var(--accent-color, oklch(60% 0.12 170));
    color: var(--abyss-color, oklch(15% 0.02 250));
    border-color: var(--accent-color, oklch(60% 0.12 170));
    flex: 1;
}

.asset-btn--primary:hover {
    background: var(--burst-color, oklch(78% 0.18 60));
}

.asset-btn--danger {
    color: oklch(70% 0.15 25);
    border-color: oklch(40% 0.1 25);
}

.asset-btn--danger:hover {
    background: oklch(30% 0.08 25);
}

/* Asset Picker (Button in Forms) */
.asset-picker {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.asset-picker__label {
    font-size: 0.85rem;
    font-weight: 600;
}

.asset-picker__choose {
    padding: 1.5rem;
    border: 2px dashed var(--seam-color, oklch(25% 0.04 150));
    border-radius: 0.5rem;
    background: transparent;
    color: var(--muted-color, oklch(55% 0.02 250));
    cursor: pointer;
    font-size: 0.9rem;
    transition: border-color 0.15s, color 0.15s;
}

.asset-picker__choose:hover {
    border-color: var(--accent-color, oklch(60% 0.12 170));
    color: var(--accent-color, oklch(60% 0.12 170));
}

.asset-picker__preview {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
}

.asset-picker__thumb {
    width: 8rem;
    height: 5rem;
    object-fit: cover;
    border-radius: 0.375rem;
    border: 1px solid var(--seam-color, oklch(25% 0.04 150));
}

.asset-picker__actions {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

/* Figure Rendering */
.asset-figure {
    margin: 0;
}

.asset-figure img {
    width: 100%;
    height: auto;
    border-radius: 0.5rem;
}

.asset-figure figcaption {
    font-size: 0.8rem;
    color: var(--muted-color, oklch(55% 0.02 250));
    padding: 0.5rem 0;
    text-align: center;
}

/* ============================================================
   Milkdown WYSIWYG Editor (Block content)
   ============================================================ */

.milkdown-wrapper {
    border: 1px solid var(--seam-color, oklch(25% 0.04 150));
    border-radius: 0.5rem;
    overflow: hidden;
    min-height: 8rem;
}

.milkdown-wrapper .milkdown {
    background: var(--neutral-color, oklch(25% 0.05 50));
    color: var(--contrast-color, oklch(90% 0 0));
}

.milkdown-wrapper .editor {
    padding: 0.75rem 1rem;
    min-height: 6rem;
    outline: none;
    font-size: 0.9rem;
    line-height: 1.7;
}

.milkdown-wrapper .editor p {
    margin-bottom: 0.5rem;
}

.milkdown-wrapper .editor h1,
.milkdown-wrapper .editor h2,
.milkdown-wrapper .editor h3 {
    font-weight: 600;
    margin: 0.75rem 0 0.5rem;
}

.milkdown-wrapper .editor strong {
    font-weight: 700;
    color: var(--contrast-color, oklch(90% 0 0));
}

.milkdown-wrapper .editor em {
    font-style: italic;
}

.milkdown-wrapper .editor code {
    background: oklch(20% 0.01 250);
    padding: 0.1rem 0.3rem;
    border-radius: 0.2rem;
    font-size: 0.85em;
}

.milkdown-wrapper .editor ul,
.milkdown-wrapper .editor ol {
    padding-left: 1.5rem;
    margin-bottom: 0.5rem;
    list-style: disc;
}

.milkdown-wrapper .editor ol {
    list-style: decimal;
}

.milkdown-wrapper .editor a {
    color: var(--accent-color, oklch(60% 0.12 170));
    text-decoration: underline;
}

.milkdown-wrapper .editor blockquote {
    border-left: 3px solid var(--accent-color, oklch(60% 0.12 170));
    padding-left: 1rem;
    margin: 0.5rem 0;
    color: var(--muted-color, oklch(55% 0.02 250));
}

.milkdown-wrapper .editor hr {
    border: none;
    border-top: 1px solid var(--seam-color, oklch(25% 0.04 150));
    margin: 1rem 0;
    /* Im Eingabefeld bleibt es bei der schlichten Linie. Border und
       Margin holt sich dieser Selektor ohnehin ueber die hoehere
       Spezifitaet zurueck -- Hintergrund und Hoehe muss er dagegen
       ausdruecklich zuruecknehmen, weil die Ornament-Regel sie setzt
       und die generische hr-Regel sie dort gar nicht anfasst. */
    background: none;
    height: auto;
}

.milkdown-wrapper .editor:focus-within {
    box-shadow: inset 0 0 0 1px var(--accent-color, oklch(60% 0.12 170));
}

/* Markdown Editor Toolbar */
.md-toolbar {
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 0.35rem 0.5rem;
    background: var(--tint-color, oklch(30% 0.02 250));
    border: 1px solid var(--seam-color, oklch(40% 0.02 250));
    border-bottom: none;
    border-radius: 0.375rem 0.375rem 0 0;
}

.md-toolbar + .milkdown .editor,
.md-toolbar + div .editor {
    border-radius: 0 0 0.375rem 0.375rem;
}

.md-toolbar__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.75rem;
    height: 1.75rem;
    padding: 0 0.35rem;
    border: none;
    border-radius: 0.25rem;
    background: transparent;
    color: var(--muted-color, oklch(60% 0.02 250));
    font-size: 0.8rem;
    font-family: var(--ff-body, monospace);
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
}

.md-toolbar__btn:hover {
    background: var(--neutral-color, oklch(35% 0.02 250));
    color: var(--contrast-color, oklch(90% 0.02 90));
}

.md-toolbar__btn:active {
    background: var(--accent-color, oklch(60% 0.12 170));
    color: var(--abyss-color, oklch(15% 0.02 250));
}

.md-toolbar__btn--bold { font-weight: 800; }
.md-toolbar__btn--italic { font-style: italic; }
.md-toolbar__btn--code { font-size: 0.7rem; letter-spacing: -0.05em; }

.md-toolbar__divider {
    width: 1px;
    height: 1.2rem;
    background: var(--seam-color, oklch(40% 0.02 250));
    margin: 0 0.25rem;
}

/* Block Editor Toggle (Intern/Extern) */
.block-editor__toggle-row {
    display: flex;
    gap: 0;
    border: 1px solid var(--seam-color, oklch(40% 0.02 250));
    border-radius: 0.375rem;
    overflow: hidden;
    width: fit-content;
}

.block-editor__toggle-btn {
    padding: 0.35rem 1rem;
    border: none;
    background: transparent;
    color: var(--muted-color, oklch(60% 0.02 250));
    font-size: 0.85rem;
    font-family: var(--ff-body, monospace);
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
}

.block-editor__toggle-btn + .block-editor__toggle-btn {
    border-left: 1px solid var(--seam-color, oklch(40% 0.02 250));
}

.block-editor__toggle-btn--active {
    background: var(--accent-color, oklch(60% 0.12 170));
    color: var(--abyss-color, oklch(15% 0.02 250));
}

.block-editor__toggle-btn:hover:not(.block-editor__toggle-btn--active) {
    background: var(--neutral-color, oklch(35% 0.02 250));
    color: var(--contrast-color, oklch(90% 0.02 90));
}

/* ============================================================
   Block-Styles — aus assets/css/blocks/ in dieses Standalone-
   Buendel uebernommen (05.08.2026).
   Sie fehlten hier komplett: assets/css/main.css zieht sie per
   @import, aber ausgeliefert wird diese Datei. Folge waren
   voellig ungestylte Beitragsseiten -- Ueberschriften wie
   Fliesstext, Code-Bloecke ohne Rahmen und ohne Hervorhebung.
   Alle 53 benoetigten Variablen sind hier bereits definiert,
   und alle Dateien liegen in @layer components.
   ============================================================ */

/* --- block-text.css --- */
/**
 * Block: Text — rendered text block styling
 */

@layer components {
  .block-text {
    margin-block: var(--spacing-400);
  }

  .block-text p:last-child {
    margin-bottom: 0;
  }

  .block-text a {
    color: var(--accent-color);
    text-decoration-color: oklch(from var(--accent-color) l c h / 40%);
    text-underline-offset: 0.2em;
  }

  .block-text a:hover {
    color: var(--burst-color);
    text-decoration-color: var(--burst-color);
  }

  /* Section dividers between text blocks */
  .block-text + .block-text {
    padding-top: var(--spacing-400);
    border-top: 1px solid oklch(from var(--seam-color) l c h / 30%);
  }
}

/* --- block-image.css --- */
/**
 * Block: Image — responsive images with popout support
 */

@layer components {
  .block-image {
    margin-block: var(--spacing-500);
  }

  .block-image img {
    width: 100%;
    height: auto;
    border-radius: var(--radius-md);
  }

  .block-image figcaption {
    margin-top: var(--spacing-200);
    font-size: var(--font-size-001);
    color: var(--muted-color);
    text-align: center;
  }

  /* Popout image — slightly wider than content */
  .block-image.prose-popout {
    grid-column: popout;
  }

  /* Breakout image — much wider */
  .block-image.prose-breakout {
    grid-column: breakout;
  }

  /* Full-width image — edge to edge */
  .block-image.prose-full-width {
    grid-column: full-width;
  }

  .block-image.prose-full-width img {
    border-radius: 0;
  }
}

/* --- block-code.css --- */
/**
 * Block: Code — syntax highlighted code blocks
 *
 * Uses OKSolar palette for syntax highlighting colors,
 * consistent across light and dark themes via solarized tokens.
 */

@layer components {
  .block-code {
    margin-block: var(--spacing-500);
    position: relative;
  }

  .block-code pre {
    font-family: var(--font-family-code);
    font-variation-settings: var(--fvs-mono);
    font-size: calc(var(--font-size-300) * var(--ux-scale-typography, 1));
    line-height: 1.6;
    padding: var(--spacing-400);
    background-color: var(--abyss-color);
    color: var(--contrast-color);
    border-radius: var(--radius-md);
    overflow-x: auto;
    tab-size: 4;
    -moz-tab-size: 4;
  }

  .block-code code {
    font-size: inherit;
    padding: 0;
    background: none;
    border-radius: 0;
  }

  /* Language label */
  .block-code[data-language]::before {
    content: attr(data-language);
    position: absolute;
    top: 0;
    right: 0;
    padding: var(--spacing-100) var(--spacing-300);
    font-size: var(--font-size-001);
    font-family: var(--font-family-code);
    color: var(--muted-color);
    background-color: oklch(from var(--abyss-color) calc(l * 1.5) c h);
    border-radius: 0 var(--radius-md) 0 var(--radius-sm);
    text-transform: uppercase;
    letter-spacing: 0.05em;
  }

  /* Syntax highlighting tokens (using OKSolar palette) */
  .block-code .token-keyword   { color: var(--solarized-green); }
  .block-code .token-string    { color: var(--solarized-cyan); }
  .block-code .token-number    { color: var(--solarized-magenta); }
  .block-code .token-comment   { color: var(--solarized-base01); font-style: italic; }
  .block-code .token-function  { color: var(--solarized-blue); }
  .block-code .token-variable  { color: var(--solarized-orange); }
  .block-code .token-type      { color: var(--solarized-yellow); }
  .block-code .token-operator  { color: var(--solarized-red); }
  .block-code .token-punctuation { color: var(--solarized-base0); }

  /* Breakout code blocks for wide code */
  .block-code.prose-breakout {
    grid-column: breakout;
  }
}

/* --- block-quote.css --- */
/**
 * Block: Quote — blockquote styling with accent border
 */

@layer components {
  /* Umgebaut am 06.08.2026 nach Oles Entwurf. Vorher: 4px-Balken links,
     getoenter Grund, abgerundete Ecken. Jetzt Anfuehrungszeichen plus Linie
     oben und -- wenn ein Urheber da ist -- spiegelbildlich unten.
     Balken und Linienrahmen zusammen waeren zwei Konzepte fuer dieselbe
     Aufgabe gewesen, deshalb ist der Balken entfallen. */
  .block-quote {
    margin-block: var(--spacing-500);
    padding: 0;
  }

  .block-quote blockquote {
    margin: 0;
    padding: 0;
    border: none;
  }

  /* Die beiden Zeilen mit Zeichen und Linie. Die Linie ist ein Pseudo-Element
     mit flex: 1 -- sie fuellt genau den Rest der Zeile, egal wie breit der
     Urhebername ist. Dieselbe Mechanik wie bei der Fuehrungslinie im
     Seitenmenue. */
  .block-quote__marke,
  .block-quote__urheber {
    display: flex;
    align-items: center;
    gap: 0 var(--spacing-400, 1rem);
    color: var(--accent-color);
  }

  .block-quote__marke::after,
  .block-quote__urheber::before {
    content: "";
    display: block;
    flex: 1;
    height: 3px;
    background-color: var(--seam-color);
  }

  .block-quote__marke {
    margin-bottom: var(--spacing-300, 0.75rem);
  }

  .block-quote__urheber {
    margin-top: var(--spacing-300, 0.75rem);
  }

  /* Ein schliessendes Anfuehrungszeichen zeigt in die andere Richtung.
     Gedreht statt ein zweites SVG zu laden. */
  .block-quote__zeichen--schliessend {
    display: flex;
    transform: rotate(180deg);
  }

  .block-quote blockquote p {
    margin: 0;
    font-size: calc(var(--font-size-450) * var(--ux-scale-typography, 1));
    line-height: 1.6;
    font-style: italic;
    font-variation-settings: var(--fvs-emphasis);
  }

  /* Der Name steht jetzt IN der unteren Zeile, nicht mehr darunter: kein
     eigener Abstand, kein Blockelement, und der Gedankenstrich davor entfaellt
     -- die Linie links davon uebernimmt seine Aufgabe. */
  .block-quote cite {
    font-size: calc(var(--font-size-200) * var(--ux-scale-typography, 1));
    font-style: normal;
    color: var(--muted-color);
    flex-shrink: 0;
  }

  /* Popout quote — slightly wider */
  .block-quote.prose-popout {
    grid-column: popout;
  }
}

/* --- block-grid.css --- */
/**
 * Block: Grid — multi-column grid block
 */

@layer components {
  .block-grid {
    display: grid;
    grid-template-columns: repeat(var(--cols, 2), 1fr);
    gap: var(--spacing-400);
    margin-block: var(--spacing-500);
  }

  /* Responsive: collapse to single column on small containers */
  @container (max-width: 30em) {
    .block-grid {
      grid-template-columns: 1fr;
    }
  }

  /* Grid items */
  .block-grid > * {
    min-width: 0;
  }
}

/* --- block-sources.css --- */
/**
 * Block: Sources — references and sources list
 */

@layer components {
  .block-sources {
    margin-block: var(--spacing-600) var(--spacing-400);
    padding-top: var(--spacing-400);
    border-top: 2px solid var(--seam-color);
  }

  .block-sources h4,
  .block-sources h3 {
    font-size: calc(var(--font-size-300) * var(--ux-scale-typography, 1));
    font-variation-settings: var(--fvs-bold);
    margin-bottom: var(--spacing-300);
    color: var(--muted-color);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
  }

  .block-sources ol,
  .block-sources ul {
    padding-inline-start: var(--spacing-500);
    font-size: calc(var(--font-size-200) * var(--ux-scale-typography, 1));
  }

  .block-sources li {
    margin-block: var(--spacing-200);
    color: var(--muted-color);
  }

  .block-sources a {
    color: var(--accent-color);
    word-break: break-all;
  }

  .block-sources a:hover {
    color: var(--burst-color);
  }
}

/* --- block-game.css --- */
/**
 * Game Block Styles
 *
 * Styles for game detail pages and game sections on the startpage.
 * Uses D3 design tokens and prose-grid layout throughout.
 *
 * Grid placement:
 *   Hero → prose-full-width (edge to edge)
 *   Text+Image, Description → content (default column)
 *   CTA → prose-breakout
 *   Nav → content (default column)
 */

@layer components {

  /* =========================================================================
     Game Detail Page
     ========================================================================= */

  .game-detail__empty {
    text-align: center;
    padding: var(--spacing-900) var(--spacing-400);
  }

  .game-detail__empty h1 {
    font-size: calc(var(--font-size-700) * var(--ux-scale-typography, 1));
    font-variation-settings: var(--fvs-display);
    margin-bottom: var(--spacing-400);
  }

  .game-detail__nav {
    display: flex;
    justify-content: space-between;
    padding: var(--spacing-500) 0;
    margin-top: var(--spacing-600);
    border-top: 1px solid var(--seam-color);
  }

  .game-detail__nav a {
    color: var(--accent-color);
    text-decoration: none;
    font-size: calc(var(--font-size-200) * var(--ux-scale-typography, 1));
    transition: color var(--motion-duration-fast) var(--motion-ease-out);
  }

  .game-detail__nav a:hover {
    color: var(--burst-color);
  }

  /* =========================================================================
     Game Startpage
     ========================================================================= */

  .games-hero-header {
    text-align: center;
    padding: var(--spacing-700) 0 var(--spacing-600);
  }

  .games-hero-header h1 {
    font-size: calc(var(--font-size-800) * var(--ux-scale-typography, 1));
    font-variation-settings: var(--fvs-display);
    margin-bottom: var(--spacing-300);
  }

  .games-hero-header p {
    font-size: calc(var(--font-size-300) * var(--ux-scale-typography, 1));
    color: var(--muted-color);
  }

  .game-section {
    margin-bottom: var(--spacing-800);
    padding-bottom: var(--spacing-600);
    border-bottom: 1px solid var(--seam-color);
  }

  .game-section:last-child {
    border-bottom: none;
  }

  /* =========================================================================
     Shared: Game Blocks
     ========================================================================= */

  .game-block {
    margin-bottom: var(--spacing-600);
  }

  .game-block:last-child {
    margin-bottom: 0;
  }

  /* =========================================================================
     Block: Hero (prose-full-width)
     ========================================================================= */

  .game-block--hero {
    position: relative;
    min-height: 20rem;
    display: flex;
    align-items: flex-end;
    padding: var(--spacing-700) var(--spacing-500);
    overflow: hidden;
    background-color: var(--neutral-color);
    background-size: cover;
    background-position: center;
  }

  .game-block--hero::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
      to top,
      oklch(0% 0 0 / 80%) 0%,
      oklch(0% 0 0 / 40%) 50%,
      oklch(0% 0 0 / 10%) 100%
    );
    pointer-events: none;
  }

  .game-block--hero .game-block-content {
    position: relative;
    z-index: 1;
    max-width: var(--content-width-wide);
    margin-inline: auto;
    width: 100%;
  }

  .game-detail .game-block--hero {
    min-height: 28rem;
  }

  .game-hero-title {
    font-size: calc(var(--font-size-900) * var(--ux-scale-typography, 1));
    font-variation-settings: var(--fvs-display);
    color: var(--color-cornsilk);
    margin-bottom: var(--spacing-200);
    text-shadow: 0 2px 8px oklch(0% 0 0 / 50%);
  }

  .game-hero-link {
    text-decoration: none;
    color: inherit;
    display: block;
  }

  .game-hero-link .game-hero-title {
    transition: color var(--motion-duration-fast) var(--motion-ease-out);
  }

  .game-hero-link:hover .game-hero-title,
  .game-hero-link:focus-visible .game-hero-title {
    color: var(--burst-color);
  }

  .game-hero-link:hover .game-hero-subtitle {
    color: var(--color-cornsilk);
  }

  .game-hero-subtitle {
    font-size: calc(var(--font-size-400) * var(--ux-scale-typography, 1));
    color: oklch(from var(--color-cornsilk) l c h / 80%);
    font-variation-settings: var(--fvs-casual);
    text-shadow: 0 1px 4px oklch(0% 0 0 / 40%);
  }

  /* =========================================================================
     Block: Text + Image (content column)
     ========================================================================= */

  .game-block--text-image .game-block-content {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--spacing-600);
    align-items: center;
  }

  .game-block--image-left .game-block-content {
    direction: rtl;
  }

  .game-block--image-left .game-block-content > * {
    direction: ltr;
  }

  .game-text-image-text h3 {
    font-size: calc(var(--font-size-500) * var(--ux-scale-typography, 1));
    font-variation-settings: var(--fvs-bold);
    margin-bottom: var(--spacing-300);
  }

  .game-text-image-body {
    font-size: calc(var(--font-size-200) * var(--ux-scale-typography, 1));
    line-height: var(--line-height-relaxed);
    color: var(--muted-color);
  }

  .game-text-image-media img {
    width: 100%;
    border-radius: var(--radius-md);
    box-shadow: var(--card-shadow-md);
  }

  /* =========================================================================
     Block: Description (content column)
     ========================================================================= */

  .game-block--description {
    padding: var(--spacing-500) 0;
  }

  .game-block--description h3 {
    font-size: calc(var(--font-size-500) * var(--ux-scale-typography, 1));
    font-variation-settings: var(--fvs-bold);
    margin-bottom: var(--spacing-300);
  }

  .game-description-body {
    font-size: calc(var(--font-size-200) * var(--ux-scale-typography, 1));
    line-height: var(--line-height-relaxed);
    color: var(--muted-color);
  }

  /* =========================================================================
     Block: Call to Action (prose-breakout)
     ========================================================================= */

  .game-block--cta {
    text-align: center;
    padding: var(--spacing-700) var(--spacing-400);
    margin-top: var(--spacing-500);
    background-color: oklch(from var(--neutral-color) l c h / 60%);
    border-radius: var(--radius-lg);
  }

  .game-block--cta h3 {
    font-size: calc(var(--font-size-500) * var(--ux-scale-typography, 1));
    font-variation-settings: var(--fvs-bold);
    margin-bottom: var(--spacing-200);
  }

  .game-cta-body {
    font-size: calc(var(--font-size-200) * var(--ux-scale-typography, 1));
    color: var(--muted-color);
    margin-bottom: var(--spacing-400);
  }

  .game-cta-btn {
    display: inline-block;
    padding: var(--spacing-300) var(--spacing-600);
    background-color: var(--accent-color);
    color: var(--abyss-color);
    text-decoration: none;
    font-variation-settings: var(--fvs-bold);
    font-size: calc(var(--font-size-200) * var(--ux-scale-typography, 1));
    border-radius: var(--radius-md);
    transition: background-color var(--motion-duration-fast) var(--motion-ease-out),
                transform var(--motion-duration-fast) var(--motion-ease-out);
  }

  .game-cta-btn:hover {
    background-color: var(--burst-color);
    transform: translateY(-1px);
  }

  .game-cta-btn--secondary {
    background-color: transparent;
    color: var(--accent-color);
    border: 1px solid var(--accent-color);
  }

  .game-cta-btn--secondary:hover {
    background-color: oklch(from var(--accent-color) l c h / 15%);
    color: var(--burst-color);
    border-color: var(--burst-color);
  }

  @media (prefers-reduced-motion: reduce) {
    .game-cta-btn:hover {
      transform: none;
    }
  }

  /* =========================================================================
     Empty Area (shared)
     ========================================================================= */

  .empty-area {
    text-align: center;
    padding: var(--spacing-900) var(--spacing-400);
    color: var(--muted-color);
  }

  .empty-area-hint {
    font-size: calc(var(--font-size-200) * var(--ux-scale-typography, 1));
    color: var(--muted-color);
    margin-top: var(--spacing-200);
    font-variation-settings: var(--fvs-casual);
  }

  /* =========================================================================
     Responsive
     ========================================================================= */

  @media (max-width: 48em) {
    .game-block--hero {
      min-height: 16rem;
      padding: var(--spacing-500) var(--spacing-400);
    }

    .game-detail .game-block--hero {
      min-height: 20rem;
    }

    .game-hero-title {
      font-size: calc(var(--font-size-700) * var(--ux-scale-typography, 1));
    }

    .game-block--text-image .game-block-content {
      grid-template-columns: 1fr;
    }

    .game-block--image-left .game-block-content {
      direction: ltr;
    }
  }
}


/* ============================================================
   Design-Tokens fuer die Block-Styles (05.08.2026).
   Gezielt aus assets/css/layers/02-tokens.css uebernommen -- NICHT
   die ganze Datei: 16 ihrer Namen kollidieren mit diesem Buendel
   (--base-color, --abyss-color, alle --category-*), abweichende Werte
   haetten die Farben der gesamten Seite verschoben. Uebernommen wurde
   nur, was block-code.css braucht, samt Abhaengigkeiten; bereits
   vorhandene Namen blieben unberuehrt.
   ============================================================ */
:root {
    --color-cornsilk: oklch(96% 0.04 90);
    --color-slate: oklch(55% 0.02 250);
    --color-raven: oklch(15% 0.02 250);
    --solarized-base01: oklch(53.5% 0.029 219.6);
    --solarized-base0: oklch(71.8% 0.017 198);
    --solarized-yellow: oklch(63.1% 0.129 86.4);
    --solarized-orange: oklch(63.1% 0.166 50.4);
    --solarized-red: oklch(63.1% 0.221 21.6);
    --solarized-magenta: oklch(63.1% 0.205 349.2);
    --solarized-blue: oklch(63.1% 0.141 244.8);
    --solarized-cyan: oklch(63.1% 0.102 187.2);
    --solarized-green: oklch(63.1% 0.148 118.8);
    --spacing-100: 0.25rem;
    --spacing-300: 0.75rem;
    --spacing-400: 1rem;
    --spacing-500: 1.5rem;
    --font-size-001: clamp(0.5rem, 0.2963rem + 0.2625vw, 0.8113rem);
    --font-size-300: clamp(0.8606rem, 0.7755rem + 0.4256vw, 1.2863rem);
    /* 27.08.2026 nachgetragen: diese neun standen nur in
       assets/css/layers/02-tokens.css und fehlten in der gebuendelten Datei.
       Undefiniert macht jede calc()-Deklaration lautlos ungueltig. */
    --font-size-100: clamp(0.6375rem, 0.5758rem + 0.3088vw, 0.9463rem);
    --font-size-200: clamp(0.7406rem, 0.6681rem + 0.3625vw, 1.1031rem);
    --font-size-400: clamp(0.875rem, 0.75rem + 0.625vw, 1.5rem);
    --font-size-450: clamp(0.9375rem, 0.825rem + 0.5625vw, 1.5rem);
    --font-size-500: clamp(1.0169rem, 0.8705rem + 0.7319vw, 1.7488rem);
    --font-size-600: clamp(1.1813rem, 1.0096rem + 0.8581vw, 2.0394rem);
    --font-size-700: clamp(1.3719rem, 1.1706rem + 1.0063vw, 2.3781rem);
    --font-size-800: clamp(1.5944rem, 1.3588rem + 1.1781vw, 2.7725rem);
    --font-size-900: clamp(1.8519rem, 1.5756rem + 1.3813vw, 3.2331rem);
    --font-size-999: clamp(1.9844rem, 1.7346rem + 1.2488vw, 3.2331rem);
    --font-size-base: var(--font-size-400);
    --font-family-code: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo,
      Consolas, "DejaVu Sans Mono", monospace;
    --fvs-mono: "wght" 400, "CASL" 0, "MONO" 1, "slnt" 0, "CRSV" 0;
    --radius-sm: 0.25rem;
    /* 28.08.2026 nachgetragen -- derselbe Fall wie am 27.08., naechste Runde.
       Diese 16 werden in DIESER Datei benutzt, standen aber nur in
       public/css/layers/. Undefiniert verwirft der Browser die ganze
       Deklaration, lautlos. Betraf u.a. den Breiten-Umschalter (alle sechs
       Stufen ergaben dieselbe Breite von 636px) und vier Schriftschnitt-Presets. */
    --spacing-200: 0.5rem;
    --spacing-600: 2rem;
    --spacing-700: 3rem;
    --spacing-800: 4rem;
    --spacing-900: 6rem;
    --line-height-relaxed: 1.75;
    --tracking-wide: 0.025em;
    --radius-lg: 1rem;
    --card-shadow-md: 0 4px 16px oklch(0% 0 0 / 20%), 0 1px 4px oklch(0% 0 0 / 15%);
    --motion-duration-fast: 150ms;
    --motion-ease-out: cubic-bezier(0, 0, 0.2, 1);
    --content-width-wide: 80em;
    --content-width-mobile: 22.5em;
    --content-width-narrow: 45.5em;
    --content-width-normal: 64em;
    --content-width-big: 120em;
    --content-width-full: 100%;
    --content-width: var(--content-width-normal);
    --fvs-bold: "wght" 600, "CASL" 0, "MONO" 0, "slnt" 0, "CRSV" 0;
    --fvs-casual: "wght" 400, "CASL" 1, "MONO" 0, "slnt" 0, "CRSV" 0.5;
    --fvs-display: "wght" 700, "CASL" 0, "MONO" 0, "slnt" 0, "CRSV" 0;
    --fvs-emphasis: "wght" 400, "CASL" 0, "MONO" 0, "slnt" -12, "CRSV" 1;
    --radius-md: 0.5rem;
    --ux-scale-typography: 1.125;
}

/* ============================================================
   Lesebreite fuer Beitragstext (05.08.2026).
   Ohne Begrenzung lief der Fliesstext ueber die volle Inhaltsspalte
   und damit weit ueber eine angenehme Zeilenlaenge.

   Bewusst NICHT das prose-grid aus assets/css/layers/06-layout.css
   uebernommen: dessen `container-type: inline-size` veraendert die
   Groessenberechnung der umgebenden Grids -- beim Versuch schrumpfte
   die .mid-section von 1393px auf 885px und die Inhaltsspalte auf 225px.
   Das gehoert im Zusammenhang gemacht, nicht als Anbau.

   Hier nur das Noetige: Text auf ~70 Zeichen begrenzt, Code und Zitate
   duerfen ueber .prose-breakout weiterhin die volle Breite nutzen.
   ============================================================ */
@layer components {
    .block-text,
    .block-heading {
        /* 28.08.2026: haengt am Breiten-Umschalter ueber eine EIGENE Skala.
           Die --content-width-Stufen sind fuer den Container gedacht (normal =
           64em ≈ 128 Zeichen); als Fliesstextbreite genommen konnte der Schalter
           nur verengen, nie verbreitern -- gemessen wirkte 1 von 6 Stufen.
           --lesebreite denkt in Zeichen und sagt damit, was sie bedeutet. */
        max-width: var(--lesebreite, 68ch);
    }

    .block-code,
    .prose-breakout {
        max-width: 100%;
    }
}

/* ============================================================
   Ueberschriften in Beitraegen (06.08.2026).

   Gemessen am DOM: alle 23 Ueberschriften eines Beitrags rendern wie
   Fliesstext -- 16px, Schriftstaerke 400, kein Abstand nach oben. Ein
   <h2> war von einem Absatz nicht zu unterscheiden, ein <h5> auch nicht.

   ⚠️ Die Notiz "block-heading existiert nirgends als CSS" stimmte nicht
   ganz: die Regel oben gab es, sie setzte nur `max-width` -- Breite ohne
   Typografie. Der Selektor war da, die Gestaltung nie.

   Hier wird nichts erfunden: die Stufen kommen aus der vorhandenen
   Skala (--ft-size-*), im Markup benutzt sind h2 bis h5. Abstand nach
   oben deutlich groesser als nach unten, damit eine Ueberschrift sichtbar
   zum folgenden Absatz gehoert und nicht zum vorigen.
   ============================================================ */
@layer components {
    .block-heading {
        font-weight: 700;
        line-height: 1.25;
        text-wrap: balance;
        margin-block: 1.8em 0.5em;
        color: var(--contrast-color);
    }

    /* Die erste Ueberschrift braucht den grossen Vorlauf nicht -- darueber
       steht die Artikel-h1, nicht ein Absatz. */
    .block-heading:first-child {
        margin-block-start: 0;
    }

    h2.block-heading { font-size: var(--ft-size-800); }
    h3.block-heading { font-size: var(--ft-size-700); }
    h4.block-heading { font-size: var(--ft-size-600); }
    h5.block-heading { font-size: var(--ft-size-500); }

    /* Ab h4 traegt die Groesse allein zu wenig Unterschied, deshalb ein
       zweites Merkmal: die Recursive-Achse CASL macht die Schrift runder
       und setzt die Unterebenen erkennbar ab, ohne sie zu verkleinern.
       Dasselbe Mittel nutzen schon .segment-subject und .footer--subject. */
    h4.block-heading,
    h5.block-heading {
        font-variation-settings: 'CASL' 0.5, 'MONO' 0.2;
        color: var(--muted-color);
    }

    /* ------------------------------------------------------------
       Der Kopf des Beitrags (06.08.2026, Oles Beobachtung:
       "'Die Vor und Nachteile von Traits' ist ja auch noch keine
       Ueberschrift und viel zu klein").

       Gemessen: das <h1> stand auf 16px / Stärke 400 -- Fliesstext, ohne
       jede Klasse. Nach der Gestaltung der Zwischenueberschriften war die
       h2 IM Artikel mit 32px doppelt so gross wie der Titel darueber; die
       Hierarchie stand also auf dem Kopf. --ft-size-900 setzt ihn mit
       ~38px wieder klar an die Spitze.
       ------------------------------------------------------------ */
    .article-title {
        font-size: var(--ft-size-900);
        font-weight: 800;
        line-height: 1.15;
        letter-spacing: -0.02em;
        text-wrap: balance;
        margin-block: 0 0.3em;
        color: var(--contrast-color);
    }

    /* ------------------------------------------------------------
       Serienkarte (06.08.2026).

       Drei Einstiege statt einer Teile-Liste. Gemessen waere die Karte
       mit acht Teilen von 443 auf 630 px gewachsen (+42%) -- und weil
       das Raster auf align-items: stretch steht, haette sie ihre
       Nachbarin in derselben Reihe mitgezogen. Drei Links bleiben drei
       Links, egal wie lang die Reihe wird.
       ------------------------------------------------------------ */
    /* Jedes Feld in seine eigene Rasterzelle. Erst stand .teaser-info auf
       grid-area: title -- solange der Titel einzeilig blieb, fiel nichts auf;
       bei zwei Zeilen lag der Teaser ueber der Ueberschrift. Bei der ersten
       Serie war der Teaser leer und hat den Fehler versteckt. */
    .series-links {
        grid-area: low;
        display: flex;
        flex-direction: column;
        gap: 0.15em;
        margin-top: 0.4em;
    }

    .teaser-card--series .teaser-low-info {
        grid-area: footer;
    }

    .series-link {
        display: inline-flex;
        align-items: baseline;
        gap: 0.4em;
        width: fit-content;
        font-size: var(--ft-size-300);
        color: var(--burst-color);
        text-decoration: none;
        padding: 0.1em 0;
    }

    /* Der Winkel sitzt als Pseudo-Element davor, damit er nicht mitkopiert
       wird, wenn jemand den Linktext markiert. */
    .series-link::before {
        content: '\25B8';
        color: var(--muted-color);
    }

    .series-link:hover,
    .series-link:focus-visible {
        text-decoration: underline;
    }

    .series-count {
        color: var(--muted-color);
    }

    /* Der Anriss bleibt, wo er hingehoert: im description-Feld. */
    .teaser-card--series .teaser-info {
        grid-area: description;
    }

    /* ------------------------------------------------------------
       Serien-Uebersichtsseite
       ------------------------------------------------------------ */
    .series-kicker {
        font-size: var(--ft-size-300);
        text-transform: uppercase;
        letter-spacing: 0.1em;
        color: var(--burst-color);
        margin-bottom: 0.2em;
    }

    .series-ongoing {
        color: var(--accent-color);
    }

    .series-description {
        margin-bottom: 2em;
    }

    .series-parts {
        list-style: none;
        margin: 0;
        padding: 0;
        display: flex;
        flex-direction: column;
        gap: 1.5em;
        max-width: 68ch;
    }

    .series-part {
        display: grid;
        grid-template-columns: 2.5em 1fr;
        gap: 0.8em;
        align-items: start;
    }

    /* Die Nummer traegt die Ordnung sichtbar. aria-hidden im Markup, weil die
       <ol> sie fuer Screenreader ohnehin ansagt -- sonst kaeme sie doppelt.
       ⚠️ Stand erst auf --seam-color und lag damit bei 1.61:1. Das ist eine
       LINIENfarbe; als Textfarbe benutzt ist sie derselbe Fehler, der heute
       frueh schon --muted-color unter AA gedrueckt hatte (Design-Review A1). */
    .series-part__number {
        font-size: var(--ft-size-700);
        font-weight: 800;
        line-height: 1;
        color: var(--muted-color);
        text-align: right;
    }

    .series-part__title {
        font-size: var(--ft-size-600);
        font-weight: 700;
        line-height: 1.25;
        margin: 0 0 0.2em;
    }

    .series-part__title a {
        color: var(--contrast-color);
        text-decoration: none;
    }

    .series-part__title a:hover,
    .series-part__title a:focus-visible {
        text-decoration: underline;
    }

    .series-part__excerpt {
        color: var(--muted-color);
        margin: 0 0 0.3em;
    }

    .series-part__date {
        font-size: var(--ft-size-300);
        color: var(--muted-color);
    }

    /* Die Metazeile soll den Titel tragen, nicht mit ihm konkurrieren:
       etwas Luft darunter und eine feine Trennung zum Text. */
    .article-meta {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        gap: 0.4em 1em;
        padding-bottom: 0.9em;
        border-bottom: 1px solid var(--seam-color);
        margin-block-end: 2em;
    }
}

/* ============================================================
   Luft links vor den Beitragskarten (05.08.2026, Oles Beobachtung).
   Gemessen: zwischen linkem Rand der Inhaltsspalte und Karte lag
   genau 1px -- die Liste klebte an der Kante.
   ============================================================ */
@layer components {
    .teaser-list {
        margin-inline-start: 1.5rem;
    }
}

/* ============================================================
   EXPERIMENT 3 (05.08.2026) — die beiden Konzepte zusammengefuehrt.
   Vorher standen sie gegeneinander: das Farbband codierte die Kategorie
   ueber Farbe, das Panel ueber Schrift; das Panel sass IN einem Kasten,
   statt selbst der Anfang der Karte zu sein; und der Verlauf war auf
   allen Panels derselbe, obwohl jede Kategorie eine eigene Farbe hat.
   Jetzt traegt das Panel beides: Motiv und Kategoriefarbe.
   Vorbild: ~/@DeepThought/Reference/d3-blog-rekonstruktion/
   ============================================================ */
@layer components {
    @font-face {
        font-family: 'capture_it';
        src: url('/fonts/capture_it-webfont.woff2') format('woff2'),
             url('/fonts/capture_it-webfont.woff') format('woff');
        font-weight: normal;
        font-style: normal;
        font-display: swap;
    }

    /* Die Kategoriefarbe hing bisher am Band. Auf der Karte gesetzt, erbt
       sie jeder Nachfahre -- Panel, Schrift und Rahmen koennen sie nutzen. */
    .teaser-card[data-category="quo-vadis"]      { --kategorie-farbe: var(--category-quo-vadis); }
    .teaser-card[data-category="tech-talk"]      { --kategorie-farbe: var(--category-tech-talk); }
    .teaser-card[data-category="con-lang"]       { --kategorie-farbe: var(--category-con-lang); }
    .teaser-card[data-category="world-building"] { --kategorie-farbe: var(--category-world-building); }
    .teaser-card[data-category="indie-game-dev"] { --kategorie-farbe: var(--category-indie-game-dev); }
    /* Siehe die gleichlautende Ergaenzung beim Band weiter oben: Devlog-Beitraege
       tragen den Spielnamen als Kategorie und erben die Indie-Game-Dev-Farbe. */
    .teaser-card[data-category="obscured-by-distance"],
    .teaser-card[data-category="concepts"]       { --kategorie-farbe: var(--category-indie-game-dev); }

    @media (min-width: 62em) {
        .teaser-list {
            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
            gap: 1.5rem;
            /* stretch, nicht start: Karten einer Reihe endeten auf
               verschiedenen Linien, weil jede nur so hoch wurde wie ihr
               Inhalt (Design-Review Punkt B1). Titel laufen zwei- oder
               dreizeilig, Anrisse reichen von 19 bis 182 Zeichen. */
            align-items: stretch;
        }
    }
    @media (min-width: 100em) {
        .teaser-list {
            grid-template-columns: repeat(3, minmax(0, 1fr));
        }
    }

    /* Das Band entfaellt: die Kategorie steht auf dem Panel. */
    .teaser-list .teaser-pre {
        display: none;
    }

    .teaser-list .teaser-body {
        grid-template-columns: 1fr;
        grid-template-areas:
            "image"
            "title"
            "description"
            "low"
            "footer";
        /* Der Anriss bekommt den freien Platz, damit Datum und Tags aller
           Karten auf einer Linie sitzen (Design-Review B1). Das Doc schlug
           margin-top: auto vor -- das ist der Flexbox-Griff; hier ist das
           Grid selbst zustaendig. Die Karte muss dafuer voll durchreichen:
           .teaser-list stretcht sie, .teaser-card gibt die Hoehe weiter. */
        grid-template-rows: auto auto 1fr auto auto;
        height: 100%;
    }

    .teaser-list .teaser-card {
        grid-template-rows: 1fr;
    }

    /* Buendig statt eingerahmt: das Panel bricht aus dem Innenabstand der
       Karte aus und wird oben vom border-radius der Karte mitbeschnitten. */
    .teaser-list .teaser-shape {
        position: relative;
        width: auto;
        height: 11rem;
        margin: -1.5em -1.5em 0.25em;
        border: 0;
        border-radius: 0;
        justify-self: stretch;
        clip-path: polygon(0 0, 100% 0, 100% 88%, 0 100%);
    }

    /* Der Verlauf nimmt jetzt die Kategoriefarbe auf, statt auf allen
       Karten derselbe zu sein. multiply laesst die dunklen Stellen des
       Motivs stehen und faerbt nur die hellen ein. */
    .teaser-list .teaser-shape-content::after {
        background: linear-gradient(
            -45deg,
            var(--kategorie-farbe, oklch(45% 0.16 250)),
            color-mix(in oklab, var(--kategorie-farbe, oklch(45% 0.16 250)) 55%, white),
            var(--kategorie-farbe, oklch(45% 0.16 250))
        );
        mix-blend-mode: multiply;
        opacity: 0.85;
    }

    /* Ohne Motiv traegt die Kategoriefarbe die Flaeche allein. */
    .teaser-list .teaser-shape-content--ohne-bild::after {
        mix-blend-mode: normal;
        opacity: 1;
    }

    /* Weiss auf jeder Kategoriefarbe ging nicht auf: indie-game-dev liegt bei
       oklch(78%), die uebrigen zwischen 45% und 65%. Statt fuenf Sonderfaelle
       zu pflegen, leitet sich die Schriftfarbe aus der Helligkeit ab -- die
       Relative Color Syntax kann die L-Achse lesen. Ab 62% Helligkeit kippt
       clamp() von 1 (weiss) auf 0 (schwarz), der Schatten genau andersherum.
       Eine neue Kategorie bekommt das damit von allein richtig. */
    .teaser-list .teaser-card {
        --panel-schrift: oklch(from var(--kategorie-farbe, oklch(45% 0.16 250))
                               clamp(0, (0.62 - l) * 1000, 1) 0 0);
        --panel-schatten: oklch(from var(--kategorie-farbe, oklch(45% 0.16 250))
                                clamp(0, (l - 0.62) * 1000, 1) 0 0);
    }

    .teaser-list .teaser-shape::after {
        content: attr(data-kategorie);
        position: absolute;
        left: 0.45em;
        bottom: 0.3em;
        z-index: 2;
        font-family: 'capture_it', var(--ff-title), sans-serif;
        font-size: clamp(1.2rem, 2.2vw, 1.9rem);
        line-height: 1;
        text-transform: uppercase;
        color: var(--panel-schrift, #fff);
        text-shadow: 0 2px 10px color-mix(in oklab, var(--panel-schatten, #000) 75%, transparent);
        transform: rotate(-3deg);
        pointer-events: none;
    }

    /* Die Kategoriefarbe darf auch den Rahmen anfassen -- so bleibt die
       Zuordnung erkennbar, wenn zwei Karten dasselbe Motiv tragen. */
    .teaser-list .teaser-card:hover {
        border-color: var(--kategorie-farbe, var(--muted-color));
    }

}

/* ============================================================
   Trennlinie (06.08.2026) — d3_hr.webp statt einer Strichlinie.
   Bewusst AUSSERHALB der @layer-Bloecke: der Block darueber laesst
   @layer components bis zur letzten Zeile offen, ein Anhaengen
   innerhalb waere also gelayert und schwaecher als beabsichtigt.

   Das Bild ist 480x21 und traegt links bei rund 10 % einen V-Knick
   -- ein asymmetrisches Ornament, keine kachelbare Textur. Deshalb
   kein repeat-x: der Knick kaeme sonst alle 480 px wieder.
   Die Hoehe steht fest, die Breite wird gedehnt; in breiten Spalten
   wird der Knick flacher, die Linie bleibt gleich duenn.
   ============================================================ */
hr {
    border: none;
    height: 21px;
    margin-block: var(--spacing-500, 1.5rem);
    background: url('/static/d3_hr.webp') center / 100% 21px no-repeat;
}

/* ============================================================
   Reihen-Navigation am Fuss eines Beitrags (06.08.2026).
   Ebenfalls ausserhalb der @layer-Bloecke, aus demselben Grund
   wie die Trennlinie darueber.
   ============================================================ */
.series-nav {
    margin-block: var(--spacing-500, 1.5rem);
}

.series-nav__intro {
    color: var(--muted-color, oklch(55% 0.02 250));
    margin-bottom: var(--spacing-400, 1rem);
}

/* Der Reihentitel ist ein Link, sah aber aus wie Fliesstext: die gedaempfte
   Farbe der Zeile vererbte sich mit. Ohne diese Regel ist der einzige Hinweis
   auf die Uebersichtsseite unsichtbar. */
.series-nav__intro a {
    color: var(--accent-color, oklch(60% 0.12 170));
}

/* Gemessen statt geschaetzt: --accent-color bringt es im Dunkelmodus gegen den
   dunklen Grund nur auf 4,31 -- unter den geforderten 4,5. Im Hellmodus reicht
   dieselbe Farbe mit 5,34. Deshalb nur hier die Helligkeit anheben, Farbton und
   Saettigung bleiben; eine generelle Anhebung wuerde den Hellmodus
   verschlechtern, wo die Schrift dann heller auf hellem Grund saesse. */
html[data-theme="dark"] .series-nav__intro a {
    color: oklch(from var(--accent-color, oklch(60% 0.12 170)) 0.72 c h);
}

.series-nav__blaettern {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-400, 1rem);
    /* Zurueck bleibt links, Weiter rechts -- auch wenn nur eines von beiden da
       ist. Beim ersten und letzten Teil fehlt genau ein Link, und ohne die
       Regel unten ruecken die Enden an die falsche Seite. */
    justify-content: space-between;
}

.series-nav__link {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    flex: 1 1 14rem;
    padding: var(--spacing-300, 0.75rem) var(--spacing-400, 1rem);
    border: 1px solid var(--seam-color, oklch(25% 0.04 150));
    border-radius: 4px;
    text-decoration: none;
    transition: border-color 0.15s ease-out;
}

/* Fehlt der Vorgaenger, darf der Nachfolger nicht nach links rutschen. */
.series-nav__link--weiter:only-child {
    margin-left: auto;
    text-align: right;
}

.series-nav__link:hover,
.series-nav__link:focus-visible {
    border-color: var(--accent-color, oklch(60% 0.12 170));
}

.series-nav__richtung {
    font-size: 0.85rem;
    /* Textfarbe, keine Linienfarbe: derselbe Fehler wie bei den Teilnummern der
       Serienkarte (4949360). --seam-color ist fuer Raender gedacht und faellt
       als Schrift durch die Kontrastpruefung. */
    color: var(--muted-color, oklch(55% 0.02 250));
}

.series-nav__titel {
    color: var(--contrast-color, oklch(90% 0.02 90));
}

/* ============================================================
   Rechtsseiten (Impressum, Datenschutz) -- 07.08.2026.

   Sie laufen ueber layout/default.phtml und stehen damit NICHT in der
   Blog-Spalte. Die dortige Lesebreite kommt von .block-text (Zeile ~3904)
   und greift hier nicht; .prose gibt es in dieser Datei gar nicht -- das
   prose-grid liegt in assets/css/ und wird nicht ausgeliefert. Beim
   Planschreiben war genau das der Fehler: das Markup nutzte .prose, die
   Seite waere randlos ueber die volle Bildschirmbreite gelaufen.

   Deshalb hier das Noetige und nicht mehr: eine Lesebreite und Luft.
   Ohne @layer, damit die Regeln nicht mit den Layer-Bloecken um die
   Reihenfolge streiten -- unlayered schlaegt jeden Layer.
   ============================================================ */
.legal-page {
    max-width: 68ch;
    margin-inline: auto;
    padding-inline: 1.5em;
    padding-block: 3em 6em;
}

/* Groesse und Gewicht muessen hier ausdruecklich stehen. Tailwinds Preflight
   (Layer `base`) setzt Ueberschriften auf `font-size: inherit; font-weight:
   inherit` -- im Blog holen sie sich beides aus .block-heading, ein blankes
   <h1> ausserhalb dieses Zusammenhangs bleibt sonst 16px/400 und sieht aus
   wie Fliesstext. Gemessen am 07.08.: h1, h2 und p waren alle identisch. */
.legal-page h1 {
    font-size: var(--ft-size-900);
    font-weight: 700;
    line-height: 1.2;
    margin-block-end: 1em;
}

.legal-page h2 {
    font-size: var(--ft-size-700);
    font-weight: 700;
    line-height: 1.3;
    margin-block: 2em 0.5em;
}

.legal-page p {
    margin-block-end: 1em;
    line-height: 1.6;
}

.legal-page a {
    text-decoration: underline;
    text-underline-offset: 0.2em;
}


/* Games-Startseite: Einordnung zwischen Hero und erstem Spiel (27.08.2026).
   Bewusst kurz - Vorbild ist Iceflake, nicht ein Firmenportraet. Steht ausserhalb
   der @layer-Bloecke wie die uebrigen Game-Styles. */
.games-intro {
    max-width: 44rem;
    margin: 0 auto;
    padding: 2.25rem 1.5rem 2.5rem;
    text-align: center;
    font-size: clamp(1rem, 1.6vw, 1.15rem);
    line-height: 1.65;
    color: var(--muted-color, inherit);
    text-wrap: balance;
}

/* Landingpage: Einleitung zwischen Hero und Kacheln (27.08.2026).
   Gleiche Haltung wie .games-intro - ruhig, mittig, Fliesstextgroesse. */
.landing-intro {
    /* .landing-hex liegt als fixed-Overlay mit z-index 0 ueber der Seite;
       Hero und Kacheln heben sich mit position/z-index heraus, dieser
       Absatz muss dasselbe tun, sonst stanzt das Muster die Buchstaben aus. */
    position: relative;
    z-index: 1;
    max-width: 46rem;
    margin: 0 auto 2.75rem;
    padding: 0 1.5rem;
    text-align: center;
    font-size: clamp(1rem, 1.6vw, 1.15rem);
    line-height: 1.65;
    color: var(--muted-color, inherit);
    text-wrap: balance;
}

.landing-intro p + p {
    margin-top: 0.9rem;
}

/* Artikel-Listen (27.08.2026). Der Reset setzt `ul, ol { list-style: none }`,
   und zurueckgesetzt wurde es bisher nur im Backstage-Editor (.milkdown-wrapper).
   Im Blog faellt das kaum auf, in der Enzyklopaedie besteht die Haelfte eines
   Artikels aus Listen -- ohne Zeichen liest sich das wie lauter kurze Absaetze. */
.block-list {
    padding-left: 1.5rem;
    margin-bottom: 1rem;
    list-style: disc;
}

ol.block-list {
    list-style: decimal;
}

.block-list li {
    margin-bottom: 0.35rem;
}

/* Fliesstext-Schnitt (Probe 27.08.2026). Recursive ist eine Variable Font --
   die Datei traegt fvar/gvar/avar. CASL oeffnet die Formen (a, g), ohne Breite
   oder Gewicht zu aendern; deshalb war die Achse in einer Breitenmessung
   unsichtbar und wirkt trotzdem. Steht ausserhalb der @layer-Bloecke. */
body,
.block-text,
.block-list,
.games-intro,
.landing-intro,
.area-intro-tagline,
.area-intro-sub {
    font-variation-settings: 'CASL' 1;
}

/* Grosse Ueberschriften bleiben strenger. Der Casual-Effekt zeigt sich vor allem
   an Grossbuchstaben -- die Staemme neigen sich, der Querbalken setzt sichtbar an.
   Im Fliesstext ist das warm, in einer H1 wird es unruhig. Ole am 27.08.:
   "bei H1/H2 finde ich CASL 0 oder .5 angenehmer". */
h1, h2,
.block-heading,
.game-hero-title,
.landing-title,
.area-intro h1 {
    font-variation-settings: 'CASL' 0.5;
}

/* ── PROBE 28.08.2026 — Blog-Startbereich ────────────────────────────
   Noch nicht entschieden, wo der Text dauerhaft hin soll.
   Rueckbau: cp main.css.probe-backup main.css                        */
.area-intro-text {
    max-width: 68ch;
    margin: 0 auto 2.5rem;
    padding: 0 1rem;
}
.area-intro-text p { margin-bottom: 1rem; line-height: 1.65; }
.area-intro-text h2 { font-size: 1.35rem; margin-bottom: 0.5rem; }
.area-intro-text .frage-einwurf { font-size: 1.1em; }
/* Initiale nach JEDER Zwischenueberschrift -- nicht nur im ersten Absatz.
   Recursive statt capture_it: CASL macht die Form runder und waermer.
   CRSV bleibt drin, greift bei Versalien aber kaum. */
.area-intro-text h2 + p::first-letter {
    float: left;
    /* 28.08.2026: slnt statt CRSV. CRSV formt vor allem Kleinbuchstaben um und
       blieb an einer Versal-Initiale wirkungslos; die Neigung dagegen laesst den
       Buchstaben angesetzt statt hingestellt wirken -- die Bewegung, die eine
       Initiale im Buchsatz hat. Gewicht bewusst bei 700 statt 800: heller Text auf
       dunklem Grund wirkt optisch fetter, 800 ging im Dark-Modus ins Klobige. */
    font-variation-settings: 'CASL' 1, 'slnt' -15, 'wght' 700;
    font-size: 3.4em;
    line-height: .82;
    padding: .05em .1em 0 0;
    text-transform: uppercase;
}
/* Markenname: Form statt Farbe -- damit themeunabhaengig (Ole, 28.08.) */
.brand {
    font-variant: small-caps;
    letter-spacing: .06em;
    font-weight: 500;
}
/* Lesehilfe schlaegt Dekoration: Kapitaelchen ebnen die Wortsilhouette ein,
   an der Leseschwache Woerter erkennen. Zuruecknehmen, nicht verstecken. */
[data-dyslexia="1"] .brand {
    font-variant: normal;
    letter-spacing: normal;
    font-weight: 600;
}

/* 28.08.2026: Ohne Autorenspalte faellt IHRE Zeile weg -- die dritte Spalte
   bleibt, sie traegt jetzt die Schlagwoerter. Zweite :has()-Nutzung im Projekt. */
@media (min-width: 85em) {
    .mid-section:not(:has(.side-info)) {
        grid-template-areas: "banner-menu page-content tag-section";
    }
}
@media (min-width: 60em) and (max-width: 84.99em) {
    .mid-section:not(:has(.side-info)) {
        grid-template-areas:
            "banner-menu page-content"
            "tag-section page-content";
    }
}

/* 28.08.2026: Der Breiten-Umschalter stand nur in public/css/layers/02-tokens.css.
   Ohne diese sechs Regeln ergaben alle Stufen dieselbe Breite (gemessen 636px).
   ⚠️ BEWUSST OHNE @layer: Der Standardwert --content-width steht weiter oben in
   einem ungelayerten :root-Block, und ungelayertes CSS schlaegt JEDEN Layer.
   In @layer tokens gepackt verloren diese Regeln gegen den Standardwert und der
   Umschalter blieb tot -- gemessen, nicht vermutet. Gleiche Spezifitaet wie :root
   (0,1,0), also entscheidet die Reihenfolge: diese stehen spaeter und gewinnen. */
[data-width="mobile"] { --content-width: var(--content-width-mobile); }
[data-width="narrow"] { --content-width: var(--content-width-narrow); }
[data-width="normal"] { --content-width: var(--content-width-normal); }
[data-width="wide"]   { --content-width: var(--content-width-wide); }
[data-width="big"]    { --content-width: var(--content-width-big); }
[data-width="full"]   { --content-width: var(--content-width-full); }

/* ============================================================
   Lesebreite je Stufe des Breiten-Umschalters (28.08.2026).
   45 bis 100 Zeichen je Zeile -- die Spanne, in der Fliesstext
   lesbar bleibt. Ohne @layer, aus demselben Grund wie die
   [data-width]-Regeln weiter unten: der Standardwert steht in
   einem ungelayerten :root-Block, und unlayered schlaegt jeden
   Layer.
   ============================================================ */
[data-width="mobile"] { --lesebreite: 45ch; }
[data-width="narrow"] { --lesebreite: 55ch; }
[data-width="normal"] { --lesebreite: 68ch; }
[data-width="wide"]   { --lesebreite: 85ch; }
[data-width="big"]    { --lesebreite: 100ch; }
[data-width="full"]   { --lesebreite: 100%; }


/* ============================================================
   Karten blenden beim Scrollen ein (28.08.2026)
   Nach Kevin Powell: youtu.be/onraoQExbHw

   ZWEISTUFIG, und das ist der ganze Witz:

   1. Der WRAPPER (.karte-melder) traegt eine Scroll-Timeline, die
      nichts Sichtbares tut -- sie schaltet nur --in-view um.
   2. Die KARTE darin traegt eine gewoehnliche ZEIT-Animation, die
      pausiert wartet und per Style-Query startet.

   Warum nicht direkt scroll-gebunden? Weil der Fortschritt dann DIE
   Scrollposition IST: Die Karte faehrt nicht herein, sie steht dort,
   wo man hingescrollt hat -- und beim Zurueckscrollen verschwindet
   sie wieder. Ole am 28.08.: "die karten verschwinden ja so, wenn
   man wieder hoch scrollt... das passiert im Demo nicht (das ist ja
   auch eben der Sinn dieser Uebung)". Genau so ist es. Der Scroll
   drueckt den Startknopf, die Uhr macht die Bewegung, und sie laeuft
   nur einmal.

   ⚠️ --in-view MUSS per @property registriert sein. Ohne das ist eine
   Custom Property fuer den Browser eine beliebige Zeichenkette und
   damit nicht animierbar -- der Keyframe schaltet sie nie um.

   ⚠️ Der Wrapper war noetig, weil .teaser-card ZWEI Kinder hat
   (.teaser-pre, .teaser-body). Ohne ihn haette die Style-Query nur
   auf die Kinder greifen koennen und die Karte waere in Einzelteilen
   animiert worden. display:grid auf dem Wrapper haelt den
   Hoehenausgleich der Grid-Zelle (gemessen: identisch zu vorher).
   ============================================================ */
@property --in-view {
    syntax: "<custom-ident>";
    inherits: false;
    initial-value: nein;
}

@keyframes in-sicht-melden {
    from { --in-view: nein; }
    to   { --in-view: ja; }
}

@keyframes karte-einblenden {
    from { opacity: 0; translate: 0 2rem; scale: 0.97; }
    to   { opacity: 1; translate: 0 0;    scale: 1; }
}

@supports (animation-timeline: view()) and (container-name: pruefung) {
    @media (prefers-reduced-motion: no-preference) {

        /* Stufe 1 -- Melder, unsichtbar */
        [data-motion="full"] .karte-melder {
            animation: in-sicht-melden both;
            animation-timeline: view();
            animation-range: entry 0% entry 45%;
        }

        /* Stufe 2 -- die Bewegung, wartet auf den Melder */
        [data-motion="full"] .karte-melder > * {
            animation: karte-einblenden 0.65s ease-out both;
            animation-play-state: paused;
            animation-delay: calc(var(--stagger, 0) * 0.14s);
        }
        @container style(--in-view: ja) {
            [data-motion="full"] .karte-melder > * {
                animation-play-state: running;
            }
        }

        /* Versatz nach Spaltenzahl. Media-Query statt container-type --
           letzteres hat heute die .mid-section von 1393px auf 885px
           schrumpfen lassen (main.css:4308). */
        @media (min-width: 62em) {
            [data-motion="full"] .teaser-list > :nth-child(2n) { --stagger: 1; }
        }
        @media (min-width: 48em) {
            [data-motion="full"] .landing-areas > :nth-child(2) { --stagger: 1; }
            [data-motion="full"] .landing-areas > :nth-child(3) { --stagger: 2; }
        }
    }
}

/* Bewegung der Nav-Verlaeufe. Wie bei den Karten an ZWEI Schaltern:
   Betriebssystem und eigener im Menue. Ohne sie schaltet der Zustand
   hart um -- sichtbar bleibt er in jedem Fall. */
@media (prefers-reduced-motion: no-preference) {
    [data-motion="full"] .main-menu nav a {
        transition: background-size 380ms cubic-bezier(.22,.61,.36,1), color 200ms;
    }
}

/* ============================================================
   Mirrorworld-Startbereich — 28.08.2026
   Der Umzug auf ddd-web hatte die Wege zu /conlang/ und zur Karte gekappt:
   die alte Torseite brachte eigene Navigation mit, diese Vorlage nicht.
   Spaltenbreite bewusst gleich .area-intro-text (68ch), damit Intro, Wege,
   Ueberleitung und Ausblick eine durchgehende Kante haben.
   ============================================================ */
@layer components {

    .mw-regeln {
        list-style: none;
        padding: 0;
        margin: 0 0 1rem;
    }

    .mw-regeln li {
        position: relative;
        padding-left: 1.5rem;
        margin-bottom: 1rem;
        line-height: 1.65;
    }

    /* Der Punkt traegt das Bereichs-Kolorit -- dieselbe Farbe wie die H1. */
    .mw-regeln li::before {
        content: "";
        position: absolute;
        left: 0;
        top: 0.62em;
        width: 0.5rem;
        height: 0.5rem;
        border-radius: 50%;
        background: oklch(72% 0.11 285);
    }

    .mw-wege {
        display: grid;
        gap: 1.25rem;
        grid-template-columns: 1fr;
        max-width: 68ch;
        margin: 0 auto 2.5rem;
        padding: 0 1rem;
    }

    /* Kein align-items: start -- beide Kacheln sollen gleich hoch sein.
       Seit dem Wegfall des Kartenbildes tragen sie vergleichbar viel Text,
       und der Aufruf unten wird per margin-top:auto auf eine Linie gezogen. */
    @media (min-width: 44em) {
        .mw-wege { grid-template-columns: 1fr 1fr; }
    }

    .mw-weg {
        display: flex;
        flex-direction: column;
        padding: 1.25rem;
        border: 1px solid var(--surface-border, oklch(35% 0.02 285));
        border-radius: 0.5rem;
        background: var(--surface-raised, oklch(24% 0.015 285));
        color: inherit;
        text-decoration: none;
    }

    a.mw-weg:hover,
    a.mw-weg:focus-visible {
        border-color: oklch(60% 0.12 285);
    }

    .mw-weg-status {
        display: inline-block;
        font-size: 0.72rem;
        letter-spacing: 0.14em;
        text-transform: uppercase;
        opacity: 0.65;
        margin-bottom: 0.4rem;
    }

    .mw-weg h3 {
        font-size: 1.15rem;
        margin: 0 0 0.5rem;
        color: oklch(82% 0.09 285);
    }

    .mw-weg p {
        margin: 0;
        font-size: 0.94rem;
        line-height: 1.6;
    }

    /* margin-top: auto zieht den Aufruf an den unteren Rand -- bei zwei
       verschieden langen Beschreibungen stehen die beiden sonst versetzt. */
    .mw-weg-cta {
        display: inline-block;
        margin-top: auto;
        padding-top: 0.75rem;
        font-size: 0.9rem;
        color: oklch(78% 0.11 285);
    }

    a.mw-weg:hover .mw-weg-cta,
    a.mw-weg:focus-visible .mw-weg-cta {
        text-decoration: underline;
    }

    .mw-ueberleitung {
        max-width: 68ch;
        margin: 0 auto 2rem;
        padding: 0 1rem;
        line-height: 1.65;
    }

    .mw-kommt {
        max-width: 68ch;
        margin: 0 auto 2.5rem;
        padding: 0 1rem;
    }

    .mw-kommt h2 {
        font-size: 1.35rem;
        margin-bottom: 0.5rem;
    }

    /* list-style zuruecksetzen -- der Reset in Zeile 49 nimmt es allen Listen,
       und ohne Zeichen liest sich der Ausblick wie lauter kurze Absaetze.
       Gleiche Stelle wie bei .block-list (Kommentar dort, 27.08.2026). */
    .mw-kommt ul {
        margin: 0 0 0.75rem;
        padding-left: 1.25rem;
        line-height: 1.7;
        list-style: disc;
    }

    .mw-kommt li {
        margin-bottom: 0.35rem;
    }

    /* Deckkraft 0.7, nicht 0.6: bei 0.6 lag der Kontrast im hellen Theme auf
       4.51 gegen die Schwelle 4.5 -- gemessen 28.08.2026. Zu knapp, um sich
       auf Kantenglaettung zu verlassen; dieselbe Ueberlegung wie bei den
       Fusszeilen-Links weiter oben. Mit 0.7 sind es 6.5 (dunkel) / 6.3 (hell). */
    .mw-kommt-hinweis {
        font-size: 0.875rem;
        opacity: 0.7;
        font-style: italic;
        margin: 0;
    }
}

/* Die Ueberschriften-Farben oben sind fuer die dunkle Flaeche geloest.
   Gleicher Farbton, Helligkeit fuer den hellen Grund neu gerechnet --
   dasselbe Vorgehen wie bei .area-intro--blog/--games weiter oben. */
html[data-theme="light"] .mw-weg h3,
#light:checked ~ #grid-frame .mw-weg h3 {
    color: oklch(43% 0.11 285);
}
html[data-theme="light"] .mw-weg-cta,
#light:checked ~ #grid-frame .mw-weg-cta {
    color: oklch(45% 0.12 285);
}
html[data-theme="light"] .mw-regeln li::before,
#light:checked ~ #grid-frame .mw-regeln li::before {
    background: oklch(52% 0.13 285);
}
