/* Erzeugt von tools/build-css.sh — nicht von Hand ändern. */
@layer tokens, base, elements, widgets, slices, layouts, templates;

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Der Tokensatz — Maße aus dem Goldenen Schnitt, Farben in OKLCH.

   Alles Weitere leitet sich hieraus ab. Kein Paket setzt eine eigene Zahl an, wo
   ein Token sie schon trägt: eine Größe, die nur an einer Stelle steht, ist eine
   Größe, die beim nächsten Durchgang nicht mitwandert. */

@layer tokens {
    :root {
        /* **Dunkel, und zwar immer.** `dark light` hieß: der Browser darf nach
           der Systemeinstellung wählen — und auf einem hellen Windows wählte er
           hell, worauf jede dunkle Fläche fast weiß wurde und der Text darauf
           unlesbar (gemeldet am 3. September 2026). Ein helles Theme ist
           vorgedacht, nicht beschlossen: die hellen Werte in `light-dark()`
           bleiben stehen, damit sie eines Tages ohne Suche zu finden sind, aber
           ausgeliefert wird die dunkle Fassung. Ein Wort hier dreht das um
           ({T0026S12}). */
        color-scheme: dark;

        /* Maßleiter — der Schritt ist √φ = 1.272, zwei Schritte sind φ.
           Schrift nimmt jeden Punkt, Abstand jeden zweiten. */
        /* Die Basis wächst mit der Breite: 14 px am Telefon, 15 ab tablet, 17 ab
           screen. Gemessen am Fließtext des Entwurfs in allen drei Fassungen —
           er skaliert die Schrift, statt sie überall gleich zu setzen, und ohne
           das wirkt die Seite am Telefon um ein Fünftel zu groß.

           Die Leiter selbst bleibt: 17 durch √φ sind 13,4; mal √φ sind 21,6; mal φ
           sind 27,5 — der Entwurf trifft 13, 22 und 28. Die Stufen darunter
           stehen bei den Haltepunkten. */
        --scale-base:  0.875rem;

        /* Zwei Größen folgen der Leiter nicht und stehen deshalb je Breite:

           Die Bühne — 23, 30, 44 px. Sie ist der Spielname und soll die Leiste
           füllen, was bei 430 px anders aussieht als bei 1440.

           Die Seitenüberschrift — 19, 22, 28 px. Ihr Verhältnis zur Basis wächst
           mit der Breite (1,36 · 1,47 · 1,65 statt konstant φ), und das ist keine
           Nachlässigkeit des Entwurfs: eine Überschrift, die am Telefon dasselbe
           Vielfache trüge wie am Schreibtisch, nähme dort die halbe Höhe des
           Sichtfelds. Wo wenig Platz ist, rückt sie näher an den Text. */
        --text-stage:  1.4375rem;
        --text-head:   1.1875rem;

        /* Beschriftungen in Versalien — Kartentitel, Kennzahlen-Label. 10, 11, 12
           px: sie wachsen langsamer als die Basis, weil Versalien bei gleicher
           Größe breiter bauen und in einer engen Karte sonst umbrechen. */
        --text-label:  0.625rem;

        --text-micro:  calc(var(--scale-base) / 1.618);   /* 10,5 → Beschriftungen */
        --text-small:  calc(var(--scale-base) / 1.272);   /* 13,4 → Etiketten, Kartentitel */
        --text:        var(--scale-base);                 /* 17   → Fließtext */
        --text-large:  calc(var(--scale-base) * 1.272);   /* 21,6 → Kennzahlen */
        --text-title:  calc(var(--scale-base) * 2.058);   /* 35   → die Bühne */

        --space-hair:  calc(var(--scale-base) * 0.146);
        --space-tight: calc(var(--scale-base) * 0.236);
        --space-snug:  calc(var(--scale-base) * 0.382);
        --space-close: calc(var(--scale-base) * 0.618);
        --space:       var(--scale-base);
        --space-open:  calc(var(--scale-base) * 1.618);

        /* Die Höhe jedes Bedienelements — Feld, Knopf, und was noch kommt.
           
           Sie stand vorher im seed_form-Widget, das Feld und Knopf in einer
           Rasterzeile streckte. Beim Herauslösen in Elemente ging sie verloren:
           auf der Startseite streckte sich der Knopf über Beschriftung **und**
           Feld, auf der Gegenstandsseite schrumpfte er auf seinen Inhalt. Zwei
           Seiten, zwei Höhen, eine Sache.
           
           Die Startseite ist das Vorbild, und diese Zahl ist von dort gemessen:
           0.9375rem Innenabstand oben und unten plus 0.8125rem Schrift. */
        --control-height: 2.6875rem;

        /* Die Hoehe des klebenden Kopfes ({T0097}). Gemessen an der laufenden
           Seite: 68 Punkte — zwei mal 1rem Innenabstand, 30 Punkte Marke,
           5 Punkte Fortschrittsbalken, 1 Punkt Rand. Sie steht hier, weil
           etwas, das unter dem Kopf schwebt, sie wissen muss und sie nicht
           erfragen kann: `position: fixed` bezieht sich auf das Fenster, und
           CSS kann keine gemessene Hoehe in eine Eigenschaft schreiben.

           **Wer den Kopf aendert, aendert diese Zahl.** Das ist der Preis
           dafuer; die Alternative waere Skript, das eine Hoehe ausliest und
           zurueckschreibt — mehr Beweglichkeit fuer mehr Teile. */
        --kopf-hoehe: 4.25rem;
        --space-wide:  calc(var(--scale-base) * 2.618);

        /* Das Blatt — die Masse jedes Popovers, das aufgeht ({T0084}).

           Sie standen im item_sheet-Widget, gemessen vom Owner am 1. September
           2026. Als die Karte ihr eigenes Blatt bekam, schrieb ich dieselben
           sechs Zahlen ein zweites Mal ab, und damit gab es zwei Wahrheiten
           ueber eine Sache: wer die Breite aendert, aendert sie an einer Stelle
           und sieht am naechsten Tag, dass die andere stehen blieb. Genau der
           Fall, den {K0054} beschreibt — nur dass die Huelle kein Markup ist,
           das man in ein Element heben koennte, sondern Mass. Und Mass wohnt
           hier — wie `--control-height` darueber.

           Wer ein Blatt baut, schreibt diese Tokens und keine Zahlen. */
        --sheet-inline-size:     min(400px, calc(100vw - 2rem));
        --sheet-max-block-size:  min(900px, calc(100vh - 2rem));
        /* **Die Kante von allem, was auf der Seite aufliegt** — Blaetter und
           Baender. Der Name sagt »Blatt«, weil dort die erste war; gemeint
           ist jedes Element, das ueber dem Inhalt schwebt und sich deshalb
           von ihm abgrenzen muss ({T0097}).

           Wer eines baut, schreibt diesen Token und keine Zahl. Bis zum
           12. September 2026 stand er an zwei Stellen nicht: das
           Favoritenblatt der Karte trug seine 3px ausgeschrieben, und das
           Hinweisband einen einzigen — und ein Band mit einer Haarlinie
           neben einem Blatt mit drei Punkten sieht nach zwei Hausordnungen
           aus. */
        --sheet-edge:            3px;
        --sheet-shadow:          0 24px 64px oklch(0% 0 0 / 0.55);
        --sheet-scrim:           oklch(0% 0 0 / 0.6);
        /* Der Guertel zum Hosentraeger: im Top-Layer braucht ein Popover keinen
           z-index, aber beim Schliessen faellt es heraus, und dann entscheidet
           er doch (Owner, 1. September 2026). */
        --sheet-layer:           9999;

        /* Ausgabebreite. Ein einziger Deckel: die Spur des Slices begrenzt sich
           selbst per min\(\), deshalb braucht die Breite keine vier Stufen —
           es wächst die Rinne, nicht der Deckel. */
        /* Der Entwurf laesst dem Inhalt bei 1440 px rund 40 px Rand, nutzt also
           fast die volle Breite. 84rem trifft das und deckelt zugleich, was auf
           einem sehr breiten Schirm sonst ins Uferlose liefe — der Fliesstext
           bleibt ohnehin bei 66ch \(siehe prose\). */
        --measure:     90rem;
        --gutter:      2.5rem;

        /* Raster der Pixelschrift. Ihre Glyphen sind acht Schriftpixel breit;
           damit eine Kante auf ein ganzes Gerätepixel fällt, muß Größe durch acht
           mal Geräteauflösung ganzzahlig sein — bei Auflösung 1 und 3 ein
           Vielfaches von 8, bei 2 eines von 4 \({K0044}\). Die Leiter bleibt
           dieselbe; gerundet wird an der Verwendungsstelle mit round\(\). */
        --pixel-grid:  8px;

        /* Fläche und Schrift sind zwei Skalen, nicht eine. Der Entwurf setzt die
           Flächen dicht beieinander — 12 bis 21 Prozent, fünf Stufen auf neun
           Punkten — und die Schrift weit darüber. Eine gemeinsame Skala müßte
           beides bedienen und träfe keines von beidem.

           Die Werte für dunkel sind aus dem Entwurf gemessen, die für hell daran
           gespiegelt. light-dark() nimmt den ersten Wert im hellen Schema. */
        --hue-surface: 260;
        --c-surface:   0.010;

        --surface-0:  light-dark(oklch(99% var(--c-surface) var(--hue-surface)),
                                 oklch(12% var(--c-surface) var(--hue-surface)));
        --surface-1:  light-dark(oklch(97% var(--c-surface) var(--hue-surface)),
                                 oklch(15% var(--c-surface) var(--hue-surface)));
        --surface-2:  light-dark(oklch(94% var(--c-surface) var(--hue-surface)),
                                 oklch(17% var(--c-surface) var(--hue-surface)));
        --surface-3:  light-dark(oklch(91% var(--c-surface) var(--hue-surface)),
                                 oklch(18% var(--c-surface) var(--hue-surface)));
        --surface-4:  light-dark(oklch(86% var(--c-surface) var(--hue-surface)),
                                 oklch(21% var(--c-surface) var(--hue-surface)));

        /* Schrift: drei Stufen, aus dem Entwurf gemessen. 65 trägt den Text, 58
           das Beiwerk, 55 das Leiseste — im hellen Schema gespiegelt. */
        /* Drei Stufen, alle aus dem Export gemessen. Die hellste trägt einen
           anderen Farbton — 90 statt 260 —, weil eine Überschrift auf dunklem
           Grund warm wirken soll und nicht blau.

           Es waren vier. Die leiseste, --ink-faint, lag im Dunkelmodus auf 48
           Prozent und ergab auf --surface-0 nur 3,02 zu 1 — unter AA, an allen
           fünf Stellen, an denen sie stand. Gemessen braucht es dort 58 Prozent,
           und das ist exakt --ink-quiet: unterhalb dieser Stufe gibt es auf
           diesem Grund keine Textfarbe mehr, die die Grenze hält. Eine Stufe,
           die nirgends regelkonform verwendbar ist, ist keine Stufe. */
        --ink-bright: light-dark(oklch(20% 0.005 90),  oklch(95% 0.005 90));
        --ink:        light-dark(oklch(28% 0.008 var(--hue-surface)),
                                 oklch(70% 0.008 var(--hue-surface)));
        /* Die leise Stufe stand auf 58 Prozent und hielt AA nur auf --surface-0
           — auf den erhöhten Flächen fiel sie auf 4,43 und 4,36, gemessen an
           neun Stellen. Der Grund ist dort heller, also muß die Schrift es auch
           sein. 61 Prozent trägt auf allen dreien mit Reserve; 60 hätte gereicht,
           läge aber bei 4,74 wieder dicht an der Kante. */
        --ink-quiet:  light-dark(oklch(42% 0.008 var(--hue-surface)),
                                 oklch(61% 0.008 var(--hue-surface)));

        --ground:     var(--surface-0);
        --ground-up:  var(--surface-2);

        /* **Drei Rollenfarben, und es waren immer drei** (Owner, 12. September
           2026: »es gibt folgende Grünwerte und das von Anbeginn … es gab
           niemals ein weiteres Grün oder ähnliches«).

           | Primary Green  | oklch(76% 0.17 130) | was führt, was auslöst |
           | Secondary Gold | oklch(80% 0.13 85)  | was mehr zeigt         |
           | Tertiary Orange| oklch(68% 0.18  35) | die Marke, sparsam     |

           Hier stand bis dahin ein viertes Token, `--action` mit
           `oklch(72% 0.20 145)`, und daneben ein `--on-action`, das nie
           jemand benutzt hat. Der Satz, der beide begründete — »Verweis führt
           weg, Aktion löst aus« — beschrieb eine Teilung, die es nie gab: der
           Aktionsknopf trug von jeher `--link`, und `--action` stand an
           zwanzig Stellen fast nur in Fokusringen. Zwei Grüntöne, einen
           Bildpunkt auseinander in der Bedeutung und deutlich im Auge
           ({K0233}, {T0096}).

           **Das gilt für die Rollen der Oberfläche, nicht für Daten.** Die
           Biomfarben der Karte und die Rangleiter Weiß-Grün-Blau-Magenta
           folgen dem Spiel und bleiben, wo sie sind. */
        --brand:      oklch(68% 0.18  35);
        --link:       oklch(76% 0.17 130);
        /* Goldgelb, die dritte Rolle neben den beiden Grüntönen: sie sagt weder
           »führt weg« noch »löst aus«, sondern »zeigt mehr«. Der Chevron am
           Kartenknopf trägt sie \(Owner, 1. September 2026\). */
        --gold:       oklch(80% 0.13 85);
        /* Rot, die vierte Rolle: »nimmt weg«. Der Knopf im Blatt des Ziels
           fuehrt damit aus dem Bauplan zurueck auf die Itemseite \(Owner,
           8. September 2026, {T0073S13}\). Gemessen auf --surface-4 mit
           dunkler Schrift: 6,1 zu 1. */
        --danger:     oklch(66% 0.19 28);

        /* Die Rangleiter der Qualität: weiß, grün, blau, magenta — die Folge,
           die World of Warcraft und Elder Scrolls Online jedem Spieler
           beigebracht haben, vom Owner gewählt \({T0023S4}, 02-valheim.md\).
           Sie sagt, **wie gut** ein Stück ist; die Biomfarben sagen, **wann**
           im Spiel. Zwei Systeme, zwei Fragen — und deshalb kein Biomton hier.

           Grün und Magenta sind aus dem Entwurf gemessen: rgb\(0,255,28\) und
           rgb\(255,0,252\). Das Blau des Entwurfs, rgb\(11,97,172\), hält auf
           surface-4 nur 2,8 zu 1 — unter AA auch für große Schrift. Es steht
           deshalb auf 65 Prozent Helligkeit im selben Farbton: 5,5 zu 1, und
           immer noch das Blau der Leiter. Die Abweichung ist gemessen und
           benannt \(AW22\). Weiß ist die helle Tinte, die der Name ohnehin trägt. */
        --rank-1:     var(--ink-bright);
        --rank-2:     oklch(86.7% 0.291 143);
        --rank-3:     oklch(65% 0.14 252);
        --rank-4:     oklch(70% 0.321 329);

        /* Das Zeilenraster. Eine dunkle Zeile auf drei, bei 14 Prozent — im
           Mittel 4,7 Prozent Abdunklung.

           **Wo ein Bild eine Rasterung bekommt, kommt sie von hier.** Nicht
           nachgeschrieben, nicht nach Augenmaß angepaßt: die Werte sind eng,
           und danebenliegen sieht nicht nach »etwas anders« aus, sondern nach
           einem anderen Effekt. Die Biomleiste stand auf einer Zeile von zwei
           bei 28 Prozent — dreimal so dunkel, und weil Zeile und Lücke auf einem
           doppelt auflösenden Schirm ineinanderlaufen, war vom Raster nichts
           mehr zu sehen. Nur ein dunkles Bild \({K0097}\).

           `linear-gradient` mit `background-size` und nicht
           `repeating-linear-gradient`: eine Kachel, die der Browser nicht selbst
           ausrechnet, läßt nicht alle paar Zeilen eine ausfallen.

           Als Hintergrundschicht einzusetzen, nicht als Auflage in einem
           Pseudoelement — die Schicht liegt im selben `background` wie das Bild
           und kann von ihm nicht abweichen. */
        --interlace:      linear-gradient(180deg,
                              oklch(0% 0 0 / 0.14) 0 1px, transparent 1px 3px);
        --interlace-size: 100% 3px;
    }

    /* Die Leiter hat fünf Stufen — base · phone 16.25rem · tablet 35rem ·
       screen 60rem · widescreen 78.75rem. Die Rinne wächst an zweien davon; an
       den übrigen steht bewußt nichts, weil dort nichts zu ändern ist. Nur
       Deltas, nur min-width. */
    /* Am Telefon ist die Rinne schmaler, sonst bleibt vom Inhalt nichts. Ab
       tablet steht sie auf den 40 px des Entwurfs und wächst nicht weiter — was
       darüber hinaus Platz ist, nimmt der Deckel measure.

       Nur Deltas, nur min-width: jede Stufe nennt, was sich ändert, und nichts
       sonst. */
    :root { --gutter: var(--space); }

    @media (min-width: 35rem) {
        :root {
            --gutter:     2.5rem;
            --scale-base: 0.9375rem;
            --text-stage: 1.875rem;
            --text-head:  1.375rem;
            --text-label: 0.6875rem;
        }
    }

    @media (min-width: 60rem) {
        :root {
            --scale-base: 1.0625rem;
            --text-stage: 2.75rem;
            --text-head:  1.75rem;
            --text-label: 0.75rem;
        }
    }

    @media (resolution >= 2x) { :root { --pixel-grid: 4px; } }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Fundament — Kastenmodell, Grundschrift, und die Spuren jedes Slices. */

@layer base {
    *, *::before, *::after { box-sizing: border-box; }

    :where(body, h1, h2, h3, h4, p, figure, ul, ol, dl, dd) { margin: 0; }
    :where(ul[role="list"], ol[role="list"]) { padding: 0; list-style: none; }

    html {
        font-size: 100%;
        -webkit-text-size-adjust: none;
    }

    body {
        font-family: "Oxanium", "Oxanium Fallback", system-ui, sans-serif;
        font-size: var(--text);
        line-height: 1.5;
        color: var(--ink);
        background: var(--ground);
    }

    :where(img, picture, svg) { display: block; max-inline-size: 100%; }
    :where(input, button, select, textarea) { font: inherit; color: inherit; }

    /* Ein Slice ist eine Zeile über die volle Breite \({K0031}\). Die Begrenzung
       trägt die benannte Spur, nicht ein Wrapper: Kinder landen in `content`, und
       was randlos laufen soll, sagt `grid-column: full`.

       Ausdrücklich keine Breitenangabe — der Slice ist Kind des einspaltigen
       Rasters und füllt dessen Spur. `width: 100vw` schlösse die Bildlaufleiste
       ein und ergäbe am Schreibtisch rund 15 px waagerechten Überlauf, der am
       Telefon nicht auftritt und deshalb spät auffällt. */
    [data-slice] {
        display: grid;

        /* Der Rand wird gerechnet und steht als Variable bereit. Das ist der
           Unterschied, der zählt: ein Band, das innen teilen will, ersetzt nur die
           content-Spur und erbt denselben Rand. Als minmax\(gutter, 1fr\) würde die
           Rinne mit jeder inneren fr-Spur um den Platz streiten, und jedes Band
           käme auf einen anderen Rand — gemessen 40, 63, 77 und 110 px an vier
           Bändern derselben Seite \({K0057}\). */
        --rand: max(var(--gutter), (100% - var(--measure)) / 2);

        grid-template-columns:
            [full-start] var(--rand)
            [content-start] minmax(0, 1fr)
            [content-end] var(--rand)
            [full-end];
    }

    [data-slice] > * { grid-column: content; }
}

/* Solange ein Blatt im Top-Layer steht, bewegt sich die Seite darunter nicht.

   Ein Popover sperrt das Rollen **nicht** von selbst — anders als ein <dialog>
   im modalen Zustand. `:has()` erledigt es ohne Skript, und die Regel gilt für
   jedes Popover, nicht nur für dieses eine \(Owner, 1. September 2026\).

   `scrollbar-gutter: stable` steht daneben, damit das Verschwinden des
   Rollbalkens die Seite nicht um seine Breite verspringen läßt. */
/* **Der Platz des Rollbalkens ist immer reserviert, nicht erst wenn er weg
   ist.** Genau hier stand `scrollbar-gutter: stable` zuerst — in der Regel
   darunter, also erst gültig, sobald das Blatt offen war. Da ist es zu spät:
   der Balken verschwindet im selben Moment, in dem die Reservierung greift,
   und die Seite springt um seine Breite. Sie muß vorher schon dastehen. */
:root {
    scrollbar-gutter: stable;
}

/* Solange ein Blatt im Top-Layer steht, bewegt sich die Seite darunter nicht.
   Ein Popover sperrt das Rollen **nicht** von selbst — anders als ein <dialog>
   im modalen Zustand. Beide Elemente, weil je nach Browser das eine oder das
   andere rollt. */
:root:has([popover]:popover-open),
:root:has([popover]:popover-open) body {
    overflow: hidden;
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Theme für Valheim — die vier Dinge, die je Spiel wechseln.

   Struktur ist allgemein, Farben sind spezifisch. Ein zweites Spiel legt eine Datei
   daneben und ändert nichts sonst; das ist die ganze Themefähigkeit ({K0029}).
   Was hier steht, ist die Palette — Icons, Karte und Biomfarben kommen aus den
   Spieldaten und nicht aus dem Stylesheet. */

@layer tokens {
    [data-theme="valheim"] {
        /* Die Bühnenleiste — Kupfer, wie das erste Erz, das man im Spiel schmilzt.
           Acht Bänder mit harten Kanten, symmetrisch um die Mitte: aus dem Entwurf
           gemessen, nicht nachempfunden. Der Farbton wandert von 38 an den Rändern
           auf 55 in der Mitte, was dem Metall den Schimmer gibt — ein Verlauf auf
           einem einzigen Ton sähe nach Farbfläche aus, nicht nach Licht. */
        /* Die Zeilenraster liegen über dem Verlauf: ein Pixel Schwarz auf 28
           Prozent, zwei Pixel Lücke. Das ist der Bildschirm, den die Schrift
           meint — ohne sie ist die Leiste eine Farbfläche mit Streifen, mit ihnen
           ist sie ein Monitor. Aus dem Entwurf gemessen.

           Als zweite Hintergrundebene statt als absolut gesetztes Pseudo-Element:
           spart einen Knoten und kann nicht verrutschen.

           Ein einfacher Verlauf mit fester Kachelgröße, nicht repeating: ein
           fortlaufender Verlauf spannt sich über die ganze Fläche und rundet
           seine Perioden gegen das Gerätepixelraster — dabei fällt jede paar
           Zeilen eine aus. Mit `background-size: 100% 3px` ist die Kachel exakt
           drei Pixel hoch und wiederholt sich sauber. */
        --stage-scan:
            linear-gradient(180deg,
                oklch(0% 0 0 / 0.28) 0 1px,
                transparent 1px 3px);

        /* Der Scheitel stand auf 74 Prozent und war die hellste Stelle der
           Leiste — genau dort, wo der Titel steht. Gemessen ergab das 2,19 zu 1
           gegen die Titelfarbe, nötig sind 3,0 für große Schrift. Die Reihe
           fiel erst ab 64 Prozent über die Grenze; 62 gibt Reserve, damit eine
           spätere Schriftgröße sie nicht sofort wieder reißt.

           Die Leiste wird dadurch etwas satter und weniger grell. Das ist eine
           Abweichung vom Entwurf, sie steht hier, und sie ist gemessen — so
           verlangt es AW22. Der Entwurf erfüllt an dieser Stelle kein AA. */
        --stage-bar:
            linear-gradient(180deg,
                oklch(30% 0.09 40) 0 14%,
                oklch(45% 0.14 42) 0 28%,
                oklch(58% 0.18 45) 0 42%,
                oklch(62% 0.17 55) 0 50%,
                oklch(58% 0.18 45) 0 58%,
                oklch(45% 0.14 42) 0 72%,
                oklch(28% 0.08 38) 0 86%,
                oklch(16% 0.05 35) 0 100%);
        --stage-ink:   oklch(97% 0.02 40);

        /* Die zehn Biome. Die Reihenfolge ist die des Spielfortschritts, nicht die
           des Alphabets — wer sie liest, liest den Weg. */
        --biome-meadows:   oklch(74% 0.14 130);
        --biome-forest:    oklch(52% 0.10 150);
        --biome-swamp:     oklch(44% 0.07  95);
        --biome-mountain:  oklch(88% 0.03 230);
        --biome-plains:    oklch(80% 0.13  85);
        --biome-mistlands: oklch(46% 0.05 280);
        --biome-ashlands:  oklch(58% 0.19  35);
        --biome-deepnorth: oklch(84% 0.05 210);
        --biome-ocean:     oklch(56% 0.10 235);
        --biome-edge:      oklch(38% 0.02 260);
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Beta-Marke — ein Workbench-Gadget in Rot. */

@layer elements.beta_badge {
    @scope ([data-type="beta-badge"]) {
        /* Die drei Rot stehen hier und nicht in tokens.css. Der Satz darueber
           hiess einmal »waechst daraus eine zweite Stelle, wandern sie hoch«,
           und mit dem Statusfenster \({T0097}\) gab es die zweite Stelle — der
           Umzug ins `:root` war trotzdem falsch. **AW10 und {K0004} verbieten
           Ableitungen im `:root`**: ein gerechneter Ton steht an jedem Kontext,
           der die Grundfarbe setzt, damit er mitgeht, wenn sie sich aendert.
           Das Statusfenster rechnet dieselbe Zeile selbst. Dieselbe Rechnung an
           zwei Stellen ist der Preis, und er ist kleiner als ein Wert, der
           einfriert.

           Gewaehlt nach dem Amiga: die Flaeche ist das Rot der Palette abgedunkelt,
           die Lichtkante liegt darueber, die Schattenkante darunter. Weiss auf
           dem Grund misst 6,66 zu 1 und traegt damit auch die kleinste Stufe. */
        :scope {
            /* **Aus der Palette gerechnet, nicht danebengeschrieben**
               ({T0096}, AW10). Hier standen drei eigene Rotwerte, und der
               Satz darueber nannte sie »das satte Rot der Palette« — sie
               waren es nicht: `--danger` steht bei 66 % Helligkeit, hier
               stand 50, und der Farbton wich um ein Grad ab. Kein
               Gestaltungswille, ein Abschreibfehler.

               Die Flaeche ist dunkler als `--danger`, und das bleibt so: sie
               traegt weisse Schrift, und dafuer braucht sie den Abstand. Die
               beiden Kanten rechnen symmetrisch aus ihr — dreizehn Punkte
               heller oben, dreizehn dunkler unten. */
            --badge-face:   oklch(from var(--danger) 0.50 c h);
            --badge-light:  oklch(from var(--badge-face) calc(l + 0.13) c h);
            --badge-shadow: oklch(from var(--badge-face) calc(l - 0.13) c h);

            display: inline-block;
            padding: 0.375rem 0.5rem 0.3125rem;
            background: var(--badge-face);
            color: oklch(100% 0 0);

            /* Der Kasten der Workbench: aussen eine schwarze Linie, innen die
               beiden Kanten. Beides ohne Radius — eine gerundete Ecke gibt es
               auf einem Geraet mit 320 mal 256 Punkten nicht. */
            border: 1px solid oklch(0% 0 0);
            box-shadow:
                inset  2px  2px 0 0 var(--badge-light),
                inset -2px -2px 0 0 var(--badge-shadow);

            font-family: "Press Start 2P", monospace;
            font-weight: 400;
            font-size: 0.5rem;
            line-height: 1;
            letter-spacing: 0.08em;
            text-transform: uppercase;

            /* Die Grundlinie der Pixelschrift sitzt tief; ohne die Korrektur
               haengt die Marke unter der Wortmarke statt neben ihr. */
            translate: 0 -0.0625rem;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ein Knopf — die Pixelkante aus dem Entwurf. */

@layer elements.button {
    @scope ([data-type="button"]) {
        :scope {
            display: grid;
            place-items: center;
            block-size: var(--control-height);
            padding-inline: 0.625rem;
            background: var(--link);
            border: 0;
            color: oklch(20% 0.05 130);
            font-family: "Press Start 2P", monospace;
            font-size: 0.8125rem;
            line-height: 1;
            text-transform: uppercase;
            cursor: pointer;

            /* Die Kante ist der ganze Knopf: zwei Innenschatten, hell oben links
               und dunkel unten rechts, zwei Pixel breit. Das ist die Form, die ein
               Knopf in einem Spiel dieser Zeit hatte — kein Rahmen, keine Rundung,
               keine Anfrage. Aus dem Entwurf gemessen, Werte unverändert. */
            box-shadow:
                inset  2px  2px 0 oklch(88% 0.12 130),
                inset -2px -2px 0 oklch(52% 0.14 130);
        }

        /* **Gesperrt: dieselbe Form, nur ohne Farbe und ohne Kante.**
           Ein Knopf, der noch nicht kann, verschwindet nicht und wandert nicht
           — er steht da, wo er stehen wird, und sagt nur, daß er noch nicht
           bereit ist. Sonst springt die Zeile darunter in dem Moment, in dem
           man hinsieht.

           Die Pixelkante fällt weg, weil sie »drück mich« sagt; die Fläche
           wird die des Blattes, die Schrift verliert ihren Kontrast. `cursor`
           bleibt nicht auf `pointer`, denn nichts geschieht (Owner,
           11. September 2026: der Knopf wird erst grün, wenn das Bild fertig
           ist). */
        :scope:disabled {
            background: var(--surface-1, oklch(32% 0.01 260));
            color: var(--ink-muted, oklch(62% 0.01 260));
            box-shadow: none;
            cursor: default;
        }

        /* Gold statt Grün: dieselbe Gestalt, andere Rolle. Die Kanten folgen
           dem Ton, sonst säße eine grüne Fassung um eine goldene Fläche. */
        :scope[data-tone="gold"] {
            background: var(--gold);
            color: oklch(24% 0.06 85);
            box-shadow:
                inset  2px  2px 0 oklch(90% 0.09 85),
                inset -2px -2px 0 oklch(58% 0.11 85);
        }

        :scope[data-tone="gold"]:where(:hover, :focus-visible) {
            background: oklch(from var(--gold) calc(l + 0.05) c h);
        }

        /* Rot: der Knopf nimmt etwas weg ({T0073S13}) - im Blatt des Ziels
           der Weg aus dem Bauplan zurueck auf die Itemseite. Dieselbe
           Gestalt, die Kanten folgen dem Ton. */
        :scope[data-tone="red"] {
            background: var(--danger);
            color: oklch(18% 0.05 28);
            box-shadow:
                inset  2px  2px 0 oklch(82% 0.14 28),
                inset -2px -2px 0 oklch(46% 0.17 28);
        }

        :scope[data-tone="red"]:where(:hover, :focus-visible) {
            background: oklch(from var(--danger) calc(l + 0.05) c h);
        }

        /* Als Verweis: die Textauszeichnung des Browsers weg, die Gestalt des
           Knopfs bleibt. */
        a:scope { text-decoration: none; }

        /* Der Knopf traegt die Aktionsfarbe, der Verweis die Verweisfarbe — die
           beiden Gruentoene und ihre Rollen stehen in {K0032}. */
        :scope:where(:hover, :focus-visible) {
            background: oklch(from var(--link) calc(l + 0.05) c h);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ein Schalter in Mischpultform — Gleiter in der Spur, Knopf darauf. */

@layer elements.checkbox {
    @scope ([data-type="checkbox"]) {
        :scope {
            display: flex;
            align-items: center;
            gap: 0.5rem;
            color: var(--ink);
            cursor: pointer;
            user-select: none;
        }

        /* Selbst gezeichnet und nicht `accent-color`.
           
           Vorher stand hier das Gegenteil, mit gutem Grund: der Browser zeichnet
           ein Kästchen in seiner eigenen Form und kennt jeden Zustand. Diese
           Form gibt es aber nicht als Kästchen — ein Schieber, dessen Knopf über
           die Spur hinausragt, wie am Mischpult. Wer eine Gestalt will, die es
           nicht gibt, muß sie zeichnen.
           
           Was dabei erhalten bleibt: es ist weiterhin ein `<input type=
           "checkbox">`. Tastatur, Vorleser und Formularübertragung merken
           nichts davon, daß es anders aussieht. */
        input {
            appearance: none;
            margin: 0;
            position: relative;
            inline-size: 2.25rem;
            /* Die Spur ist schmal — der Knopf darüber ist die Sache. */
            block-size: 0.5rem;
            background: oklch(18% 0.008 260);
            cursor: pointer;

            /* Die Spur liegt vertieft: dunkel oben links, hell unten rechts.
               Dieselbe Richtung wie beim Eingabefeld, und aus demselben Grund —
               das Licht fällt für alles aus derselben Ecke. */
            box-shadow:
                inset 1.5px 1.5px 0 oklch(9% 0.008 260),
                inset -1.5px -1.5px 0 oklch(30% 0.010 260);
        }

        /* Der Knopf: höher als die Spur, damit er darauf sitzt statt darin.
           Aus steht er links und ist dunkles Metall, an steht er rechts und
           trägt die Aktionsfarbe — dieselbe wie der Suchknopf, weil beides
           dasselbe sagt: hier ist etwas scharf. */
        input::before {
            content: "";
            position: absolute;
            inset-block: -0.3125rem;
            inset-inline-start: 0;
            inline-size: 1.125rem;
            background: oklch(32% 0.010 260);
            box-shadow:
                inset  2px  2px 0 oklch(46% 0.012 260),
                inset -2px -2px 0 oklch(16% 0.008 260);
            transition: inset-inline-start 90ms steps(3), background 90ms;
        }

        input:checked::before {
            inset-inline-start: calc(100% - 1.125rem);
            background: var(--link);
            box-shadow:
                inset  2px  2px 0 oklch(88% 0.12 130),
                inset -2px -2px 0 oklch(52% 0.14 130);
        }

        /* Kein weiches Gleiten: `steps(3)` rastet in drei Stufen, wie ein
           Schieber, der über Rasterpunkte läuft. Wer reduzierte Bewegung
           meldet, bekommt gar keine. */
        @media (prefers-reduced-motion: reduce) {
            input::before { transition: none; }
        }

        input:where(:focus-visible) {
            outline: 2px solid var(--link);
            outline-offset: 4px;
        }

        /* Gold statt Grün, wo zwei Reihen nebeneinander verschiedene Dinge
           schalten ({T0093}): auf der Karte die Ebenen in Grün, die Biome in
           Gold. Nur der Knopf wechselt die Farbe — Form, Weg und Rastung
           bleiben, weil es dieselbe Bedienform ist und nicht eine zweite.

           Die Glanzlichter werden aus der Grundfarbe gerechnet und nicht
           danebengeschrieben ({K0004}), mit denselben Abständen wie beim
           Grün: zwölf Punkte heller oben links, vierundzwanzig dunkler unten
           rechts. Ein Ton, der zweimal dasteht, weicht beim dritten Mal ab. */
        :scope[data-tone="gold"] input:checked::before {
            background: var(--gold);
            box-shadow:
                inset  2px  2px 0 oklch(from var(--gold) calc(l + 0.12) c h),
                inset -2px -2px 0 oklch(from var(--gold) calc(l - 0.24) c h);
        }

        /* Der angeschaltete Zustand färbt auch die Beschriftung: wer über die
           Reihe blickt, sieht ohne Vergleich, was an ist. */
        input:checked ~ span { color: var(--ink-bright); }

        /* Ein Schalter, der nichts bewirkt, nimmt auch keinen Zeiger an. Wie
           er dabei aussieht, entscheidet die Seite, die ihn deaktiviert —
           hier steht nur, daß er ruht. Der Vorleser sagt es ohnehin. */
        :scope:has(input:disabled) { cursor: default; }
        input:disabled { cursor: default; }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Kurze Etiketten — ohne Rahmen, die Farbe traegt die Aussage. */

@layer elements.chip {
    @scope ([data-type="chip"]) {
        :scope {
            font-family: ui-monospace, monospace;
            font-size: var(--text-micro);
            letter-spacing: 0.16em;
            text-transform: uppercase;
            color: var(--link);

            /* Kein Rahmen und keine Flaeche. Ein Etikett, das schon durch Schrift,
               Farbe und Versalien aus dem Text heraussticht, braucht keinen
               zweiten Traeger — der Rahmen kostet Platz und traegt nichts. */
        }

        /* Gold statt Gruen, wie am Knopf und am Schalter ({K0032}, {T0093}).
           Auf der Karte trennt es die Reglergruppe, die nach dem Ort fragt,
           von der, die nach der Art fragt: die Beschriftung traegt denselben
           Ton wie die Schalter darunter. Nur die Farbe wechselt — Schrift,
           Sperrung und Versalien bleiben, weil es dasselbe Etikett ist. */
        :scope[data-tone="gold"] { color: var(--gold); }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ein Knopf in der Ecke einer Karte — flach, duenner grauer Rahmen. */

@layer elements.corner_button {
    @scope ([data-type="corner-button"]) {
        /* So groß wie das Zeichen der Marke in der Kopfzeile: 1,875 rem. Der
           Owner hat das als Mass genannt, damit die Symbole erkennbar bleiben —
           darunter wird ein Chevron zum Strich. */
        :scope {
            display: grid;
            place-items: center;
            inline-size: 1.875rem;
            aspect-ratio: 1;
            padding: 0;
            background: none;

            /* Flach, mit dünnem grauem Rahmen. Keine Fläche und keine Farbe am
               Knopf selbst: zwei farbige Kacheln nebeneinander in der Ecke einer
               Karte, die schon eine Biomfarbe trägt, wären eine Farbe zuviel.
               Der Rahmen sagt »hier ist etwas zu drücken«, das Zeichen sagt was. */
            border: 1px solid oklch(from var(--ink-quiet) l c h / 0.45);

            color: var(--ink-quiet);
            font-size: 1.0625rem;
            line-height: 1;
            cursor: pointer;
        }

        /* Die Farbe trägt das Zeichen, nicht der Knopf. */
        :scope[data-tone="action"] { color: var(--link); }
        :scope[data-tone="gold"]   { color: var(--gold); }

        /* Beim Zeigen tritt der Rahmen an die Farbe des Zeichens heran — die
           einzige Bewegung, die keine ist: nichts springt, nichts wächst. */
        :scope:where(:hover, :focus-visible) {
            border-color: currentColor;
            background: oklch(from currentColor l c h / 0.12);
        }

        :scope:where(:focus-visible) {
            outline: 2px solid var(--link);
            outline-offset: 2px;
        }

        /* Der Chevron zeigt nach rechts, solange zu ist, und dreht beim Öffnen
           nach unten — der übliche Aufklappzeiger. Die Drehung sitzt am Zeichen
           und nicht am Knopf, damit der Rahmen stehen bleibt. */
        :scope[data-part="toggle"] span {
            display: block;
            transition: rotate 120ms ease;
        }

        @media (prefers-reduced-motion: reduce) {
            :scope[data-part="toggle"] span { transition: none; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Der Spendenknopf — weiss, gerundet, die Tasse davor. */

@layer elements.donate_button {
    @scope ([data-type="donate-button"]) {
        :scope {
            /* Das Mass kommt vom Kontext, die Form rechnet der Knopf daraus:
               Rundung, Innenabstand und Tasse wachsen mit der Hoehe. Ohne
               Vorgabe ist er so hoch wie jedes Bedienelement; der Fussstreifen
               der Karte gibt ihm eine niedrigere. */
            --hoehe: var(--spende-hoehe, var(--control-height));

            /* Symbol neben Text: die eine Reihe, fuer die Flex erlaubt ist. */
            display: inline-flex;
            align-items: center;
            gap: calc(var(--hoehe) * 0.22);
            block-size: var(--hoehe);
            padding-inline: calc(var(--hoehe) * 0.3) calc(var(--hoehe) * 0.42);

            /* Weiss und das Dunkel von Ko-fi, 32 32 32 in RGB — gemessen 16 zu 1. */
            background: oklch(100% 0 0);
            color: oklch(24.4% 0 0);
            border-radius: calc(var(--hoehe) * 0.3);

            font-size: var(--spende-schrift, var(--text-small));
            font-weight: 700;
            line-height: 1;
            white-space: nowrap;
            text-decoration: none;
        }

        img {
            block-size: calc(var(--hoehe) * 0.52);
            inline-size: auto;
        }

        /* Beim Zeigen tritt die Flaeche einen Hauch zurueck und die Tasse
           wackelt, wie auf Ko-fi selbst. Die Flaeche springt, die Tasse
           bewegt sich nur ueber transform ({K0036}). */
        :scope:where(:hover, :focus-visible) {
            background: oklch(93% 0 0);
        }

        :scope:where(:hover, :focus-visible) img {
            animation: tasse-wackeln 600ms ease-in-out;
        }

        /* Der Fokusring liest das eine Gruen wie jeder andere ({K0234}). */
        :scope:focus-visible {
            outline: 2px solid var(--link);
            outline-offset: var(--space-tight);
        }

        @media (prefers-reduced-motion: reduce) {
            :scope:where(:hover, :focus-visible) img { animation: none; }
        }
    }

    /* `@keyframes` kennt keinen Scope und steht darum daneben, mit einem Namen,
       der sagt, wem er gehoert. */
    @keyframes tasse-wackeln {
        0%, 100% { transform: rotate(0deg); }
        25%      { transform: rotate(-12deg); }
        50%      { transform: rotate(10deg); }
        75%      { transform: rotate(-6deg); }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Griffleiste am unteren Rand einer Karte. */

@layer elements.drawer_handle {
    @scope ([data-type="drawer-handle"]) {
            /* Die Griffleiste am unteren Rand — sechzehn Pixel, abgedunkelt,
               und sie liegt tiefer als die Karte.

               »Tiefer« ist hier keine Farbe, sondern eine Kante: der Schatten sitzt
               oben und innen, wie bei allem Eingelassenen in diesem Entwurf. Das
               Licht fällt für Feld, Spur und Leiste aus derselben Ecke, und wer die
               Richtung tauscht, macht aus der Vertiefung eine Erhebung.

               Über die ganze Breite und nicht als Knopf in der Ecke: ein Streifen
               ist ein größeres Ziel, und seine Lage sagt, wohin die Karte aufgeht. */
            :scope {
                position: absolute;
                inset-inline: 0;
                inset-block-end: 0;
                display: grid;
                place-items: center;
                /* Die Höhe kommt von dem, was den Griff trägt — die Karte
                   rechnet mit ihr, wenn sie ihren Inhalt mittig setzt. Der
                   Rückfall steht daneben, damit das Element auch allein steht:
                   ein Bauteil, das ohne seinen Aufrufer zusammenfällt, ist
                   keines. */
                block-size: var(--griff, 16px);
                padding: 0;
                border: 0;
                background: oklch(0% 0 0 / 0.30);
                box-shadow:
                    inset 0  2px 0 oklch(0% 0 0 / 0.38),
                    inset 0 -1px 0 oklch(100% 0 0 / 0.05);
                cursor: pointer;
            }

            /* Das Chevron gezeichnet, nicht gesetzt: ein Winkel aus zwei Kanten an
               einem gedrehten Quadrat. Ein Schriftzeichen ginge auch, aber »breit
               gezogen« hiesse dann, eine Glyphe zu verzerren — und eine verzerrte
               Glyphe sieht aus wie ein Fehler, während eine gezeichnete Linie
               einfach breit ist.

               `transform` und nicht die Einzeleigenschaften `rotate`/`scale`: dort
               liegt die Reihenfolge fest, und `scale` käme nach der Drehung, würde
               also die Diagonale strecken statt die Breite. Hier steht `scaleX`
               zuerst und wirkt damit von aussen. */
            :scope::before {
                content: "";
                inline-size: 5px;
                block-size: 5px;
                /* Hellgrün, nicht grau (Owner, 1. September 2026). Auf der
                   abgedunkelten Leiste ist es das einzige Farbige, und damit
                   findet das Auge den Griff, ohne daß er eine Fläche braucht.
                   Die Primaerfarbe — es gibt nur eine ({T0096}). */
                border-inline-end: 2px solid var(--link);
                border-block-end: 2px solid var(--link);
                transform: translateY(-2px) scaleX(2.4) rotate(45deg);
            }

            /* Beim Zeigen wird es heller statt anders: eine zweite Farbe
               hiesse, der Griff täte etwas anderes. */
            :scope:where(:hover, :focus-visible)::before {
                border-color: oklch(from var(--link) calc(l + 0.08) c h);
            }

            :scope:where(:focus-visible) {
                outline: 2px solid var(--link);
                outline-offset: -2px;
            }

        @media (prefers-reduced-motion: no-preference) {
            :scope::before { transition: transform 140ms ease; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ein Eingabefeld — eingelassen, mit Beschriftung darueber. */

@layer elements.field {
    @scope ([data-type="field"]) {
        :scope {
            display: grid;
            gap: 0.4375rem;
            min-inline-size: 0;
        }

        /* Der chip traegt sonst die Verweisfarbe — hier ist er eine
           Feldbeschriftung und keine Aussage, also gedaempft und 10 px.
           Aus dem Entwurf gemessen. */
        [data-type="chip"] {
            font-size: 0.625rem;
            letter-spacing: 0.12em;
            color: var(--ink);
        }

        input {
            min-inline-size: 0;
            /* Die Höhe kommt aus dem Token und nicht aus dem Innenabstand: so
               ist sie dieselbe wie beim Knopf, auch wenn eine Schrift anders
               fällt. Der Innenabstand richtet nur noch waagerecht aus. */
            block-size: var(--control-height);
            padding: 0 0.75rem;
            background: var(--surface-0);
            border: 0;
            color: oklch(90% 0.005 90);
            font-family: ui-monospace, monospace;
            font-size: 0.8125rem;
            letter-spacing: 0.04em;

            /* Die Kante liegt umgekehrt zum Knopf: dunkel oben links, hell unten
               rechts. Das ist kein Zierat, sondern die Aussage — ein Knopf steht
               hervor, ein Feld ist eingelassen, und das Licht fällt für beide aus
               derselben Richtung. Wer die Reihenfolge tauscht, macht aus dem Feld
               einen Knopf, den niemand drücken kann. Aus dem Entwurf gemessen. */
            box-shadow:
                inset  1.5px  1.5px 0 oklch(9% 0.008 var(--hue-surface)),
                inset -1.5px -1.5px 0 oklch(32% 0.014 var(--hue-surface));
        }

        /* min-inline-size: 0 am Feld ist kein Zierat: ein Eingabefeld bringt eine
           voreingestellte Mindestbreite mit, die eine 1fr-Spur ueberstimmt und die
           Zeile am Telefon aus dem Raster schiebt. */

        input::placeholder { color: var(--ink-quiet); }

        /* Der Fokusring liegt innen, damit er die Kante nicht überdeckt: das Feld
           behält seine Vertiefung und bekommt eine Fassung.

           In der Farbe des Knopfs daneben, nicht in der Aktionsfarbe: Feld und
           Knopf sind ein Paar, und ein Feld, das im Fokus ein anderes Grün
           trägt als der Knopf, der es absendet, sieht aus wie zwei Bauteile
           (Owner, 2. September 2026). Dasselbe Token wie `button`, damit es
           exakt dasselbe Grün ist und bleibt. */
        input:where(:focus-visible) {
            outline: 2px solid var(--link);
            outline-offset: -2px;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Item-Icons aus dem Spiel. */

@layer elements.game_icon {
    @scope ([data-type="game-icon"]) {
        :scope {
            inline-size: var(--space-wide);
            block-size: auto;

            /* Die Vorlagen sind Pixelgrafik. Ohne diese Zeile interpoliert der
               Browser beim Hochskalieren und macht aus harten Kanten Matsch —
               dasselbe Problem wie bei der Pixelschrift, nur ohne Rundung als
               Ausweg \({K0044}\). */
            image-rendering: pixelated;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Schriften — selbst ausgeliefert, mit Metrik-Rückfall.

   Kein Element im Dokumentkopf wird dargestellt, deshalb steht hier kein Scope:
   was dieses Paket an CSS beiträgt, sind die Schriftdeklarationen selbst. */

@layer tokens {
    @font-face {
        font-family: "Oxanium";
        src: url("/fonts/oxanium.woff2") format("woff2");
        font-weight: 200 800;
        font-display: swap;
    }

    @font-face {
        font-family: "Press Start 2P";
        src: url("/fonts/press-start-2p.woff2") format("woff2");
        font-weight: 400;
        font-display: swap;
    }

    /* Der Metrik-Rückfall: er nimmt dem Schriftwechsel den Sprung, ohne den Text
       zu verstecken ({K0039}). Die Werte sind an Oxanium gemessen — Aufstrich 790,
       Abstrich 210, Zeilenabstand 250 von 1000 Einheiten. */
    @font-face {
        font-family: "Oxanium Fallback";
        src: local("Helvetica Neue"), local("Arial");
        ascent-override: 79%;
        descent-override: 21%;
        line-gap-override: 25%;
        size-adjust: 99%;
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ueberschriften — Pixelschrift, auf ihr Raster gerundet. */

@layer elements.heading {
    @scope ([data-type="heading"]) {
        :scope {
            font-family: "Press Start 2P", monospace;
            font-weight: 400;
            color: var(--ink-bright);
            text-wrap: pretty;

            /* Die Pixelschrift hat keine Unterlaenge im Metriksinn, deshalb steht
               die Zeilenhoehe hier immer ausgeschrieben \({K0044}\). 1.4 laesst
               zwischen zwei Zeilen genau eine halbe Zeichenhoehe. */
            line-height: 1.55;
        }

        /* KEINE Rundung auf das Pixelraster. Sie war hier gedacht und ist hier
           falsch: der Entwurf gibt 28, 22 und 13 px vor, und round\(\) verschiebt
           jeden dieser Werte auf das nächste Vielfache — aus 27,5 würden 24, aus
           13 würden 16. Wo der Entwurf eine Größe setzt, gilt sie; die Rundung
           bleibt dem vorbehalten, was wir selbst wählen ({K0059}).

           Die Leiter trifft die Werte ohnehin fast genau: 27,5 gegen 28,
           21,6 gegen 22, 13,4 gegen 13. */
        :scope:is(h1) { font-size: var(--text-head); }
        :scope:is(h2) { font-size: var(--text-head); }
        :scope:is(h3) { font-size: var(--text-large); }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ein Knopf, der nur ein Zeichen traegt. */

@layer elements.icon_button {
    @scope ([data-type="icon-button"]) {
        :scope {
            display: grid;
            place-items: center;
            inline-size: var(--space-open);
            aspect-ratio: 1;
            background: none;
            border: 0;
            color: var(--ink-quiet);
            font-size: var(--text-large);
            line-height: 1;
            cursor: pointer;
        }

        /* Keine Fläche, kein Rahmen: die Farbe ist die ganze Rückmeldung. Ein
           Knopf ohne Fläche, der beim Zeigen eine bekäme, spränge — und die
           Aktionsfarbe sagt dasselbe, ohne das Layout anzufassen. */
        :scope:where(:hover, :focus-visible) { color: var(--link); }

        /* **Das × im Kopf eines Blattes** ({T0084}, Owner am 11. September
           2026: »der schliessen button im popover für download und für share
           entspricht nicht dem was im item popover zu sehen ist«).

           Es ist doppelt so groß wie ein gewöhnliches Zeichen, weil es als
           einziges Bedienelement in einer Kopfzeile steht, die sonst nur Text
           trägt — und weil ein Blatt sich schließen lassen muß, bevor man
           sucht, wo.

           Golden beim Zeigen und nicht grün: Grün ist die Handlungsfarbe, und
           Schließen ist keine Handlung, sondern ihr Ende. Die Farbe des
           Blattrahmens sagt statt dessen »dieses Blatt«.

           Die Maße standen im Gegenstandsblatt und nirgends sonst, weshalb die
           Kartenblätter ein kleineres × trugen. Hier stehen sie einmal, und
           jedes Blatt bekommt dasselbe; **wo** es sitzt, entscheidet weiter
           das Blatt. */
        :scope[data-rolle="schliessen"] {
            inline-size: calc(var(--space-open) * 2);
            font-size: calc(var(--text-large) * 2);
        }

        :scope[data-rolle="schliessen"]:where(:hover, :focus-visible) {
            color: var(--gold);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Verweise — eine Stelle, an der Farbe, Fokus und Zustand festliegen. */

@layer elements.link {
    @scope ([data-type="link"]) {
        :scope {
            color: var(--link);
            text-decoration: none;
        }

        /* Der Fokusring steht hier und nur hier. Das ist der eigentliche Gewinn
           des Elements: eine Stelle statt vier, und keine Chance, dass eine davon
           vergessen wird. :focus-visible statt :focus, damit er bei der Maus nicht
           erscheint — wer klickt, sieht wohin er geklickt hat. */
        :scope:where(:focus-visible) {
            outline: 2px solid var(--link);
            outline-offset: var(--space-tight);
        }

        :scope:where(:hover) {
            text-decoration: underline;
            text-underline-offset: var(--space-tight);
        }

        /* Die aktuelle Seite wird aus dem Zustand gelesen, nicht aus einer Klasse:
           aria-current sagt es dem Screenreader ohnehin, und ein zweiter Traeger
           derselben Aussage koennte von ihr abweichen \({K0005}\). */
        :scope[aria-current] {
            text-decoration: underline;
            text-underline-offset: var(--space-tight);
        }

        /* Das Chevron rueckt beim Ueberfahren nach, nicht der ganze Verweis: eine
           Bewegung ueber transform, die den Aufbau nicht anfasst \({K0036}\). */
        > span {
            display: inline-block;
            margin-inline-start: 0.35em;
            transition: transform 120ms ease-out;
        }

        :scope:where(:hover, :focus-visible) > span {
            transform: translateX(0.15em);
        }

        @media (prefers-reduced-motion: reduce) {
            > span { transition: none; }
            :scope:where(:hover, :focus-visible) > span { transform: none; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Zeichen der Marke — Flaeche plus Maske, beide Farben aus dem Theme. */

@layer elements.logo_picture {
    @scope ([data-type="logo-picture"]) {
        :scope {
            display: grid;
            place-items: center;
            inline-size: 1.875rem;
            aspect-ratio: 1;
            background: var(--brand);

            /* Dieselbe Kante wie der Startknopf, nur schmaler: anderthalb Pixel.
               Sie macht aus dem Feld ein Objekt statt einer Farbfläche. Aus dem
               Entwurf gemessen. */
            box-shadow:
                inset  1.5px  1.5px 0 oklch(from var(--brand) calc(l + 0.14) c h),
                inset -1.5px -1.5px 0 oklch(from var(--brand) calc(l - 0.16) c h);
        }

        /* Das Signet als Maske auf einer Flaeche in der Grundfarbe. Kein
           zusaetzlicher Knoten, und ein Themewechsel aendert zwei Variablen. */
        /* 62 Prozent. Der Wert war nie das Problem — die Ansicht des Zeichens
           war es: sie lag auf dem Vorschub statt auf der Tinte, und die letzten
           125 Einheiten davon sind der Abstand zum nächsten Buchstaben. Das
           Zeichen saß dadurch links im Feld, und Vergrößern verdeckte den Fehler,
           statt ihn zu beheben. Seit die Ansicht auf der Tinte liegt, sitzt es
           mittig, und 62 Prozent lassen ihm wieder Luft. */
        :scope::before {
            content: "";
            inline-size: 62%;
            aspect-ratio: 1;
            background: var(--ground);
            mask-image: url("/brand/signet.svg");
            mask-size: contain;
            mask-repeat: no-repeat;
            mask-position: center;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Wortmarke. */

@layer elements.logo_text {
    @scope ([data-type="logo-text"]) {
        :scope {
            font-family: "Press Start 2P", monospace;
            font-weight: 400;
            font-size: 0.875rem;
            line-height: 1;
            letter-spacing: 0.04em;
            color: var(--ink);
        }

        /* b und i tragen hier keine Betonung, sondern Auszeichnung ohne Stimme —
           genau wofuer die beiden Elemente seit HTML5 gedacht sind. Die Kursive
           wird zurueckgenommen, weil die Pixelschrift keine hat und der Browser
           sie sonst schraeg stellt. */
        > i {
            font-style: normal;
            color: var(--brand);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Kennzahl ueber Beschriftung. */

@layer elements.metric {
    @scope ([data-type="metric"]) {
        :scope {
            display: grid;
            gap: var(--space-tight);
        }

        /* Der Wert steht im Markup vor der Beschriftung, weil er die Aussage ist —
           die Beschriftung erklaert ihn. Im Raster stehen sie in derselben
           Reihenfolge; es gibt keinen Grund, sie visuell zu tauschen. */
        > dd {
            margin: 0;
            font-family: ui-monospace, monospace;
            font-size: var(--text-large);
            color: var(--link);
        }

        > dt {
            font-family: ui-monospace, monospace;

            /* 12 px liegt zwischen zwei Stufen der Leiter — 10,5 und 13,4. Der
               Entwurf setzt es so, und AW22 gibt ihm recht: eine Beschriftung
               unter einer Zahl braucht Gewicht, ohne mit ihr zu konkurrieren. */
            font-size: var(--text-label);
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--ink-quiet);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Bild folgt seiner Spur und hält sein Seitenverhältnis. */

@layer elements.picture {
    @scope ([data-type="picture"]) {
        :scope {
            display: block;
            max-inline-size: 100%;
            block-size: auto;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Eine Pille — Optionsfeld mit Rangfarbe, das goldene Dreieck über der gewählten. */

@layer elements.pill {
    @scope ([data-type="pill"]) {
        :scope {
            /* Der Rang als Farbe. Weiß ist der Ruhewert und zugleich der
               erste Rang — deshalb kann die Farbe nicht auch aktiv gegen
               inaktiv sagen: das täte sie zweimal. Aktiv sagt die Form. */
            --pill: var(--rank-1);
            position: relative;
            display: inline-grid;
            /* Platz für das Dreieck über der Pille, im eigenen Kasten und
               nicht als negativer Versatz (AW5). */
            padding-block-start: var(--space-snug);
            cursor: pointer;
        }

        :scope[data-rank="2"] { --pill: var(--rank-2); }
        :scope[data-rank="3"] { --pill: var(--rank-3); }
        :scope[data-rank="4"] { --pill: var(--rank-4); }

        /* Das Optionsfeld bleibt ein Optionsfeld — Tastatur, Vorleser und
           Gruppe merken nichts —, liegt aber unsichtbar über der ganzen Pille,
           damit jeder Treffer zählt. */
        input {
            appearance: none;
            position: absolute;
            inset: 0;
            margin: 0;
            opacity: 0;
            cursor: pointer;
        }

        /* Die Pille selbst: eine Umrandung in der Rangfarbe, die Ziffer
           darin. Aus dem Entwurf gemessen — umrandet, nicht gefüllt, mit
           leicht gebrochener Ecke; die anderthalb Pixel sind die Kante von
           Marke und Feld ({K0062}). */
        span {
            display: grid;
            place-items: center;
            min-inline-size: 2.4em;
            block-size: 1.6em;
            padding-inline: 0.4em;
            border: 1.5px solid var(--pill);
            border-radius: 0.2em;
            color: var(--pill);
            font-family: ui-monospace, monospace;
            font-size: var(--text-micro);
            line-height: 1;
            font-variant-numeric: tabular-nums;
        }

        /* Das goldene Dreieck über der gewählten Pille, mittig, mit der
           Spitze nach unten (Owner, 2. September 2026). Gold heißt im Blatt
           »gehört hierher« — Ecke, Rahmen, Orte —, und die gewählte Stufe
           gehört dazu. Zwei Kanten ohne Fläche ergeben die Form; ein SVG wäre
           eine Datei für drei Striche. */
        :scope::before {
            content: "";
            position: absolute;
            inset-block-start: 0;
            inset-inline: 0;
            margin-inline: auto;
            inline-size: 0;
            block-size: 0;
            border-inline: 0.3em solid transparent;
            border-block-start: 0.35em solid var(--gold);
            opacity: 0;
        }

        :scope:has(:checked)::before { opacity: 1; }

        /* Der Fokusring in der Aktionsfarbe, außen um die Pille — vom
           Dreieck darüber durch Form, Ort und Farbe unterschieden. */
        input:where(:focus-visible) ~ span {
            outline: 2px solid var(--link);
            outline-offset: 2px;
        }

        /* Beim Zeigen ein Hauch der eigenen Farbe auf der Fläche: sagt
           »diese ist gemeint«, ohne den Rang zu ändern. */
        :scope:hover span {
            background: color-mix(in oklab, var(--pill) 12%, transparent);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Der Fortschrittsbalken — Rahmen, Fuellung, Groesse. */

@layer elements.progress {
    @scope ([data-type="progress"]) {
        /* Hundert mal fuenfzehn Pixel: ein Prozent je Pixel, wie der Owner es
           festgelegt hat. Das ist der eine Ort, an dem eine Pixelzahl richtig
           ist — sie ist die Aussage, nicht das Mass. */
        :scope {
            inline-size: 100px;
            block-size: 15px;
            appearance: none;
            border: 1px solid oklch(72% 0.13 85);
            background: oklch(20% 0.02 260 / 0.8);
        }

        /* Zwei Schreibweisen fuer dieselbe Sache: WebKit und Firefox nennen
           die Fuellung verschieden, und ein Selektor, den ein Browser nicht
           kennt, wirft in manchen Fassungen die ganze Regel weg. */
        :scope::-webkit-progress-bar {
            background: oklch(20% 0.02 260 / 0.8);
        }
        :scope::-webkit-progress-value {
            background: var(--gold, oklch(80% 0.15 85));
            transition: inline-size 0.2s linear;
        }
        :scope::-moz-progress-bar {
            background: var(--gold, oklch(80% 0.15 85));
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Fliesstext. */

@layer elements.prose {
    @scope ([data-type="prose"]) {
        :scope {
            font-size: var(--text);
            line-height: 1.6;
            color: var(--ink);

            /* In ch, nicht in rem: die Zeilenlaenge ist eine Frage der Zeichen je
               Zeile und nicht der Breite in Pixeln. 66 ist der obere Rand dessen,
               was sich noch bequem liest. */
            max-inline-size: 58ch;
            text-wrap: pretty;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ein quadratischer Knopf mit Flaeche fuer ein Bild oder ein Zeichen ({T0106}). */
@layer elements.quick_button {
    @scope ([data-type="quick-button"]) {
        /* **Er sieht aus wie `button`, nur quadratisch und ohne Wort** (Owner,
           14. September 2026: »der goldene button sieht exakt genauso aus wie
           der zufall-button im geoeffneten, nur eben mit dem wuerfel darauf«).
           Also dieselbe Pixelkante: zwei Innenschatten, hell oben links und
           dunkel unten rechts, zwei Pixel breit, die Werte aus button.css
           unveraendert. Die Groesse sagt der Aufrufer ueber `--quick-size`;
           der Ruhewert ist die Hoehe eines Knopfs, damit er neben einem
           stehen koennte. Im Streifen des Panels ist er so breit wie der
           Streifen abzueglich Rand. */
        :scope {
            display: grid;
            place-items: center;
            inline-size: var(--quick-size, var(--control-height));
            /* Quadratisch, solange der Aufrufer keine Hoehe gibt; im Streifen
               des Panels teilen sich zwei Knoepfe die Hoehe (Owner,
               14. September 2026: »vertikal im grid mit gap, aber beide den
               platz nutzend«), dort setzt der Aufrufer --quick-block. */
            block-size: var(--quick-block, var(--quick-size, var(--control-height)));
            padding: 0;
            border: 0;
            text-decoration: none;
            line-height: 1;
            cursor: pointer;
            /* Der stille Ton: dunkles Grau, auf dem die Kante sichtbar bleibt
               (Owner: »mit einem dunklen grau, das die erhabenheit gut
               sichtbar macht«). Die Kanten folgen dem Ton wie bei jedem
               Knopf: hell aus der Flaeche heraus, dunkel in sie hinein. */
            background: oklch(34% 0.012 260);
            color: var(--gold);
            box-shadow:
                inset  2px  2px 0 oklch(52% 0.012 260),
                inset -2px -2px 0 oklch(20% 0.012 260);
        }
        :scope:where(:hover, :focus-visible) {
            background: oklch(39% 0.012 260);
        }
        /* Gold: Flaeche, Schrift und Kanten wie am Zufallsknopf (button.css,
           Ton gold) - Werte unveraendert, damit beide dieselbe Fassung tragen. */
        :scope[data-tone="gold"] {
            background: var(--gold);
            color: oklch(24% 0.06 85);
            box-shadow:
                inset  2px  2px 0 oklch(90% 0.09 85),
                inset -2px -2px 0 oklch(58% 0.11 85);
        }
        :scope[data-tone="gold"]:where(:hover, :focus-visible) {
            background: oklch(from var(--gold) calc(l + 0.05) c h);
        }
        :scope:where(:focus-visible) {
            outline: 2px solid var(--link);
            outline-offset: 2px;
        }
        /* Das Bild laesst rundum Flaeche stehen, sonst sieht der Knopf aus
           wie ein Bild mit Rand. Sieben Zehntel sind aus dem Entwurf des
           Owners gemessen: rund 56 auf 80. */
        :scope > [data-type="picture"] {
            inline-size: 70%;
            block-size: 70%;
            object-fit: contain;
        }
        /* Der goldene Stern des Favoritenknopfs: leer, solange nichts
           gemerkt ist, gefuellt danach - derselbe Zustand wie am Stern der
           Titelzeile, gesetzt vom Skript ueber data-gemerkt. Die Groesse
           folgt dem Knopf, nicht der Schrift der Seite. */
        :scope > span {
            font-size: calc(var(--quick-size, var(--control-height)) * 0.55);
        }
        /* Der Wuerfelpfeil ist schwarz auf Gold (Owner: »einen schwarzen
           reroll pfeil«) - dieselbe dunkle Schrift wie das Wort ZUFALL. */
        :scope[data-tone="gold"] > span { color: oklch(24% 0.06 85); }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ein Bildstreifen als Hintergrund einer Leiste. */

@layer elements.strip {
    @scope ([data-type="strip"]) {
        :scope {
            position: absolute;
            inset: 0;
            inline-size: 100%;
            block-size: 100%;
            object-fit: cover;
            /* Gedämpft, weil darüber Text steht: das Bild ist Stimmung und
               nicht Inhalt. */
            opacity: 0.55;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Der Bedienstreifen — Anordnung, Flaeche und die Schrift darauf. */

@layer widgets.control_bar {
    @scope ([data-type="control-bar"]) {
        /* `flex-wrap` statt eines Haltepunkts — wird es eng, rutschen die Regler
           unter die Suche. Die Zeilenrinne ist größer als die Spaltenrinne,
           damit die beiden dann als zwei Sachen lesbar bleiben statt als eine
           gedrängte. Dieselbe Bauart wie ein Raster mit `auto-fill`: kein Media
           Query, der eine Breite rät, die flex ohnehin kennt.

           `space-between` und kein `margin-inline-start: auto` an den Reglern.
           Beide schieben sie nach rechts, solange die Zeile geteilt ist — aber
           nach dem Umbruch stehen sie allein, und dann setzt `auto` sie an den
           rechten Rand, wo nichts mehr ist, womit sie bündig wären. */
        :scope {
            display: flex;
            flex-wrap: wrap;
            align-items: end;
            justify-content: space-between;
            gap: 1.5rem 2rem;
            padding: 1rem 1.25rem;
            /* Der Streifen ist ein Container: die Regler antworten auf seine
               Breite, nicht auf das Fenster (how-we-build.md). */
            container-type: inline-size;

            /* Zehn Prozent Deckkraft: der Streifen hebt sich ab, ohne eine
               eigene Fläche zu werden. Er bleibt ein aufgehellter Ausschnitt
               des Seitengrunds, und die Seite behält ihr Dunkel.

               Symmetrisch in beiden Modi — im Hellen dieselben zehn Prozent in
               Schwarz. Die Aussage ist »etwas näher am Betrachter«, und die gilt
               in beide Richtungen. */
            background: light-dark(oklch(0% 0 0 / 0.1),
                                   oklch(100% 0 0 / 0.1));

            /* Die Pixelkante einer erhöhten Fläche: hell oben links, dunkel
               unten rechts, wie Gebietsblock und Karte. */
            box-shadow:
                inset  2px  2px 0 oklch(33% 0.014 var(--hue-surface)),
                inset -2px -2px 0 oklch(12% 0.010 var(--hue-surface));

            /* Die drei Ink-Stufen eine Stufe kräftiger, in beide Richtungen.

               Der Streifen liegt im Dunkelmodus bei rund 26 Prozent Helligkeit
               und damit über --surface-4; die Leiter in tokens.css ist bis
               --surface-2 geprüft. --ink-quiet stünde dort bei 3,5 zu 1 und
               fiele durch. Auf einer aufgehellten Fläche muß die Schrift es
               auch sein.

               Am Container gesetzt und nicht in den Elementen überschrieben:
               field, button und checkbox lesen weiter dieselben Tokens, die sie
               immer lesen — die sagen hier etwas anderes. Ein Element, das
               morgen dazukommt, ist ohne Zutun richtig. Die Begründung steht in
               manual/web/03-tokens.md. */
            --ink:        light-dark(oklch(24% 0.008 var(--hue-surface)),
                                     oklch(82% 0.008 var(--hue-surface)));
            --ink-quiet:  light-dark(oklch(36% 0.008 var(--hue-surface)),
                                     oklch(72% 0.008 var(--hue-surface)));
            --ink-bright: light-dark(oklch(16% 0.005 90),
                                     oklch(95% 0.005 90));
        }

        /* Die Suche wächst nicht in den Freiraum: ein Feld von neunhundert
           Pixeln neben sechs Reglern sieht nicht nach zwei Bedienungen aus,
           sondern nach einer verunglückten. Sie nimmt 26 rem und gibt davon ab,
           wenn es eng wird. */
        [data-type="search-form"] { flex: 0 1 26rem; }

        /* Die Regler, mobile first: ein Raster mit zwei exakt gleich breiten
           Spalten und 8 px Lücke, über die ganze Breite des Streifens, damit
           die sechs Schalter untereinander fluchten (Owner, 2. September 2026).
           `minmax(0, 1fr)` und nicht `1fr`: eine Spalte darf nicht breiter
           werden, weil ihr Wort länger ist — sonst stünden die Schalter der
           rechten Spalte nicht mehr auf einer Linie.

           Die Reglerreihe sitzt auf Höhe des Feldes, nicht auf Höhe seiner
           Beschriftung: der Streifen richtet an der Unterkante aus, und ohne die
           Mindesthöhe stünden die Regler am Boden des Etiketts. */
        /* Die Zeile unter den Reglern ({T0052}) nimmt eine eigene Zeile des
           Streifens — sonst stünde sie neben ihnen, sobald der Streifen in
           einer Reihe läuft. Was sie sagt, gehört der Seite. */
        [data-part="options-note"] {
            flex: 1 1 100%;
            /* Mittig unter den Reglern und sechzehn Punkte darunter (Owner,
               7. September 2026): sie gehört zu ihnen, steht aber nicht in
               ihrer Reihe. */
            margin: 16px 0 0;
            text-align: center;
        }

        /* Der Behaelter der Gruppen traegt ihren Abstand ({T0093}, Owner,
           11. September 2026). **Ein Raster mit `gap` und keine Abstaende an
           den Gruppen selbst**: ein `margin` am zweiten Formular gaelte auch
           dort, wo gar kein erstes steht, und muesste dann wieder
           weggeregelt werden. Der `gap` gilt nur zwischen zweien — gibt es
           nur eine, gibt es nichts zu trennen.

           Er steht hier und nicht am Streifen, weil der Streifen auf der
           Kartenseite `display: block` traegt: dort gibt es keine Suche,
           neben die etwas zu legen waere, und ein Block kennt kein `gap`.
           Genau deshalb stapelten sich die beiden Gruppen dort ohne jeden
           Abstand, obwohl am Streifen eine Zeilenrinne stand.

           `--space-open` ist die Stufe ueber der Grundstufe, Faktor 1.618:
           22,6 bis 27,5 Punkte je nach Breite. Sie muss deutlich groesser
           sein als der Abstand **innerhalb** einer Gruppe — sonst gehoert
           das Etikett optisch zur Reihe darueber statt zu seiner eigenen. */
        [data-part="gruppen"] {
            display: grid;
            gap: var(--space-open);
            flex: 1 1 100%;
        }

        /* Das Formular traegt nichts mehr als seine Semantik; die Breite
           kommt vom Raster darueber, die Anordnung vom <fieldset> darin. */

        [data-part="gruppe"] {
            /* Ein <fieldset> bringt Rahmen, Innenabstand und eine
               Mindestbreite aus dem Browser mit, und alle drei stehen dem
               Raster im Weg. `min-inline-size: 0` ist der unbekannteste davon:
               ohne ihn nimmt ein fieldset `min-content` als Untergrenze und
               laesst sich in einem Flex- oder Grid-Kind nicht schmaler
               rechnen, als sein laengstes Wort ist. */
            border: 0;
            padding: 0;
            margin: 0;
            min-inline-size: 0;

            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
            align-items: center;
            gap: 0.5rem;
            min-block-size: var(--control-height);
        }

        /* Der Bereichsmarker (Owner, 11. September 2026): das Etikett in der
           Schrift jedes anderen — Monospace, Versalien, gesperrt — und darunter
           die Linie ueber die volle Breite.

           **Die Linie nimmt `currentcolor`.** Damit steht sie nie in einer
           anderen Farbe als ihr Etikett, ohne dass der Ton zweimal
           geschrieben waere ({K0004}); wer die Gruppe umfaerbt, faerbt beide.

           Die Abstaende kommen aus der Leiter und nicht als Zahl: `tight`
           haelt Etikett und Linie als ein Zeichen zusammen, `close` setzt den
           Abstand zu den Schaltern. `close` traegt den Faktor 0.618 und ist
           damit die Stufe des Goldenen Schnitts selbst — sie liegt je nach
           Breite bei 8,7, 9,3 und 10,5 Punkten, auf der schmalen also fast
           genau auf den genannten acht. */
        [data-part="gruppe"] > legend {
            inline-size: 100%;
            padding-block-end: var(--space-tight);
            margin-block-end: var(--space-close);
            border-block-end: 1px solid currentcolor;
        }

        /* Ab 35 rem Streifenbreite — die Tablet-Stufe der Leiter, hier als
           Container-Abfrage — wieder die Reihe, die umbricht, wenn es eng
           wird; die Zeilenrinne bleibt bei 8 px. */
        @container (min-width: 35rem) {
            [data-part="gruppen"] { flex: 0 1 auto; }

            [data-part="gruppe"] {
                display: flex;
                flex-wrap: wrap;
                gap: 0.5rem 1.5rem;
            }

            /* Stehen zwei Gruppen da, nimmt der Behaelter die volle Breite
               ({T0093}) — sonst rutschte er neben die Suche und die beiden
               waeren zusammen so schmal wie eine. Ohne obere Reihe aendert
               sich nichts: `:has` greift dann nicht. */
            :scope:has([data-reihe="lead"]) [data-part="gruppen"] {
                flex: 1 1 100%;
            }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Der Rechtsvermerk — leise, aber lesbar. */

@layer widgets.footer_legal {
    @scope ([data-type="footer-legal"]) {
        :scope {
            display: grid;
            gap: var(--space-tight);
            justify-items: center;
            text-align: center;

            /* 80 Zeichen — die Breite, bei der eine Zeile noch in einem Blick
               erfaßt wird. In ch gemessen und nicht in rem: die Grenze ist eine
               Zahl von Zeichen, keine Länge, und sie bleibt richtig, wenn jemand
               die Schrift größer stellt. */
            max-inline-size: 80ch;
            margin-inline: auto;

            /* Abstand nach oben: der Vermerk gehört zum Fuß, steht aber nicht auf
               einer Ebene mit Name und Spieleliste. */
            margin-block-start: var(--space-open);
        }

        /* Kleiner als der Fliesstext, aber nicht kleiner als das, was noch
           bequem zu lesen ist. Ein Rechtsvermerk, den niemand entziffern kann,
           erfuellt seinen Zweck nicht — er soll leise sein, nicht versteckt. */
        :scope :where([data-type="prose"], small) {
            font-size: var(--text-small);
            color: var(--ink-quiet);
            max-inline-size: none;
        }

        > p { margin: 0; }

        /* Die Pflichtverweise stehen ueber dem Vermerk und nebeneinander: sie
           sind zwei Ziele und keine Liste, und ein ul mit zwei Eintraegen ist
           eine Aufzaehlung, die keine ist \(AW2\). Der Abstand trennt sie von
           den Saetzen darueber, ohne eine Linie zu ziehen. */
        [data-part="links"] {
            display: flex;
            gap: var(--space-open);
            justify-content: center;
            flex-wrap: wrap;
            margin-block-start: var(--space-tight);
        }

        /* Ziffern gleicher Breite, damit die Jahreszahl nicht flimmert.

           **Keine Versalien mehr.** Sie standen hier, solange die Zeile eine
           Signatur war — Vioxide // Vivian Voss // Anno MMXXVI. Der Owner hat am
           9. September 2026 die Schreibweise selbst gesetzt \({T0077}\), und die
           Darstellung schreibt sie nicht um: ein Rechtsvermerk zeigt, was dort
           steht. Die Marken tragen ihre Versalien im Text \(VGA.RUN, VIOXIDE
           SARL\), der Name des Menschen daneben nicht. Nebenbei liest ein
           Vorleseprogramm durchgehende Versalien oft als Abkürzung. */
        small {
            font-family: ui-monospace, monospace;
            font-variant-numeric: tabular-nums;
            letter-spacing: 0.02em;
        }

        /* Die Trägerzeile steht unter dem Vermerk und leiser als er: sie nennt
           die Gesellschaft, das Vollständige steht im Impressum. Eine Stufe
           kleiner reicht dafür — darunter beginnt das, was niemand mehr liest,
           und ein Vermerk, den niemand entziffert, erfüllt seinen Zweck nicht. */
        [data-part="carrier"] small {
            font-size: 0.8125rem;
            letter-spacing: 0.01em;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Buehnenleiste — Streifen ueber die volle Breite, Titel darauf. */

@layer widgets.header_banner {
    @scope ([data-type="header-banner"]) {
        :scope {
            display: grid;
            grid-template-columns: 1fr;
            align-items: start;
            gap: var(--space-close);
            padding-block: 2.125rem 1.875rem;
            padding-inline: max(var(--gutter), (100% - var(--measure)) / 2);
            border-block-end: 1px solid oklch(30% 0.014 260);
            color: var(--stage-ink);

            /* Die Streifen entstehen aus einem wiederholten Verlauf, nicht aus
               einem Bild: keine Anfrage, keine Datei, und die Farben kommen aus dem
               Theme. Ein zweites Spiel aendert drei Variablen und bekommt seine
               eigene Leiste.

               Der Verlauf steht im Theme und nicht hier: er ist das, was je Spiel
               wechselt. Die Leiste selbst bleibt dieselbe Form. */
            background: var(--stage-scan), var(--stage-bar);
            background-size: 100% 3px, auto;
            background-repeat: repeat, no-repeat;
        }

        /* Der Titel steht auf den Streifen und braucht Halt gegen sie. Ein Schatten
           in der tiefsten Farbe der Leiste selbst, nicht in Schwarz: er sitzt damit
           in derselben Familie und wirkt als Kante, nicht als Schmutz. */
        /* 44 px — die größte Schrift der Seite und die einzige, die nicht auf der
           Leiter liegt. Sie ist die Bühne selbst: der Spielname, und er soll die
           Leiste füllen, nicht in ihr stehen. Aus dem Entwurf gemessen.

           Der Schatten ist hart und versetzt, drei Pixel nach rechts unten — kein
           weicher Schein. So warfen Bildschirme dieser Zeit ihre Schrift, und es
           ist derselbe Grund, aus dem die Leiste ein Zeilenraster trägt. */
        [data-type="heading"] {
            font-size: var(--text-stage);
            line-height: 1.2;
            color: oklch(99% 0.01 90);
            text-shadow: 3px 3px 0 oklch(22% 0.06 34);
        }

        /* Die Datumszeile: 13 px, weit gesperrt, in der hellen Bühnenfarbe. Der
           chip bringt seine eigene Größe mit und wird hier überschrieben, weil er
           auf der Leiste steht und nicht im Text. */
        [data-type="chip"] {
            font-size: 0.8125rem;
            letter-spacing: 0.12em;
            color: oklch(97% 0.02 70);
            padding-block-end: 0.5rem;
        }

        /* Die Erweiterung, unter dem Namen. Kleiner und zurückgesetzt: sie sagt,
           welcher Inhalt gilt, und das ist eine Auskunft und keine Ansage. Der
           Schriftschnitt bleibt derselbe wie beim Titel — es ist dieselbe Sache,
           nur eine Ebene tiefer.

           Kein eigener Rasterplatz: die Zeile steht im Fluß unter dem Titel und
           ist bedingt. Ein Spiel ohne Erweiterung zeigt sie nicht, und die
           Leiste darf davon nicht springen. */
        [data-type="edition"] {
            margin: 0;
            font-size: var(--text-head);
            line-height: 1.1;
            letter-spacing: 0.02em;
            color: oklch(88% 0.06 70);
        }

        /* Ab tablet steht die Notiz neben dem Titel statt darunter. Nur der
           Unterschied, und nur nach oben — die Basis ist die schmale Form. */
        /* Ab tablet zwei Spalten: links Titel und Erweiterung untereinander,
           rechts die Notiz. Die Erweiterung gehört unter den Namen und nicht
           daneben — sie sagt, welcher Inhalt gilt, und das liest man als
           Fortsetzung des Titels, nicht als zweite Angabe am anderen Ende.

           Benannte Bereiche statt Zählen: mit drei Kindern und zwei Spalten
           landet sonst das dritte an einer Stelle, die niemand gewählt hat —
           genau das ist hier passiert. */
        @media (min-width: 35rem) {
            :scope {
                grid-template-columns: 1fr auto;
                grid-template-areas:
                    "titel   notiz"
                    "edition notiz";
                align-items: end;
            }
            [data-type="heading"] { grid-area: titel; }
            [data-type="edition"] { grid-area: edition; }
            [data-type="chip"]    { grid-area: notiz; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Zeichen und Wortmarke nebeneinander. */

@layer widgets.header_logo {
    @scope ([data-type="header-logo"]) {
        :scope {
            display: grid;
            grid-auto-flow: column;
            align-items: center;
            gap: 0.75rem;
            text-decoration: none;

            /* Der Verweis fuellt seine Zelle nicht aus — er ist so breit wie sein
               Inhalt. Ohne das griffe die Streckung des Kopfrasters durch und die
               anklickbare Flaeche reichte bis zur Navigation. */
            justify-self: start;
        }

        :scope:where(:focus-visible) {
            outline: 2px solid var(--link);
            outline-offset: var(--space-tight);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Navigation — Schalter am Telefon, offene Liste ab screen. */

@layer widgets.header_menu {
    @scope ([data-type="header-menu"]) {
        /* Das nav reicht seine beiden Kinder an das Kopfraster durch, statt sie
           einzuschliessen. Sonst muessten Schalter und Liste sich einen Bereich
           teilen, und der Schalter stuende am Telefon dort, wo ab screen die Liste
           beginnt — also in der Mitte statt aussen. */
        :scope { display: contents; }

        /* Der Knopf ist seit dem Element icon_button kein nacktes <button>
           mehr. Der alte Selektor griff nicht mehr, und die Regel, die ihn ab
           screen ausblendet, lief ins Leere: er stand auf dem Schreibtisch und
           machte die Kopfleiste höher. Gefunden hat es der Bildvergleich, nicht
           das Markup — dort steht ein Knopf, der stehen darf. */
        [data-type="icon-button"] {
            grid-area: toggle;
        }

        /* Kein display hier: ein geschlossenes Popover ist display:none, und jede
           Angabe an dieser Stelle haelt es offen. Das Raster steht deshalb erst im
           geoeffneten Zustand und ab screen, wo es ohnehin im Fluss liegt. */
        > ul {
            grid-area: menu;
            gap: var(--space-close);
        }

        > ul:popover-open {
            display: grid;
        }

        /* Am Telefon steht die Liste als Popover ueber dem Inhalt. Der Browser
           bringt Fokusfalle, Escape und das Schliessen bei Klick daneben mit —
           alles, was sonst als Skript geschrieben wuerde. */
        > ul:where([popover]) {
            margin: 0;
            padding: var(--space-open);
            background: var(--ground-up);
            border: 1px solid var(--surface-4);
        }

        /* Farbe, Fokusring und Zustand des Verweises stehen im link-Element und
           nicht hier — eine Stelle statt vier \({K0054}\). Das Widget bestimmt
           nur, wie die Verweise angeordnet sind, und ueberschreibt die Farbe fuer
           die Kopfzeile, wo sie gedaempfter sitzt als im Fliesstext. */
        /* Die Typographie hängt am Eintrag, nicht am Verweis: ein Reiter ohne
           Ziel (span, »soon«) trägt dieselbe Schrift, dieselben Versalien und
           dieselbe Sperrung wie einer mit — er unterscheidet sich nur in Farbe
           und Zeiger. Vorher hing sie am link, und »Bauplan« und »Spiele«
           fielen in die Grundschrift zurück (Owner, 3. September 2026, T0026 QS). */
        li {
            font-family: ui-monospace, monospace;
            font-size: var(--text-small);
            letter-spacing: 0.08em;
            text-transform: uppercase;
        }

        [data-type="link"]:not([aria-current]) { color: var(--ink-quiet); }
        [data-type="link"]:where(:hover, :focus-visible) { color: var(--ink); }

        /* Ein Reiter, dessen Seite es noch nicht gibt. Er steht im Fluß wie die
           anderen, nimmt aber keinen Zeiger an und ist leiser — er zeigt, was
           kommt, ohne ein Ziel zu versprechen ({K0079}). */
        [data-state="soon"] { color: var(--ink-quiet); cursor: default; }

        /* Ab screen tauschen die beiden Bereiche die Rolle. Das Popover wird
           sichtbar gesetzt und verliert damit alles Popover-hafte: es steht im
           Fluss, im Bereich menu, als Zeile. Der Schalter verschwindet.

           position:static ist noetig, weil das Vorgabe-Stylesheet einem Popover
           position:fixed gibt. Mehr nicht: in die obere Ebene kommt ein Popover
           erst beim Oeffnen, und geoeffnet wird dieses hier nie — der Schalter
           steht ab screen nicht mehr. */
        @media (min-width: 60rem) {
            [data-type="icon-button"] { display: none; }

            > ul:where([popover]) {
                display: grid;
                position: static;
                grid-auto-flow: column;
                gap: var(--space-open);
                padding: 0;
                background: none;
                border: 0;
            }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Sprachwahl — der aktuelle Wert sichtbar, die uebrigen aufklappbar. */

@layer widgets.header_options {
    @scope ([data-type="header-options"]) {
        :scope {
            position: relative;
            justify-self: end;
        }

        > summary {
            display: grid;
            place-items: center;
            min-inline-size: var(--space-wide);
            padding-block: var(--space-tight);
            padding-inline: var(--space-snug);
            border: 1px solid var(--surface-4);
            font-family: ui-monospace, monospace;
            font-size: var(--text-small);
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--ink-quiet);
            cursor: pointer;
            list-style: none;
        }

        > summary::-webkit-details-marker { display: none; }

        /* Die Liste haengt ueber dem Inhalt statt ihn zu schieben. Anchor
           Positioning waere hier das feinere Mittel, aber absolute Position an
           einem relativ gesetzten Elternteil kostet nichts und ist ueberall da.
           Der Wechsel lohnt erst, wenn die Liste an eine Kante stossen kann. */
        > ul {
            position: absolute;
            inset-inline-end: 0;
            inset-block-start: calc(100% + var(--space-tight));
            z-index: 10;
            display: grid;
            gap: var(--space-tight);
            min-inline-size: max-content;
            padding: var(--space-snug);
            background: var(--ground-up);
            border: 1px solid var(--surface-4);
        }

        /* Der Verweis bringt Fokusring und Zustand mit \({K0054}\); hier steht
           nur die gedaempftere Farbe der Kopfzeile. */
        [data-type="link"] { font-size: var(--text-small); color: var(--ink-quiet); }
        [data-type="link"]:where(:hover, :focus-visible) { color: var(--ink); }
        [data-type="link"][aria-current] { color: var(--link); }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ein Gegenstand als Karte — Symbol, Name, Marken, Werte, Herkunft, Zutaten. */

@layer widgets.item_card {
    @scope ([data-type="item-card"]) {
        /* Dieselbe Pixelkante wie Werkzeugkarte, Knopf und Marke: zwei
           Innenschatten statt eines Rahmens. Ein Rahmen läge außerhalb und
           verschöbe die Fläche um zwei Pixel; die Kante liegt innen und läßt die
           Karte, wo sie ist. Abgeleitet aus tool_card, nicht neu erfunden — eine
           zweite Kartenform mit eigener Kante wäre eine zweite Designsprache. */
        :scope {
            display: grid;
            grid-template-areas:
                "icon  title"
                "icon  marks"
                "stats stats"
                "orig  orig"
                "input input";
            grid-template-columns: auto 1fr;
            column-gap: 1rem;
            row-gap: 0.5rem;
            align-content: start;
            padding: 1rem;
            background: var(--surface-4);
            box-shadow:
                inset  2px  2px 0 oklch(33% 0.014 var(--hue-surface)),
                inset -2px -2px 0 oklch(12% 0.010 var(--hue-surface));

            /* Die Karte trägt ihre eigene Abfrage: sie steht in einer Liste, in
               einem Raster oder allein, und keiner dieser Orte soll ihr die
               Breite sagen müssen. */
            container-type: inline-size;
        }

        /* 100 px angezeigt, 128 geliefert. Die Datei ist die exakte Verdopplung
           der 64er aus dem Spiel — jedes Pixel zu vieren, ohne Zwischentöne —,
           und auf 100 dargestellt ergibt das 1,28fache Dichte.
           
           Kein `image-rendering: pixelated`: hier wird verkleinert, nicht
           vergrößert, und dabei ist die Vorgabe die bessere. Und keine KI
           dazwischen: bei genau 2x gäbe es nichts zu erfinden, was nicht schon
           dasteht, und ein Upscaler würde Kanten glätten, die das Design zeigt.
           Gemessen: verdoppelt 1416 Bytes, dieselbe Größe mit Lanczos 8438. */
        [data-type="game-icon"] {
            grid-area: icon;
            inline-size: 100px;
            block-size: 100px;
            align-self: start;
        }

        [data-type="heading"] { grid-area: title; align-self: end; }

        [data-part="marks"] {
            grid-area: marks;
            display: flex;
            flex-wrap: wrap;
            gap: 0.25rem;
            margin: 0;
            align-self: start;
        }

        /* Werte zweispaltig, Bezeichnung links, Zahl rechts. Die Linie sitzt
           unter jeder Zeile, weil die Liste unten offen endet und dort ohnehin
           der nächste Bereich beginnt. */
        [data-part="stats"] {
            grid-area: stats;
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
            gap: 0.25rem 1rem;
            margin: 0.5rem 0 0;

            > div {
                display: flex;
                justify-content: space-between;
                gap: 0.5rem;
                padding-block-end: 0.25rem;
                border-block-end: 1px solid oklch(33% 0.014 var(--hue-surface));
            }

            dt { color: var(--ink-quiet); }
            dd {
                margin: 0;
                color: var(--ink-bright);
                font-variant-numeric: tabular-nums;
            }
        }

        /* Die Herkunftsart färbt die Kante, trägt die Auskunft aber im Text:
           wer die Farbe nicht unterscheiden kann, liest sie. */
        [data-part="origins"] {
            grid-area: orig;
            display: grid;
            gap: 0.25rem;

            p {
                margin: 0;
                padding-inline-start: 0.75rem;
                border-inline-start: 2px solid oklch(33% 0.014 var(--hue-surface));
            }

            [data-kind="hergestellt"],
            [data-kind="Nebenprodukt"] { border-inline-start-color: var(--link); }

            b { color: var(--ink-bright); font-weight: 500; }
            i { color: var(--ink-quiet); font-style: normal; }
            i::before { content: " · "; }
        }

        [data-part="inputs"] {
            grid-area: input;
            display: flex;
            flex-wrap: wrap;
            gap: 0.25rem 0.75rem;
            margin: 0;
            padding: 0;
            list-style: none;

            li { display: flex; align-items: center; gap: 0.25rem; }

            /* In der Zutatenliste ist das Symbol eine Marke neben der Zahl und
               keine Bühne — 32 px, wie in der Werkzeugkarte am Telefon. */
            [data-type="game-icon"] { inline-size: 2rem; block-size: 2rem; }

            [data-part="amount"] {
                color: var(--ink-bright);
                font-variant-numeric: tabular-nums;
            }
        }

        /* Schmal: das Symbol steht über dem Namen statt daneben. Bei 100 px
           Symbol und 9 rem Werten bleibt sonst für den Namen nichts. */
        @container (width < 20rem) {
            :scope {
                grid-template-areas: "icon" "title" "marks" "stats" "orig" "input";
                grid-template-columns: 1fr;
            }
            [data-part="stats"] { grid-template-columns: 1fr; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Blatt eines Gegenstands - das Popover, sein Kopf, die
   Stufenwahl, das Schliesszeichen. Herausgeloest aus slice_items beim dritten
   Aufrufer ({T0073S8}, {K0054}); die Regeln sind die vom 1. und 2. September,
   unveraendert. Die Gestalt der Bereiche darin (Zeilen, Werte, Herstellung)
   liegt weiter in slice_items.css, weil die Einzelseite dieselben Regeln
   fuer ihr eigenes Dossier-Markup braucht - der Scope dort traegt seit
   {T0073S8} drei Wurzeln. */

@layer widgets.item_sheet {
    @scope ([data-type="item-sheet"]) {
        /* ---- Das Blatt --------------------------------------------------- */

        /* Im Top-Layer, also über allem — ohne einen einzigen z-index. Das ist
           der Gewinn gegenüber der Überlagerung, die hier vorher stand: dort
           mußte die Stapelordnung von Hand stimmen, und sie stimmte erst beim
           zweiten Versuch.

           Die Stelle setzt das Skript: es merkt sich, wo die Kachel liegt, und
           läßt das Blatt von dort wachsen. Ohne Skript steht es mittig — das
           ist der Rückfall, nicht der Regelfall. */
        :scope {
            /* Mittig und fest bemessen: 400 breit, höchstens 900 hoch (Owner,
               1. September 2026). Ein Popover zentriert sich von selbst, wenn
               man es läßt — `inset: 0` mit `margin: auto` sagt es noch einmal
               ausdrücklich, weil jede eigene Stellungsangabe die Vorgabe des
               Browsers sonst aufhebt. Genau daran saß das Blatt vorher in der
               linken oberen Ecke.

               Die Höhe ist eine Obergrenze und keine Vorschrift: ein Blatt mit
               zwei Angaben soll nicht neunhundert Pixel Leere tragen. Und auf
               einem kleinen Schirm gewinnt der Schirm. */
            position: fixed;
            inset: 0;
            margin: auto;

            /* **Der Gürtel zum Hosenträger.** Im Top-Layer liegt das Blatt über
               allem und braucht keinen `z-index` — aber genau darauf war kein
               Verlaß: beim Schließen fällt es aus dem Top-Layer, und `overlay`
               überbrückt den Moment nicht überall. Für einen Wimpernschlag lag
               es dann hinter den Kacheln (Owner, 1. September 2026).

               Fällt es heraus, ist es immer noch `position: fixed` — und dann
               entscheidet der `z-index`. Der hier ist so hoch, daß nichts in
               diesem Entwurf ihn erreicht; die Kacheln stehen auf `auto`.

               Die Zeile ist keine Lösung des Problems, sondern Vorsorge für den
               Fall, daß die Lösung aussetzt. Wo eine Eigenschaft genau den
               einen Moment nicht überbrückt, in dem sie gebraucht wird, gehört
               eine zweite daneben — und sie bleibt stehen, auch wenn sie
               überflüssig aussieht. */
            z-index: var(--sheet-layer);
            inline-size: var(--sheet-inline-size);
            max-block-size: var(--sheet-max-block-size);
            overflow-y: auto;
            padding: 0;

            /* Der goldene Rahmen ringsherum, drei Pixel (Owner, 1. September
               2026). Die Kachel trägt die Ecke als Ankündigung — »hier poppt
               etwas auf«; das Geöffnete trägt dieselbe Farbe als geschlossene
               Linie. Ankündigung und Einlösung in derselben Sprache. */
            border: var(--sheet-edge) solid var(--gold);

            background: var(--surface-4);
            color: var(--ink);

            /* Kein Innenschatten mehr. Die Pixelkante sagt »erhöhte Fläche«,
               und das braucht ein Blatt nicht: es liegt im Top-Layer über
               allem, und der Schlagschatten sagt es deutlicher. Zwei Aussagen
               über dieselbe Sache, und die eine zog als helle Linie links und
               dunkle rechts durchs Bild (Owner, 1. September 2026). */
            box-shadow: var(--sheet-shadow);

            /* Nur einblenden, nicht auffahren. Der Weg von der Kachel zum Blatt
               brauchte ein Skript, das drei Variablen setzte; mit dem Verzicht
               darauf fällt die einzige Skriptdatei der Seite weg.

               `allow-discrete` hält das Element beim Schließen lange genug am
               Leben, damit das Ausblenden überhaupt zu sehen ist — ohne das
               verschwindet ein Popover ohne Übergang. */
            /* Eine halbe Sekunde, weich (Owner, 1. September 2026). Nicht
               länger: ein Blatt, das man schließt, soll nicht noch da sein,
               wenn man die nächste Kachel anklickt.

               `translate` steht daneben, weil das Blatt gezogen werden kann —
               beim Ziehen selbst schaltet das Skript den Übergang ab, sonst
               liefe es der Maus hinterher statt an ihr zu hängen. */
            /* **Der Ruhewert ist unsichtbar, nicht sichtbar.** Hier stand
               `opacity: 1`, und damit gab es beim Schließen nichts
               auszublenden: das Blatt ging von eins nach eins, verschwand nach
               einer halben Sekunde schlagartig — und lag in dieser halben
               Sekunde voll sichtbar unter den Kacheln, weil `overlay` mangels
               Änderung nichts zu halten hatte.

               Der geschlossene Zustand ist der Ruhezustand; sichtbar wird das
               Blatt erst durch `:popover-open`. `@starting-style` sagt
               zusätzlich, wo das Einblenden beginnt — ohne das gäbe es nur den
               Rückweg. */
            opacity: 0;

            /* `transition-behavior` steht ausgeschrieben und nicht als
               `allow-discrete` in der Kurzform. Beides ist gültig, aber die
               Kurzform wird nicht überall gleich behandelt — und wenn sie
               ausfällt, verläßt das Blatt den Top-Layer sofort beim Schließen,
               während es noch ausblendet. Für eine halbe Sekunde liegt es dann
               unter den Kacheln, die es eben noch überdeckt hat (Owner,
               1. September 2026).

               `overlay` ist die Eigenschaft, die das hält: sie beschreibt die
               Zugehörigkeit zum Top-Layer, und nur wenn sie mit übergeht,
               bleibt das Blatt oben, bis es unsichtbar ist. */
            transition-property: opacity, overlay, display;
            transition-duration: 500ms;
            transition-timing-function: ease;
            transition-behavior: allow-discrete;
        }

        :scope:popover-open { opacity: 1; }

        /* Während des Ausblendens nimmt das Blatt keine Klicks mehr an: was
           halb durchsichtig ist, ist keine Bedienung mehr — sonst träfe man in
           der halben Sekunde noch »Zum Planer«. */
        :scope:not(:popover-open) { pointer-events: none; }

        @starting-style {
            :scope:popover-open { opacity: 0; }
        }

        /* **Ein Loch im Backdrop für die Kachel, über der die Maus steht.**

           Die Kacheln bleiben anklickbar, solange ein Blatt offen ist — das ist
           gewollt, man wechselt so von einem Gegenstand zum nächsten. Nur sah
           man die Kachel unter dem Zeiger dabei nur abgedunkelt (Owner,
           1. September 2026).

           Die Maske besteht aus zwei Rechtecken: das erste deckt die ganze
           Fläche, das zweite steht dort, wo die Kachel liegt, und
           `mask-composite: exclude` zieht es ab. Was übrig bleibt, ist der
           Backdrop mit einem Loch.

           Ein Element über den Backdrop zu heben ginge nicht: der liegt im
           Top-Layer, und dorthin kommt nichts, was nicht selbst ein Popover
           ist. Also wird nicht überdeckt, sondern ausgespart.

           Die vier Werte setzt das Skript; ohne es bleibt das Loch bei null
           Größe und der Backdrop ist einfach ganz. */
        :scope::backdrop {
            background: var(--sheet-scrim);
            opacity: 0;

            /* Die Maske braucht nur Deckung, keine Farbe — und trotzdem OKLCH,
               weil im Stylesheet kein Hex steht (AW10, {C0022}). */
            mask-image: linear-gradient(oklch(0% 0 0) 0 0), linear-gradient(oklch(0% 0 0) 0 0);
            mask-repeat: no-repeat, no-repeat;
            mask-position: 0 0, var(--loch-x, -9999px) var(--loch-y, -9999px);
            mask-size: 100% 100%, var(--loch-b, 0) var(--loch-h, 0);
            mask-composite: exclude;
            transition-property: opacity, overlay, display;
            transition-duration: 500ms;
            transition-timing-function: ease;
            transition-behavior: allow-discrete;
        }

        :scope:popover-open::backdrop { opacity: 1; }

        @starting-style {
            :scope:popover-open::backdrop { opacity: 0; }
        }

        @media (prefers-reduced-motion: reduce) {
            :scope,
            :scope::backdrop {
                transition-duration: 1ms;
            }
        }

        /* Der Kopf ist der Griff: dort faßt man ein Fenster an, seit es
           Fenster gibt. `touch-action: none` verhindert, daß der Browser das
           Ziehen als Rollen deutet und die Bewegung abfängt. */
        :scope > header {
            cursor: grab;
            touch-action: none;
            user-select: none;
            position: sticky;
            inset-block-start: 0;
            display: grid;
            grid-template-columns: auto 1fr;
            align-items: center;
            gap: 0.75rem;
            /* Rechts so viel Platz, daß der Name nicht unter das Schließzeichen
               läuft: dessen Breite plus sein Schutzabstand. */
            padding: 0.875rem calc(var(--space-open) * 2 + 0.75rem) 0.875rem 1rem;
            background: var(--surface-4);

            [data-type="game-icon"] {
                grid-area: 1 / 1;
                inline-size: 64px;
                block-size: 64px;
            }

            /* Die Stufe am Icon ({T0023S4}): dieselbe Rasterzelle wie das
               Symbol, oben rechts hineingesetzt — kein absoluter Versatz
               (AW5). Die Ziffer kommt aus `--level`, und die setzt die
               gewählte Pille weiter unten; ohne Wahl steht die Eins. Weiß
               für jede Stufe wie auf dem Entwurf: den Rang trägt der Name. */
            [data-part="level"] {
                grid-area: 1 / 1;
                justify-self: end;
                align-self: start;
                padding: 0.1em 0.25em;
                background: var(--surface-4);
                color: var(--ink-bright);
                font-size: var(--text-small);
                font-style: normal;
                font-weight: 500;
                line-height: 1;
                font-variant-numeric: tabular-nums;
            }

            [data-part="level"]::after { content: var(--level, "1"); }

            /* Der Name in der Farbe seines Rangs: weiß, grün, blau, magenta
               ({T0023S4}, 02-valheim.md). `--rank` setzt die gewählte Pille;
               ohne sie bleibt die helle Tinte, und die ist der erste Rang. */
            b {
                grid-area: 1 / 2;
                color: var(--rank, var(--ink-bright));
                font-size: 1.0625rem;
                font-weight: 500;
                line-height: 1.25;
            }
        }

        /* ---- Die Stufenwahl ({T0023S4}) --------------------------------- */

        /* **Eine Bedingung, kein Paar** ({K0005}): welche Stufe gilt, sagt
           die gewählte Pille, und jeder Selektor hier blendet genau das aus,
           was nicht zu ihr gehört. Nichts wird zurückgenommen — die aktive
           Stufe behält den Anzeigetyp, den ihre eigene Regel ihr gibt, ob
           Zeile, Raster oder Zahl im Text. Ohne Pillen gilt die Eins: der
           Ruhewert ist die erste Stufe.

           Ohne Skript: ein Optionsfeld und `:has()` an der Wurzel des Blattes
           können das. Die Seite trägt weiterhin genau ein Skript, und das
           zieht nur.

           `:has()` am Blatt und nicht am Slice: die Wahl eines Blattes darf
           kein anderes umschalten, und ein Selektor, der am Slice hinge,
           liefe bei jedem Klick über alle 472 Blätter. */
        :scope {
            &:not(:has([data-rank] > :checked)) [data-level]:not([data-level="1"]),
            &:has([data-rank="1"] > :checked) [data-level]:not([data-level="1"]),
            &:has([data-rank="2"] > :checked) [data-level]:not([data-level="2"]),
            &:has([data-rank="3"] > :checked) [data-level]:not([data-level="3"]),
            &:has([data-rank="4"] > :checked) [data-level]:not([data-level="4"]) {
                display: none;
            }

            /* Der Name und die Marke folgen der Wahl. Die Eins braucht keine
               Regel: sie ist der Ruhewert von beidem. */
            &:has([data-rank="2"] > :checked) { --rank: var(--rank-2); --level: "2"; }
            &:has([data-rank="3"] > :checked) { --rank: var(--rank-3); --level: "3"; }
            &:has([data-rank="4"] > :checked) { --rank: var(--rank-4); --level: "4"; }

            /* Eine Zutat, die erst ab einer höheren Stufe anfällt, fehlt
               darunter: Knochenfragmente kommen an die Lederrüstung erst mit
               dem Update. */
            &:not(:has([data-rank] > :checked)) [data-from],
            &:has([data-rank="1"] > :checked) [data-from],
            &:has([data-rank="2"] > :checked) [data-from="3"],
            &:has([data-rank="2"] > :checked) [data-from="4"],
            &:has([data-rank="3"] > :checked) [data-from="4"] {
                display: none;
            }

            /* Auf der höchsten Stufe entfällt die Update-Zeile, statt leer
               dazustehen: die Zeile trägt die höchste Stufe als data-max. */
            &:has([data-rank="2"] > :checked) [data-max="2"],
            &:has([data-rank="3"] > :checked) [data-max="3"],
            &:has([data-rank="4"] > :checked) [data-max="4"] {
                display: none;
            }
        }

        /* Das Schließzeichen sitzt in der Ecke, nicht in der Kopfzeile: dort
           sucht man es, und dort steht es in jedem Fenster. Vier Pixel
           Schutzabstand zum Rahmen (Owner, 1. September 2026).

           Doppelt so groß wie ein gewöhnlicher Symbolknopf — es ist die einzige
           Bedienung, die im Blatt immer da ist, und ein Blatt ohne sichtbaren
           Ausgang macht unruhig.

           Grau wie der Nebentext, beim Zeigen golden wie der Rahmen (Owner,
           1. September 2026). Es stand zuerst umgekehrt — gold, beim Zeigen rot
           —, und das war eine Stufe zu laut: das Schließzeichen ist die
           selbstverständlichste Bedienung im Blatt und muß nicht um
           Aufmerksamkeit werben. Wer es sucht, findet es; wer darauf zeigt,
           bekommt die Farbe des Rahmens als Bestätigung. */
        /* Das Schließzeichen ist kein Griff — sonst zöge man das Blatt, wenn
           man es schließen will. */
        /* Nur noch die Stellung — Groesse und Farbe traegt die Rolle
           »schliessen« im Element ({T0084}). Sie standen hier und nirgends
           sonst, weshalb die Kartenblaetter ein kleineres Zeichen trugen. */
        :scope [data-type="icon-button"] {
            cursor: pointer;
            position: absolute;
            inset-block-start: 4px;
            inset-inline-end: 4px;
            z-index: 1;
        }


        :scope [data-part="dossier"] {
            padding: 0 1rem 1rem;
        }

        :scope header a {
            color: inherit;
            text-decoration: none;
        }

        :scope header a:where(:hover, :focus-visible) {
            text-decoration: underline;
            text-underline-offset: 3px;
        }

    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Kachel eines Gegenstands - Symbol, Name, goldene Ecke, Hover.
   Herausgeloest aus slice_items beim zweiten Aufrufer ({T0073S2}, {K0054});
   die Regeln sind die vom 1. September, unveraendert. */

@layer widgets.item_tile {
    @scope ([data-type="item-tile"]) {
    /* ---- Die Kachel ------------------------------------------------- */

    /* Ein <button> und kein <article>: sie schaltet etwas, und was schaltet,
       ist ein Knopf. Damit kommen Tastatur, Fokusring und die Rolle für den
       Vorleser umsonst. */
    :scope {
        position: relative;
        display: grid;
        grid-template-columns: auto 1fr;
        align-items: center;
        column-gap: 0.75rem;
        inline-size: 100%;
        block-size: 79px;
        padding: 6px 0.75rem;
        border: 0;
        background: var(--surface-4);
        color: inherit;
        font: inherit;
        text-align: start;
        cursor: pointer;

        box-shadow:
            inset  2px  2px 0 oklch(33% 0.014 var(--hue-surface)),
            inset -2px -2px 0 oklch(12% 0.010 var(--hue-surface));

        [data-type="game-icon"] {
            inline-size: 50px;
            block-size: 50px;
        }

        b {
            color: var(--ink-bright);
            font-weight: 500;
            line-height: 1.2;
            overflow-wrap: break-word;
            hyphens: none;
        }
    }

    /* **Die goldene Ecke: das Zeichen für »poppt auf«.**

       Fünfzehn Pixel in jede Richtung, fünf Pixel stark, über der
       bestehenden Kante — die bleibt darunter unverändert (Owner,
       1. September 2026). Zwei Kanten an einem Kasten ohne Fläche ergeben
       den Winkel; ein SVG wäre eine Datei mehr für zwei Striche.

       **Diese Sprache gilt durchgehend für alles, was aufpoppt.** Wer sie
       an einem anderen Bauteil braucht, kopiert nicht die Regel, sondern
       nimmt die Zeile hier zum Vorbild — und wenn es ein drittes Mal
       gebraucht wird, wird ein Element daraus ({K0054}).

       `pointer-events: none`, weil sie über der Kachel liegt und ein Klick
       auf sie ein Klick auf die Kachel bleiben muß. */
    :scope::after {
        content: "";
        position: absolute;
        inset-block-start: 0;
        inset-inline-end: 0;
        inline-size: 15px;
        block-size: 15px;
        border-block-start: 5px solid var(--gold);
        border-inline-end: 5px solid var(--gold);
        pointer-events: none;
    }

    /* Beim Überfahren ein Hauch Gold über der Fläche — zehn Prozent
       (Owner, 1. September 2026). Es ist dieselbe Farbe wie die Ecke und
       der Rahmen des Blattes, nur so schwach, daß sie die Fläche tönt statt
       sie zu färben: die Kachel bleibt eine Kachel und wird nicht zum
       Knopf.

       `color-mix` und keine zweite Schicht: eine Auflage mit `opacity`
       läge über dem Symbol und nähme ihm die Farbe mit. */
    :scope:where(:hover, :focus-visible) {
        background: color-mix(in oklab, var(--gold) 10%, var(--surface-4));
    }

    /* Beim Überfahren ein Hauch Gold über der Fläche — zehn Prozent
       (Owner, 1. September 2026). Dieselbe Farbe wie Ecke und Rahmen, nur so
       schwach, daß sie die Fläche tönt statt sie zu färben: die Kachel
       bleibt eine Kachel und wird nicht zum Knopf.

       `color-mix` und keine zweite Schicht: eine Auflage mit `opacity` läge
       über dem Symbol und nähme ihm die Farbe mit.

       Der Ton reicht für beide Fälle. Bei offenem Blatt stanzt die Maske
       ohnehin ein Loch in den Backdrop, und durch das Loch ist derselbe
       Hover zu sehen — es braucht keine zweite Behandlung für den Fall,
       daß gerade etwas offen ist. */
    :scope:where(:hover, :focus-visible) {
        background: color-mix(in oklab, var(--gold) 10%, var(--surface-4));
    }

    :scope:where(:hover, :focus-visible)::after {
        border-color: oklch(from var(--gold) calc(l + 0.08) c h);
    }

    :scope:where(:focus-visible) {
        outline: 2px solid var(--link);
        outline-offset: -2px;
    }

    }
}

@layer widgets.item_tile {
    @scope ([data-type="item-tile"]) {
        /* Die Marke des letzten Patches ({T0078}, Gestalt vom Owner am
           9. September 2026 nach dem 1.0-Durchlauf geaendert).

           Vorher ein rotes Abzeichen mit Grund und Pixelkante wie das Beta am
           Logo — das war zu laut: nach einem Patch mit 211 neuen Gegenstaenden
           steht die Liste voller Signale, und ein Warnzeichen, das auf jeder
           dritten Kachel klebt, warnt vor nichts mehr. Jetzt nur noch das Wort,
           klein, in der Handlungsfarbe des Hauses, ohne Grund.

           »Geaendert« faellt ganz weg: dass ein Gegenstand andere Werte hat,
           interessiert beim Blick auf die Liste niemanden — neu zu sein schon.

           Sie sitzt unten rechts, wo die goldene Ecke oben rechts nicht steht;
           absolut positioniert, damit sie den Namen nicht umbricht und die
           Kachel nicht zweizeilig wird. */
        [data-part="signal"] {
            position: absolute;
            inset-block-end: var(--space-close);
            inset-inline-end: var(--space-close);

            /* Gemessen gegen den Kachelgrund (--surface-4, oklch 86 % hell und
               21 % dunkel): die Primaerfarbe traegt im Dunklen mit 7,68 zu 1,
               im Hellen aber nur mit 1,51 — dort steht derselbe Ton
               abgedunkelt und kommt auf 4,53.

               **Abgedunkelt heisst gerechnet, nicht abgeschrieben** (AW10,
               {K0004}). Hier stand `oklch(45% 0.15 145)`, also der Farbton des
               aufgeloesten `--action`; jetzt kommt Buntheit und Ton aus dem
               Primaerwert und nur die Helligkeit steht fest ({T0096}). */
            color: light-dark(oklch(from var(--link) 0.45 c h), var(--link));

            font-family: "Press Start 2P", monospace;
            font-size: 0.5rem;
            line-height: 1;
            letter-spacing: 0.04em;
            text-transform: uppercase;
        }

        /* Der Bezugsrahmen steht schon oben: die Kachel ist seit je relativ
           positioniert, weil die goldene Ecke ihn ebenfalls braucht. */
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Kennzahlen nebeneinander, durch Linien getrennt. */

@layer widgets.metric_row {
    @scope ([data-type="metric-row"]) {
        :scope {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
            gap: var(--space-open);
        }

        /* Die Trennlinie sitzt vor jeder Zahl ausser der ersten, nicht hinter jeder
           ausser der letzten: so bleibt sie richtig, wenn eine Zahl ausgeblendet
           wird oder die Reihe umbricht. Ein Rahmen an der letzten Zelle waere eine
           Linie an der Aussenkante. */
        > [data-type="metric"] + [data-type="metric"] {
            padding-inline-start: var(--space-open);
            border-inline-start: 1px solid var(--surface-4);
        }

        /* Am Telefon steht die vierte nicht. Sie bleibt im Markup — was fehlt, ist
           nur der Platz, und der kommt ab screen zurueck. */
        > [data-type="metric"]:nth-child(n + 4) { display: none; }

        @media (min-width: 60rem) {
            > [data-type="metric"]:nth-child(n + 4) { display: grid; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Hinweisband — Gold auf dunklem Grund, quer ueber die Buehne. */

@layer widgets.notice_band {
    @scope ([data-type="notice-band"]) {
        /* **Gold, nicht Hellblau** ({T0097}, {K0234}). Der Entwurf sah einen
           hellblauen Rahmen vor, und Hellblau waere eine vierte Rollenfarbe
           gewesen — die Palette hat drei, und {T0096} hat sie durchgesetzt.
           Gold sagt im Haus bereits »achte darauf« und steht auf der Karte
           unter den Reglern fuer genau so einen Hinweis.

           Eigener Grund, nicht nur ein Rahmen: das Band liegt auf der Buehne,
           und die ist orange, gestreift und hell. Ein Text ohne Flaeche waere
           dort nicht lesbar — dieselbe Rechnung wie beim Kartenpanel, das
           deshalb auf 85 Prozent Deckkraft steht. */
        :scope {
            display: grid;
            grid-template-columns: 1fr auto;
            align-items: center;
            gap: var(--space-close);
            /* **Ein Mass nach allen Seiten** (Owner, 12. September 2026: links
               klein, rechts gross, oben und unten mittel — »das sieht sehr
               unprofessionell aus«). Es war links `snug`, oben `tight` und
               rechts das, was der Knopf von sich aus mitbringt.

               `--space-close` traegt den Faktor 0.618 und ist damit die Stufe
               des Goldenen Schnitts selbst; sie liegt je nach Breite bei 8,7,
               9,3 und 10,5 Punkten und trifft die genannten acht auf der
               schmalen fast genau. Dieselbe Stufe wie an der goldenen Linie
               der Filterleiste ({T0093}) — ein Abstand, der zweimal »acht
               Punkte« heissen soll, heisst besser zweimal dasselbe. */
            padding: var(--space-close);
            background: oklch(12% 0.012 260 / 0.94);
            /* Dieselbe Kante wie ein Blatt ({T0097}, Owner am 12. September
               2026: »genau so breit wie vom popover«). Der Token traegt sie
               fuer alles, was auf der Seite aufliegt — hier stand eine
               Haarlinie, und die sah neben einem Blatt nach einer anderen
               Hausordnung aus. */
            border: var(--sheet-edge) solid var(--gold);
            color: var(--ink-bright);

            /* **Es schwebt ueber der Seite und damit ueber jedem Raster**
               (Owner, 12. September 2026: »der toast schwebt über der seite
               also auch über deren raster«).

               Vorher lag es als Rasterkind in der Zeile der Buehne. Das ging
               dort, wo eine Buehne steht — und schob auf der Kartenseite das
               Optionspanel nach unten, weil die Buehne im Vollbild
               ausgeblendet ist und das Band damit seine eigene Zeile bekam.
               Ein Hinweis, der das Bedienfeld verschiebt, ist teurer als er
               nuetzt.

               `fixed` statt `sticky`: es soll an seiner Stelle bleiben und
               keinen Platz beanspruchen, auf keiner Seite und in keinem
               Raster. Der `z-index` liegt unter dem Kopf, der 6 traegt —
               eine Ansage verdeckt die Navigation nicht. */
            position: fixed;
            inset-block-start: calc(var(--kopf-hoehe) + var(--space-close));
            inset-inline: 0;
            margin-inline: auto;
            z-index: 5;
            /* Schmal genug, dass die Buehne daneben noch zu sehen ist: sie
               ist das Bild der Seite, und ein Band, das sie zudeckt, nimmt
               mehr weg als es sagt. */
            inline-size: min(46rem, calc(100% - 2 * var(--gutter)));
        }

        /* **Die Grundgroesse der Seite, nicht die kleine** (Owner,
           12. September 2026: »es soll ja auch von Personen gelesen werden
           die Probleme mit den Augen haben«). Hier stand `--text-small`, und
           das ist die Groesse fuer Etiketten und Beiwerk — eine Ansage, die
           jemand lesen **muss**, gehoert in die Groesse des Fliesstextes. */
        [data-part="wort"] {
            margin: 0;
            font-size: var(--text);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Spieleliste — eine Zeile mit Trennpunkten. */

@layer widgets.page_footer_nav {
    @scope ([data-type="page-footer-nav"]) {
        ul {
            display: flex;
            flex-wrap: wrap;
            gap: var(--space-close);
            font-family: ui-monospace, monospace;
            font-size: 0.625rem;
            letter-spacing: 0.1em;
            text-transform: uppercase;
        }

        /* Flex, nicht Grid: die Liste ist ein einzelner Stapel, der umbricht, und
           genau dafuer ist Flex die Ausnahme, die erlaubt bleibt. Ein Grid muesste
           die Spaltenzahl kennen, die hier vom Text abhaengt. */

        li { color: var(--ink-quiet); }

        /* Der Trennpunkt gehoert nicht in den Text, sondern zwischen die Eintraege —
           sonst liest ein Screenreader ihn mit. */
        li + li::before {
            content: "·";
            margin-inline-end: var(--space-close);
            color: var(--ink-quiet);
        }

        /* Der Verweis bringt Fokus und Zustand mit; hier steht nur, dass er im
           Fuss gedaempfter sitzt \({K0054}\). */
        [data-type="link"] { color: var(--ink-quiet); }
        [data-type="link"]:where(:hover, :focus-visible) { color: var(--link); }

        /* Ein kommendes Spiel ist kein Verweis und sieht auch nicht so aus. */
        span { color: var(--ink-quiet); }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ein Suchfeld mit Knopf — Anordnung, mehr nicht. */

@layer widgets.search_form {
    @scope ([data-type="search-form"]) {
        /* Feld und Knopf tragen ihre Gestalt selbst. Hier steht nur, wie sie
           zueinander liegen — anders als bei der Seed-Eingabe, wo der Knopf
           doppelt so breit ist wie das Feld: dort ist START das Ziel, hier ist
           das Feld die Hauptsache und der Knopf nur die Bestätigung. */
        form {
            display: grid;
            grid-template-columns: minmax(0, 1fr) auto;
            gap: 0.625rem;
            align-items: end;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Seed-Eingabe — Beschriftung über Feld und Knopf. */

@layer widgets.seed_form {
    @scope ([data-type="seed-form"]) {
        /* Ein eigener Grund, sobald etwas dahinterliegt. Das Formular ist ein
           Bedienelement und muß auf dem Bild stehen, nicht darin: die
           Beschriftung ist gedämpft und braucht dunklen Grund, sonst fällt sie
           auf 1,1 zu 1 — gemessen mit tools/kontrast.py.

           Den Verlauf stattdessen rechts weiter abzudunkeln hätte das Bild genau
           dort verdeckt, wo es sichtbar sein soll. Eine eigene Fläche kostet
           nichts und trennt Bedienung von Bild.

           Sie ist durchscheinend, damit das Bild darunter sichtbar bleibt, und
           genau so weit, wie der Kontrast es zuläßt: gemessen fällt die
           Beschriftung schon bei 88 Prozent Deckkraft unter AA, wenn sie in der
           gedämpften Farbe steht. Deshalb wandert sie eine Stufe höher — 72
           Prozent Fläche und hellere Schrift ergeben zusammen mehr Kontrast als
           94 Prozent Fläche und gedämpfte.

           Beides weicht vom Entwurf ab, und beides ist gemessen: dessen eigene
           Kombination käme auf 3,4 zu 1. Er erfüllt an dieser Stelle kein AA; wir
           tun es, und die Abweichung steht hier — so verlangt es AW22. */
        :scope {
            padding: 1rem 1.125rem 1.125rem;
            background: oklch(8% 0.008 260 / 0.72);
            /* **Flex und keine benannten Spuren** \({T0103}\). Hier stand ein
               Raster mit `grid-template-areas: "input action random"` und drei
               festen Spalten. Das trug genau drei Kinder — und der dritte Knopf
               war das vierte: der Selektor `[data-type="button"]` wies Start
               UND Super Seed dieselbe Area zu, beide landeten auf x 261, y 161
               und lagen exakt uebereinander \({K0232}: ein Selektor, der zu
               viel trifft\).

               Eine vierte Spur zu benennen haette es nicht geloest, denn
               dasselbe Formular steht auf der Startseite mit nur drei Kindern
               und haette dort eine leere Spalte getragen. Flex zaehlt keine
               Spuren: drei Kinder verteilen sich auf drei, vier auf vier, und
               Start und Zufall werden dabei von selbst schmaler — genau das,
               was hier gefragt war \(Owner, 12. September 2026\). */
            display: flex;
            flex-wrap: wrap;
            gap: 0.4375rem 0.625rem;
            /* An der Unterkante: das Feld trägt seine Beschriftung darüber,
               der Knopf hat keine. Beide sind gleich hoch, weil die Höhe am
               Element steht — hier wird nur noch bündig gemacht. */
            align-items: end;
        }

        /* Feld und Knopf schreiben sich selbst — hier steht nur noch, wie sie
           zueinander liegen. Was vorher an dieser Stelle stand, ist in
           elements/field und elements/button gewandert, als die Suche auf der
           Gegenstandsseite dasselbe brauchte ({K0054}). */
        /* Das Feld nimmt den meisten Platz und faellt zuerst um: es traegt
           eine Eingabe, die Knoepfe nur ein Wort. */
        [data-type="field"] { flex: 1 1 12rem; }
        /* Die Knoepfe teilen den Rest zu gleichen Teilen. Die Basis ist so
           gewaehlt, dass drei davon bei 711 Punkten Panelbreite noch
           nebeneinander stehen und bei weniger umbrechen, statt die Schrift zu
           quetschen. */
        [data-type="button"] { flex: 1 1 7rem; }
        /* Der Zufallsknopf in Gold: er sagt »zeigt mehr«, nicht »tut, was du
           eingabst« ({K0032}). Ein Verweis in der Gestalt des Knopfs —
           dasselbe Element. Er darf schmaler bleiben als die beiden, die etwas
           ausloesen: sein Wort ist kuerzer und seine Rolle die leichtere. */
        [data-type="button"][href] { flex: 0 1 6rem; }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Einstiegskarten — quer am Telefon, hochkant ab screen. */

@layer widgets.tool_card {
    @scope ([data-type="tool-card"]) {
        /* Am Telefon eine Zeile: Icon links, Titel und Kurztext daneben, das
           Chevron rechts. Ab screen hochkant mit langem Text und dem Weg unten.
           Nur die Bereiche aendern sich — das ist der ganze Umbau \({K0009}\). */
        :scope {
            position: relative;
            display: grid;
            grid-template-areas:
                "icon title act"
                "icon text  act";
            grid-template-columns: auto 1fr auto;
            align-items: center;
            column-gap: 0.875rem;
            row-gap: 0.25rem;
            padding: 0.875rem 1rem;
            background: var(--surface-4);

            /* Dieselbe Pixelkante wie Knopf und Marke, nur in den Flächentönen:
               zwei Innenschatten statt eines Rahmens. Ein Rahmen läge außerhalb
               und verschöbe die Fläche um zwei Pixel; die Kante liegt innen und
               läßt die Karte, wo sie ist. Aus dem Entwurf gemessen. */
            box-shadow:
                inset  2px  2px 0 oklch(33% 0.014 var(--hue-surface)),
                inset -2px -2px 0 oklch(12% 0.010 var(--hue-surface));
        }

        /* 48 px, aus dem Entwurf gemessen — nicht die Standardgröße des
           Elements, weil die Karte ihm mehr Gewicht gibt als eine Liste. */
        /* Am Telefon 32 px, ab screen 48: in der Zeile ist das Icon eine Marke
           neben dem Titel, in der hochkanten Karte die Bühne darüber. */
        [data-type="game-icon"] { grid-area: icon; inline-size: 2rem; }
        [data-type="heading"]   { grid-area: title; }
        /* 14,5 px und eine Stufe gedämpfter als der Fließtext der Seite: in der
           Karte ist die Beschreibung Beiwerk zum Titel, nicht der Text, für den
           man gekommen ist. Beides aus dem Entwurf gemessen; 14,5 liegt zwischen
           zwei Stufen der Leiter, und AW22 gibt dem Entwurf recht. */
        [data-type="prose"] {
            grid-area: text;
            font-size: 0.90625rem;
            line-height: 1.6;
            color: oklch(65% 0.008 var(--hue-surface));
            max-inline-size: none;
        }

        /* Der lange Text steht erst ab screen, der kurze nur darunter. */
        [data-type="prose"]:nth-of-type(2) { display: none; }

        /* Die Ueberschrift traegt hier 13 px, nicht die Groesse ihrer Ebene: in
           der Karte ist sie eine Beschriftung und keine Ansage. Aus dem Entwurf
           gemessen \({K0058}\). */
        [data-type="heading"] {
            font-size: var(--text-label);
            text-transform: uppercase;
        }

        [data-type="link"] {
            grid-area: act;
            justify-self: end;
            font-family: ui-monospace, monospace;
            font-size: 0.6875rem;
            letter-spacing: 0.1em;
            text-transform: uppercase;

            /* Nach unten gedrueckt, damit der Weg bei allen Karten auf derselben
               Linie sitzt, gleich wie lang die Beschreibung ist. */
        }

        /* Am Telefon traegt der Verweis nur sein Chevron: der Wortlaut steht in
           der Karte darueber, und zweimal dasselbe zu lesen ist kein Dienst. */
        [data-type="link"] { font-size: 0; }
        [data-type="link"] > span { font-size: 1rem; margin-inline-start: 0; }

        /* Der Verweis zieht seine anklickbare Flaeche ueber die ganze Karte. Das ist
           der Grund fuer position:relative oben — und der Grund, warum Titel und
           Icon nicht selbst verlinkt sind. */
        [data-type="link"]::after {

        /* Der Aufruf einer Karte, deren Seite es noch nicht gibt. Er steht an
           derselben Stelle wie der Verweis, damit die Karten gleich hoch
           bleiben — nur leiser und ohne Zeiger ({K0079}). */
        [data-state="soon"] { color: var(--ink-quiet); cursor: default; }
            content: "";
            position: absolute;
            inset: 0;
        }

        /* Beim Überfahren wird die Karte gedrückt: die Kante kehrt sich um, dunkel
           oben links und hell unten rechts, wie beim Eingabefeld \({K0062}\). Dazu
           nimmt die Fläche einen Hauch der Verweisfarbe an — sechs Prozent, gerade
           genug, daß man es bemerkt, ohne es benennen zu können.

           Kein farbiger Rahmen: ein Rahmen sagt „ausgewählt", und ausgewählt ist
           die Karte nicht. Sie wird gedrückt, und das ist eine Handlung, keine
           Eigenschaft. */
        :scope:has([data-type="link"]:where(:hover, :focus-visible)) {
            background: color-mix(in oklab, var(--surface-4) 94%, var(--link));
            box-shadow:
                inset  2px  2px 0 oklch(12% 0.010 var(--hue-surface)),
                inset -2px -2px 0 oklch(33% 0.014 var(--hue-surface));
        }

        /* Der Übergang läuft nur über die Fläche; die Kante springt. Ein Schatten,
           der weich umschlägt, sähe nach Leuchten aus statt nach Druck. */
        :scope {
            transition: background 90ms ease-out;
        }

        @media (prefers-reduced-motion: reduce) {
            :scope { transition: none; }
        }

        /* Hochkant ab screen: das Icon rueckt ueber den Titel, die Karte wird eine
           Spalte. Nur die Bereiche aendern sich — das ist der ganze Umbau
           \({K0009}\). */
        @media (min-width: 60rem) {
            :scope {
                grid-template-areas:
                    "icon"
                    "title"
                    "text"
                    "act";
                grid-template-columns: 1fr;
                grid-template-rows: auto auto auto 1fr;
                align-items: start;
                gap: 1rem;
                padding: 1.625rem;
            }

            [data-type="game-icon"] { inline-size: 3rem; }

            [data-type="prose"]:nth-of-type(1) { display: none; }
            [data-type="prose"]:nth-of-type(2) { display: block; }

            [data-type="link"] {
                justify-self: start;
                font-size: var(--text-micro);
                margin-block-start: auto;
            }

            [data-type="link"] > span { font-size: inherit; margin-inline-start: 0.35em; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Fussband — eine Flächenstufe tiefer als der Inhalt darüber. */

@layer slices.page_footer {
    @scope ([data-slice="page-footer"]) {
        :scope {
            padding-block: 1.5rem;
            row-gap: 1rem;
            border-block-start: 1px solid oklch(26% 0.014 var(--hue-surface));

            /* Die Fläche wechselt, nicht ein Rand: Bänder trennen sich über den
               Grund, nicht über Linien \({K0031}\). Eine Stufe tiefer als der
               Inhalt darüber, damit der Fuß zurücktritt, ohne zu verschwinden. */
            background: var(--surface-1);
        }

        /* Verweise im Fuss tragen die Fussfarbe, nicht die Verweisfarbe.
           --link ist gruen und heisst »fuehrt weg«; im Inhalt ist das ein
           Angebot, im Fuss waere es ein Zuruf. Der Fuss ist das leiseste Band
           der Seite, und zwei Pflichtverweise darin sind keine Einladung,
           sondern eine Auskunft \({T0077} QS\). Erkennbar bleiben sie an der
           Unterstreichung und daran, dass sie beim Ueberfahren aufhellen. */
        /* Der Vermerk in der Groesse des uebrigen Fusses ({T0077}, Owner am
           9. September 2026). Er stand eine Stufe groesser, weil er einmal die
           Signatur war; jetzt ist er die eine Zeile, die in der Kartenansicht
           mittig allein steht, und dort soll er sich einreihen und nicht
           hervortreten. */
        [data-type="footer-legal"] small {
            font-size: 0.65625rem;
            letter-spacing: 0.08em;
        }

        a[data-type="link"] {
            color: var(--ink-quiet);
            font-family: ui-monospace, monospace;
            font-size: 0.65625rem;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            /* Unterstrichen, weil sie sonst durch nichts mehr als Verweise
               kenntlich waeren: die Farbe ist jetzt die des Fusses, und Farbe
               allein duerfte es ohnehin nicht sein \(AW3, WCAG 1.4.1\). Der
               Abstand haelt die Linie von den Versalien frei. */
            text-decoration: underline;
            text-underline-offset: 0.3em;
            text-decoration-thickness: from-font;
        }

        a[data-type="link"]:where(:hover, :focus-visible) {
            color: var(--ink);
        }

        [data-type="prose"] {
            font-family: ui-monospace, monospace;
            font-size: 0.65625rem;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            color: var(--ink-quiet);
            max-inline-size: none;
        }

        /* Der Spendenknopf steht unter dem Namen und nimmt nur seine eigene
           Breite: gestreckt waere er ein weisser Balken über das ganze Band
           statt eines Knopfes ({T0134}). */
        > [data-type="donate-button"] {
            justify-self: start;
        }

        /* Ab tablet stehen Name und Spieleliste nebeneinander, der Spendenknopf
           unter dem Namen, der Rechtsvermerk darunter über die ganze Breite — er
           ist die leiseste Zeile und soll nicht mit der Navigation um denselben
           Platz streiten. */
        @media (min-width: 35rem) {
            :scope {
                grid-template-areas:
                    "name   games"
                    "donate ."
                    "legal  legal";
                /* Die Rinne wird gerechnet, nicht als 1fr gegeben: neben einem
                   inneren 1fr teilten sich sonst drei flexible Spuren den Platz
                   und schoeben den Inhalt in die Mitte statt an die Raender. */
                grid-template-columns:
                    [full-start] var(--rand)
                    [content-start] auto var(--space-wide) 1fr
                    [content-end] var(--rand)
                    [full-end];
            }

            > [data-type="prose"]            { grid-column: 2; grid-row: 1; }
            > [data-type="page-footer-nav"]  { grid-column: 4; grid-row: 1; justify-self: end; }
            > [data-type="donate-button"]    { grid-column: 2; grid-row: 2; }
            > [data-type="footer-legal"]     { grid-column: 2 / 5; grid-row: 3; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Der Kopf — die Reihe aus Marke, Zielen und Sprache, darunter die Bühne. */

@layer slices.page_header {
    @scope ([data-slice="page-header"]) {
        /* Zwei Zeilen, vier Bereiche. Die Ziele stehen in der Mitte und werden
           dort zentriert — nicht durch space-between, sondern durch eine 1fr-Spur
           zwischen zwei auto-Spuren. Der Unterschied zählt: bei space-between
           wandert die Mitte, sobald die Enden verschieden breit sind, und genau
           das passiert, wenn die Sprachwahl von DE auf Русский wechselt.

           Der Kopf trägt eigenen Grund und Trennlinie: er hebt sich von der Seite
           ab, bevor die Leiste darunter beginnt. Aus dem Entwurf gemessen. */
        /* **Das Spaltenraster steht einmal und wird zweimal gelesen**
           ({T0095}): der Kopf braucht es fuer die Buehne, die klebende Leiste
           fuer ihre eigenen Bereiche. Zwei Abschriften derselben sieben Spuren
           waeren die Sorte Wiederholung, bei der die zweite irgendwann
           abweicht (AW11). Eine Custom Property darf benannte Rasterlinien
           tragen. */
        /* **Der Kopf bleibt stehen** (Owner, 12. September 2026), und er ist
           dafuer auf das geschrumpft, was zur Bedienung gehoert: Marke,
           Ziele, Sprache, darunter der Fortschrittsbalken. Die Buehne steht
           daneben statt darin.

           Daran haengt das Kleben. `position: sticky` haelt nur innerhalb des
           Containerblocks, und solange die Buehne im Kopf lag, war das der
           Kopf selbst — die Leiste stand nach 787 Punkten Scrollweg
           gemessen bei -787. Jetzt ist der Containerblock der `<body>`, und
           der reicht so weit wie die Seite. */
        :scope {
            position: sticky;
            inset-block-start: 0;
            /* Ueber allem im Band; die Karte setzt ihre Marker auf 5. */
            z-index: 6;

            grid-template-areas:
                ".    logo badge menu options toggle .   "
                "fort fort fort  fort fort    fort   fort";
            grid-template-columns:
                [full-start] var(--rand)
                [content-start] auto auto 1fr auto auto
                [content-end] var(--rand)
                [full-end];
            align-items: center;
            row-gap: 0;
            background: var(--surface-3);
            border-block-end: 1px solid oklch(30% 0.014 260);
        }

        /* Der Innenraum sitzt an den Kindern der Leiste, nicht am Band: die
           Bühnenleiste läuft darunter über die volle Breite und darf ihn nicht
           erben. */
        > :where([data-type="header-logo"], nav, [data-type="header-options"]) {
            padding-block: 1rem;
        }

        /* Marke und Sprache liegen in der content-Spur, nicht an einem Rand mit
           Abstand. Der Unterschied trägt weit: --rand rechnet mit 100 Prozent, und
           das löst sich beim Einsetzen gegen den umgebenden Kasten auf — bei einem
           margin also gegen die Rasterzelle des Elements und nicht gegen das Band.
           Am Schreibtisch klebte die Marke deshalb bei 40 px, während der Inhalt
           darunter bei 240 begann. Dieselbe Falle wie eine Ableitung in :root, die
           dort einmal berechnet wird und überall gleich herauskommt. */
        [data-type="header-logo"]    { grid-area: logo; }

        /* Der Abstand steht hier und nicht als column-gap am Band: eine Luecke
           zwischen allen Spuren schoebe auch die Sprachwahl vom Rand. Das Mass
           ist das der Marke selbst, damit Zeichen, Wortmarke und Beta-Kasten in
           einem Rhythmus stehen. */
        /* Kein padding-block wie bei den Nachbarn: der Kasten ist die sichtbare
           Flaeche selbst, und ein Innenabstand liesse ihn wachsen statt seine
           Zelle. Die Zeilenhoehe kommt von der Marke daneben, align-items setzt
           ihn hinein. */
        [data-type="beta-badge"]     { grid-area: badge; margin-inline-start: 0.75rem; }
        [data-type="header-options"] { grid-area: options; }

        :scope > nav > button { grid-area: toggle; }
        :scope > nav > ul     { grid-area: menu; }

        /* Der Lesefortschritt ({T0095}, Owner am 12. September 2026): ein
           Streifen zwischen der Zielreihe und der Buehne, der mitwaechst,
           waehrend man liest.

           **Der Ruhezustand ist leer, nicht voll.** Das ist die ganze
           Vorsichtsmassnahme: `animation-timeline: scroll()` ist nicht
           Baseline — Chrome und Edge seit 115, Safari seit 26, Firefox stable
           haelt es bis heute hinter einem Flag. Wo es fehlt, laeuft die
           Animation nicht, und das Element bleibt stehen, wie es hier steht:
           auf null gestaucht und damit unsichtbar. Ein Balken, der ohne
           Unterstuetzung voll dastuende, waere schlimmer als keiner — er
           behauptete etwas.

           `scale` und nicht `inline-size`: das eine laeuft auf dem
           Compositor, das andere durch das Layout — und zwar bei jedem
           Scrollschritt (AW18). `transform-origin` am Anfang der Zeile, damit
           er von links waechst; `inset-inline-start` waere die Alternative und
           ginge wieder durchs Layout. */
        [data-part="fortschritt"] {
            grid-area: fort;
            /* Die Primaerfarbe, dieselbe wie der Menuepunkt eine Zeile
               darueber. Der Balken trug einen Tag lang einen zweiten
               Gruenton, und der Owner sah es sofort — inzwischen gibt es
               keinen zweiten mehr ({T0096}). */
            background: var(--link);
            transform-origin: 0 50%;
            scale: 0 1;

            /* **Ohne Unterstuetzung auch keine Hoehe.** Auf null gestaucht
               waere der Streifen zwar unsichtbar, seine Rasterzeile aber
               weiterhin fuenf Punkte hoch — in Firefox stuende der Kopf dann
               fuenf Punkte tiefer als anderswo, fuer nichts. Die Hoehe kommt
               deshalb erst mit dem Feature, gemessen: Firefox 0 px, Chromium
               5 px. */
            block-size: 0;
        }

        /* **Kein Rueckzug bei `prefers-reduced-motion`**, und das ist eine
           bewusste Auslegung von AW18: die Regel meint Bewegung, die **ohne
           Zutun** geschieht — Parallaxe, Selbstlauf, Uebergaenge. Dieser
           Balken bewegt sich ausschliesslich synchron zum Scrollen des Lesers
           und damit in derselben Kategorie wie die Bildlaufleiste daneben.
           Ihn abzuschalten naehme eine Auskunft weg und schuetzte vor nichts.
           Wenn der Owner das anders sieht, ist es eine Zeile.

           Die Abfrage steht trotzdem hier — sie schaltet die **Uebergaenge**
           ab, falls dem Streifen je einer zuwaechst. */
        @supports (animation-timeline: scroll()) {
            [data-part="fortschritt"] {
                /* Fuenf Punkte hoch, und das ist die erlaubte Ausnahme von
                   AW16: eine Linie, deren Mass ihr Aussehen ist. */
                block-size: 5px;
                animation: lesefortschritt linear;
                animation-timeline: scroll(root block);
            }
        }

        @media (prefers-reduced-motion: reduce) {
            [data-part="fortschritt"] { transition: none; }
        }

        @media (min-width: 60rem) {
            :scope > nav > ul { justify-self: center; }
        }
    }
}

/* Die Stauchung als Bild: von null auf ganz, linear ueber den Scrollweg. Ein
   `@keyframes` kennt keinen Scope — es steht deshalb neben ihm, in derselben
   Layer, und traegt den Paketnamen im eigenen Namen, damit es nicht mit einem
   fremden kollidiert ({T0095}). */
@layer slices.page_header {
    @keyframes lesefortschritt {
        to { scale: 1 1; }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Einleitung — Text links, Seed rechts, ab screen nebeneinander. */

@layer slices.slice_intro {
    @scope ([data-slice="intro"]) {
        :scope {
            padding-block: 3rem 2.75rem;
            row-gap: var(--space-close);

            /* Unten ausgerichtet, wie der Entwurf: die Seed-Zeile sitzt damit auf
               derselben Grundlinie wie die letzte Textzeile daneben, statt oben
               anzusetzen und das Band in die Höhe zu ziehen. */
            align-items: end;

            /* Vier Ebenen, von oben nach unten gelesen:

               1. das Zeilenraster aus `--interlace`. Es stand hier ausgeschrieben
                  und war damit die stillschweigende Vorlage für jede weitere
                  Rasterung — die Biomleiste schrieb es ab und traf die Werte
                  nicht. Jetzt gibt es eine Definition, und beide lesen sie
                  \({K0097}\).
               2. der Verlauf, der den Text trägt — links deckend, nach rechts
                  durchscheinend. Er dunkelt dort, wo gelesen wird, und läßt das
                  Bild dort stehen, wo nur der Seed-Knopf sitzt.
               3. das Bild selbst, über image-set: der Browser nimmt AVIF, wo er
                  es kann, sonst WebP. Drei Breiten stehen bereit, die Auswahl
                  trifft die Breitenabfrage weiter unten.
               4. die Grundfarbe, falls kein Bild lädt — dann steht der Text auf
                  ihr und bleibt lesbar. */
            background:
                var(--interlace),
                linear-gradient(to right,
                    oklch(12% 0.01 260 / 0.94) 0%,
                    oklch(12% 0.01 260 / 0.86) 38%,
                    oklch(12% 0.01 260 / 0.52) 72%,
                    oklch(12% 0.01 260 / 0.38) 100%),
                image-set(url("/press/skepp-860.avif") type("image/avif"),
                          url("/press/skepp-860.webp") type("image/webp")),
                var(--surface-2);
            background-size: var(--interlace-size), auto, cover, auto;

            /* Der Bildinhalt sitzt rechts — Drache und Mannschaft. Ohne das
               zeigt ein schmales Fenster nur Wasser. */
            background-position: center, center, right center, center;
            background-repeat: repeat, no-repeat, no-repeat, repeat;
        }

        /* Basis: alles untereinander. Der Seed steht zuletzt, weil man erst liest,
           was die Seite kann, und dann eingibt. */
        [data-type="seed-form"] { margin-block-start: var(--space-open); }

        /* Ab screen rueckt der Seed nach rechts neben den Text — die Anordnung
           des Entwurfs. Die Spuren des Slices werden dafür geteilt: die
           content-Spur traegt zwei Spalten statt einer. */
        /* Größere Fassungen erst, wo sie gebraucht werden: 48 kB am Telefon
           gegen 186 am Schreibtisch. */
        @media (min-width: 35rem) {
            :scope {
                background-image:
                    var(--interlace),
                    linear-gradient(to right,
                        oklch(12% 0.01 260 / 0.94) 0%,
                        oklch(12% 0.01 260 / 0.86) 38%,
                        oklch(12% 0.01 260 / 0.52) 72%,
                        oklch(12% 0.01 260 / 0.38) 100%),
                    image-set(url("/press/skepp-1280.avif") type("image/avif"),
                              url("/press/skepp-1280.webp") type("image/webp"));
            }
        }

        @media (min-width: 60rem) {
            :scope {
                background-image:
                    var(--interlace),
                    linear-gradient(to right,
                        oklch(12% 0.01 260 / 0.94) 0%,
                        oklch(12% 0.01 260 / 0.86) 38%,
                        oklch(12% 0.01 260 / 0.52) 72%,
                        oklch(12% 0.01 260 / 0.38) 100%),
                    image-set(url("/press/skepp-1920.avif") type("image/avif"),
                              url("/press/skepp-1920.webp") type("image/webp"));
                grid-template-areas:
                    "chip   ."
                    "head   ."
                    "text   seed";
                grid-template-columns:
                    [full-start] var(--rand)
                    [content-start] minmax(0, 1fr) 4.375rem 26.25rem
                    [content-end] var(--rand)
                    [full-end];
            }

            /* Etikett und Ueberschrift laufen ueber beide Spalten: die Ueberschrift
               ist die Ansage der Seite und soll nicht auf drei Fuenftel umbrechen,
               waehrend daneben Platz frei bleibt. Erst der Satz darunter teilt sich
               die Zeile mit dem Seed. */
            > [data-type="chip"]      { grid-column: 2 / 5; }
            > [data-type="heading"]   { grid-column: 2 / 5; }
            > [data-type="prose"]     { grid-column: 2; }
            > [data-type="seed-form"] { grid-column: 4; margin-block-start: 0; }
            > [data-type="chip"], > [data-type="heading"] { align-self: start; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Ein Gegenstand als Seite — Kopf mit Beschreibung, darunter das
   Dossier in zwei Spalten. */

@layer slices.slice_item {
    @scope ([data-slice="item"]) {
        :scope {
            display: grid;
            /* **Nur Zeilen, keine Spalten.** `gap` legt sich auch zwischen
               die Rasterspalten des Seitenlayouts und schob die Inhaltsspalte
               um elf Punkte nach rechts - der Block stand dann neben der
               Linie, an der Kopf und Fusszeile beginnen. */
            row-gap: var(--space-close);
            padding-block: var(--space-close);
            /* Eine Grenze fuer die Breite (Owner, 7. September 2026: »ich
               finde es dehnt sich zu weit«). Auf einem breiten Schirm zog die
               Zahlenspalte die Werte an den rechten Rand, und zwischen der
               Beschriftung und ihrer Zahl lag eine halbe Handbreit Nichts.

               64 rem sind gut tausend Punkte: die Herkunft behaelt ihre
               Zeilenlaenge, die vier Stufenspalten stehen ohne Gedraenge, und
               das Auge findet von der Beschriftung zur Zahl. Links
               ausgerichtet und nicht mittig - die Seite darunter ist es
               auch. */
            /* Der Container für die Abfragen darunter: ohne ihn messen sie den
               nächsten Vorfahren mit `container-type`, und den gibt es hier
               nicht — die zwei Spalten blieben eine. */
            container-type: inline-size;
        }

        /* **Die Grenze gilt den Zeilen, nicht dem Block** (Owner, 7.
           September 2026). Der Block traegt die Spalten des Seitenrasters,
           und wer ihn schmaler macht, schrumpft sie mit: der Inhalt stand
           dann links neben der Linie, an der Kopf, Banner und Fusszeile
           beginnen. */
        :scope > * { max-inline-size: 64rem; }

        /* Der Kopf: Zeichen, Name, Gebiet — und rechts die Beschreibung.
           Alles auf einer Linie mittig zum Zeichen (Owner, 7. September 2026):
           der Name stand darüber und das Gebiet weit darunter, weil das
           Zeichen zwei Reihen überspannte.

           Der Name bricht nicht um: er hat die Breite der Seite, und ein
           Umbruch im Titel sähe nach Enge aus, wo keine ist. */
        [data-part="kopf"] {
            display: flex;
            align-items: center;
            gap: 1.5rem;
            flex-wrap: wrap;

            [data-type="game-icon"] {
                inline-size: 64px;
                block-size: 64px;
                flex: none;
            }

            [data-type="heading"] {
                margin: 0;
                white-space: nowrap;
            }
        }

        [data-part="titel"] { flex: none; }

        [data-part="gebiet"] {
            margin: 0;
            color: var(--gold);
            font-size: var(--text-small);
        }

        /* Die Beschreibung nimmt den Rest der Zeile und beginnt erst, wo der
           Kopf endet. **Im Kopf und nur dort**: `data-part="text"` heißt auch
           der Satz zur Herstellung, und der steht im Dossier. */
        /* Der Text als eigene Zeile, durchgehend zwischen Titel und Zahlen.
           **Nur als direktes Kind**: `data-part="text"` heisst auch der Satz
           zur Herstellung, und der steht im Dossier.

           Die Zeilenlaenge ist gedeckelt: ueber 75 Zeichen findet das Auge den
           Anfang der naechsten Zeile schlecht, und dieser Text wird gelesen
           und nicht ueberflogen. */
        :scope > [data-part="text"] {
            margin: 0;
            max-inline-size: 75ch;
            color: var(--ink);
            line-height: 1.6;

            /* Der Atmosphaerentext des Spiels ist ein Zitat: kursiv und in den
               Anfuehrungszeichen seiner Sprache (Owner, 7. September 2026).
               Die Zeichen kommen aus der Sprachtabelle und stehen im Text -
               `q` setzt eigene, und die waeren die des Browsers. */
            q { font-style: italic; quotes: none; }
        }

        /* Der Satz fuer die Suche, eine Zeile unter dem Zitat: er gehoert
           dazu und ist doch nicht dasselbe - gedaempft, weil er nicht vom
           Gegenstand erzaehlt, sondern vom Eintrag. */
        [data-part="seo"] {
            margin: 0;
            max-inline-size: 75ch;
            color: var(--ink);
            line-height: 1.6;
        }

        /* Der Knopf rechts unten, so breit wie sein Wort. */
        [data-part="planner"] {
            display: flex;
            justify-content: flex-end;
            margin: 0;

            /* Der Knopf nimmt sonst die Breite seiner Zeile: im Blatt ist das
               richtig, hier ist die Zeile die Seite. */
            [data-type="button"] { inline-size: auto; }
        }

        /* Das Dossier zweispaltig, sobald Platz ist (Owner, 7. September
           2026): die Herkunft links, die Werte rechts. Auf dem Telefon
           untereinander — dieselbe Reihenfolge, eine Spalte.

           `align-items: start`, damit die kürzere Spalte nicht mitwächst, und
           `column-gap` größer als der Zeilenabstand, weil zwei Spalten ohne
           Luft wie eine Tabelle aussehen. */
        /* Zwei Spalten, sobald Platz ist; auf dem Telefon untereinander.
           Links Herkunft und Herstellung, rechts die Werte - je ein Element,
           damit die Hoehe der einen Spalte die andere nicht verschiebt. */
        [data-part="spalten"] {
            display: grid;
            row-gap: var(--space-close);
        }

        [data-part="links"],
        [data-part="rechts"] {
            display: grid;
            row-gap: var(--space-close);
            align-content: start;
        }

        /* Zwischen Herkunft und Herstellung eine goldene Linie, darueber
           Luft bis zur gedachten Linie unter dem letzten Eintrag. */
        [data-part="links"] [data-art="herstellung"] {
            border-block-start: 1px solid var(--gold);
            /* Von der Linie zur Ueberschrift --space-close, 10,5 Punkte
               (Owner, 7. September 2026: »um 8px«, auf die Skala gelegt).
               Massgeblich ist die gleichlautende Regel in slice_items.css:
               sie liegt im selben Layer, kommt spaeter und gewinnt. */
            padding-block-start: var(--space-close);
        }

        @container (min-width: 45rem) {
            [data-part="spalten"] {
                grid-template-columns: repeat(2, minmax(0, 1fr));
                align-items: start;
            }

            /* Und zwischen den Spalten eine goldene Linie: der linke Rand der
               rechten Spalte, mit Luft auf beiden Seiten. */
            [data-part="rechts"] {
                border-inline-start: 1px solid var(--gold);
                padding-inline-start: 1.5rem;
                margin-inline-start: 1.5rem;
            }
        }

        /* Die Werte als Tabelle ohne Linien ({T0056S1}, Owner, 7. September
           2026): eine Spalte je Stufe, der Kopf traegt ihre Farbmarke.

           Der Abstand zwischen den Spalten ist ein Rem, sechzehn Punkte, wie
           der Owner es genannt hat. Die Reihe der Seite faellt mit dem
           goldenen Schnitt und bietet hier 10,5 (`--space-close`) und 27,5
           (`--space-open`) - keiner trifft, und zwischen zwei Zahlenspalten
           ist zehn zu eng und siebenundzwanzig zu weit. Die Zeilenluecke
           bleibt aus der Reihe. */
        [data-part="werte"] {
            inline-size: 100%;
            border-collapse: separate;
            border-spacing: 1rem var(--space-tight);
            margin-inline: calc(var(--space-close) * -1);

            th, td {
                padding: 0;
                text-align: end;
                font-weight: 500;
                font-variant-numeric: tabular-nums;
                color: var(--ink-bright);
            }

            /* Die Beschriftung links, in der Tintenfarbe: sie ist die Frage,
               die Zahl ist die Antwort. */
            tbody th {
                text-align: start;
                font-weight: 400;
                color: var(--ink);
            }

            /* Der Kopf: die Stufe in ihrer Rangfarbe, klein und mittig ueber
               ihrer Spalte — dieselben Farben wie die Pillen im Blatt. */
            thead th {
                text-align: end;
                font-size: var(--text-label);
                color: var(--rang, var(--ink));
            }

            thead th[data-rank="1"] { --rang: var(--rank-1); }
            thead th[data-rank="2"] { --rang: var(--rank-2); }
            thead th[data-rank="3"] { --rang: var(--rank-3); }
            thead th[data-rank="4"] { --rang: var(--rank-4); }

            /* Die Kennung: eine Eingabe, keine Zahl — feste Schrift und eine
               Leerzeile darueber, wie im Blatt ({T0039S1}). */
            tr[data-art="id"] td {
                font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                font-size: var(--text-small);
                font-weight: 400;
                user-select: all;
            }

            tr[data-art="id"] th,
            tr[data-art="id"] td { padding-block-start: 1lh; }
        }

        /* Der Rueckweg mit goldenem Winkel (Owner, 7. September 2026): ein
           Chevron nach links, in derselben Farbe wie die Ecken der Kacheln.
           Es steht als Pseudoelement und nicht im Text - ein Zeichen, das
           kein Wort ist, gehoert nicht in die Sprachtabelle und nicht in den
           Vorlesestrom. */
        [data-part="zurueck"] {
            margin: 0;
            /* Einen Schritt groesser auf der Skala (Owner, 7. September 2026:
               »ca. 6 px groesser«): die Grundgroesse statt --text-small, also
               17 statt 13,4 Punkte, und das Chevron mit 1,45 em auf 25 statt
               19 - das trifft die sechs Punkte. Der naechste Schritt,
               --text-large, machte das Chevron 30 Punkte gross und riss den
               Rueckweg aus dem Massstab der Seite. Beide auf einer
               Mittellinie. */
            font-size: var(--scale-base);

            a {
                display: inline-flex;
                align-items: center;
                gap: 0.4em;
                color: var(--ink);
                text-decoration: none;
            }

            a::before {
                content: "\2039";
                color: var(--gold);
                font-size: 1.45em;
                line-height: 1;
            }

            /* Die Linie beim Zeigen unter dem Wort, nicht unter dem Chevron:
               deshalb am span und nicht am Link. */
            a:where(:hover, :focus-visible) { color: var(--ink-bright); }

            a:where(:hover, :focus-visible) span {
                text-decoration: underline;
                text-underline-offset: 3px;
            }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Gegenstandsuebersicht — ein aufklappbarer Block je Gebiet. */

@layer slices.slice_items {
    /* Zwei Wurzeln, eine Gestalt ({T0056S1}): das Dossier steht im Blatt der
       Uebersicht und auf der eigenen Seite eines Gegenstands, und es ist
       dasselbe. Die Alternative waere gewesen, sechzig Regeln zu kopieren -
       und dann sehen zwei Dossiers nach dem naechsten Umbau verschieden aus.
       Was der Einzelseite eigen ist, steht in slice_item.css. */
    @scope ([data-slice="items"], [data-slice="item"], [data-slice="tree"]) {
        :scope {
            padding-block: 2.5rem;
            display: grid;
            row-gap: 1.25rem;
        }

        :scope > * { grid-column: content; }

        [data-part="intro"] {
            margin: 0.5rem 0 0;
            max-inline-size: var(--measure);
            color: var(--ink-quiet);
        }

        /* Die Zahl neben dem Gebiet bleibt die Gesamtzahl, auch wenn der Filter
           etwas ausblendet. Sie mitzurechnen ginge nur mit Skript, und eine
           Zahl, die manchmal stimmt, wäre schlechter als eine, die immer
           dasselbe meint. */

        /* Die Treffer stehen über allem anderen, weil sie die Antwort auf die
           Frage sind, die gerade gestellt wurde. */
        /* Dieselben Kacheln und Blätter wie in den Gebietslisten, im selben
           Raster — das Raster ist die Regel unten, die für jedes
           [data-part="grid"] gilt. Hier steht nur die Statuszeile. */
        [data-part="results"] {
            p {
                margin: 0 0 0.5rem;
                color: var(--ink-quiet);

                /* »Suchergebnisse: 19 · Holz« — die Beschriftung in derselben
                   Schrift und Farbe wie die Zahl, der Doppelpunkt im CSS wie
                   bei jeder Beschriftung im Blatt (Owner, 2. September 2026). */
                span::after { content: ":"; }
            }
        }

        /* ---- Der Gebietsblock ------------------------------------------ */

        /* Die Biomfarbe steht als eigene Eigenschaft am Block, damit Leiste,
           Fläche und Zwischenüberschriften dieselbe nehmen. Der Token für den
           Düsterwald heißt --biome-forest, unsere Kennung blackforest — das ist
           die einzige Stelle, wo die beiden Namen auseinandergehen, und sie wird
           hier aufgelöst statt in den Daten.
           
           Dieser Block ging beim Umbau des Filters verloren: ich habe Blöcke
           nach ihrer Position im Text geschnitten statt nach ihrem Namen, und
           der Schnitt lag zu weit. Danach war --biome leer, der Verlauf `none`
           und die Fläche grau — im CSS stand die Regel noch, sie hatte nur
           nichts mehr zu färben. */
        /* Vor dem ersten Gebiet das Doppelte der Rasterlücke (Owner,
           2. September 2026): die Suche und ihre Treffer sind eine Frage, die
           Gebiete sind der Bestand, und zwischen beidem liegt mehr als zwischen
           zwei Zeilen. Gemessen vorher 20 px, jetzt 40 — am ersten `details`,
           ob davor der Bedienstreifen steht oder die Trefferliste. Ein Rand,
           weil der Block eine Fläche mit Kante trägt und ein Innenabstand sie
           vergrößerte; die Gebiete halten ihren Abstand untereinander ohnehin
           über einen Rand. */
        [data-part="region"]:first-of-type { margin-block-start: 1.25rem; }

        [data-part="region"] {
            --biome: var(--surface-4);
            margin-block-end: 1.25rem;

            /* Der Bildstreifen als zweite Hintergrundschicht der Leiste, direkt
               unter dem Zeilenraster.

               Der Hohe Norden stand hier lange ohne — »dort gibt es nichts
               aufzunehmen«, hieß es, und das stimmte: bis Valheim 1.0 war das
               Gebiet nicht ausgebaut und meldete sich selbst als Gebirge
               ({K0207}, GetBiome gab oberhalb 0,4 Mountain zurück). Seit dem
               1.0-Patch ist es ein eigenes Biom mit 48 Gegenständen, und der
               Owner hat die Aufnahme am 9. September nachgeliefert. */
            &[data-biome="meadows"] > summary     { background-image: var(--interlace), var(--leiste-traeger), linear-gradient(var(--aufhellung), var(--aufhellung)), url("/biome/meadows.webp"), var(--biome-verlauf); }
            &[data-biome="blackforest"] > summary { background-image: var(--interlace), var(--leiste-traeger), linear-gradient(var(--aufhellung), var(--aufhellung)), url("/biome/blackforest.webp"), var(--biome-verlauf); }
            &[data-biome="swamp"] > summary       { background-image: var(--interlace), var(--leiste-traeger), linear-gradient(var(--aufhellung), var(--aufhellung)), url("/biome/swamp.webp"), var(--biome-verlauf); }
            &[data-biome="mountain"] > summary    { background-image: var(--interlace), var(--leiste-traeger), linear-gradient(var(--aufhellung), var(--aufhellung)), url("/biome/mountain.webp"), var(--biome-verlauf); }
            &[data-biome="plains"] > summary      { background-image: var(--interlace), var(--leiste-traeger), linear-gradient(var(--aufhellung), var(--aufhellung)), url("/biome/plains.webp"), var(--biome-verlauf); }
            &[data-biome="mistlands"] > summary   { background-image: var(--interlace), var(--leiste-traeger), linear-gradient(var(--aufhellung), var(--aufhellung)), url("/biome/mistlands.webp"), var(--biome-verlauf); }
            &[data-biome="ashlands"] > summary    { background-image: var(--interlace), var(--leiste-traeger), linear-gradient(var(--aufhellung), var(--aufhellung)), url("/biome/ashlands.webp"), var(--biome-verlauf); }
            &[data-biome="deepnorth"] > summary   { background-image: var(--interlace), var(--leiste-traeger), linear-gradient(var(--aufhellung), var(--aufhellung)), url("/biome/deepnorth.webp"), var(--biome-verlauf); }
            &[data-biome="ocean"] > summary       { background-image: var(--interlace), var(--leiste-traeger), linear-gradient(var(--aufhellung), var(--aufhellung)), url("/biome/ocean.webp"), var(--biome-verlauf); }

            &[data-biome="meadows"]     { --biome: var(--biome-meadows); }
            &[data-biome="blackforest"] { --biome: var(--biome-forest); }
            &[data-biome="swamp"]       { --biome: var(--biome-swamp); }
            &[data-biome="mountain"]    { --biome: var(--biome-mountain); }
            &[data-biome="plains"]      { --biome: var(--biome-plains); }
            /* Die Nebellande hatten hier eine Aufhellung von zwölf Prozent —
               sie lagen bei 37,7 mittlerer Helligkeit gegen rund 98 im Mittel.
               Die Vorlage des Owners bringt die Belichtung mit und liegt bei
               103,0; die Schicht würde jetzt doppelt hellen. Der Mechanismus
               bleibt, der Wert geht. */

            &[data-biome="mistlands"]   { --biome: var(--biome-mistlands); }
            &[data-biome="ashlands"]    { --biome: var(--biome-ashlands); }
            &[data-biome="deepnorth"]   { --biome: var(--biome-deepnorth); }
            &[data-biome="ocean"]       { --biome: var(--biome-ocean); }
        }

        /* ---- Die Leiste ------------------------------------------------ */

        /* Das direkte Kind, nicht jedes summary darunter. Ohne das `>` trug
           jeder Kartenkopf und jede Bereichszeile den Biomstreifen — sichtbar
           erst, als die Karten selbst <details> wurden. Ein Selektor, der zu
           weit greift, ist so lange richtig, wie es nur ein Ziel gibt. */
        [data-part="region"] > summary {
            position: relative;
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: 1rem;
            /* Das Seitenverhältnis der Vorlagen des Owners: 4717x646. Vorher
               standen hier 120 Pixel fest, und das Bild wurde per `cover` auf
               dieses Mass beschnitten — bei einem von Hand gesetzten Ausschnitt
               heisst das, ihn zu verwerfen. Die Leiste folgt jetzt dem Bild.

               **Die Breite folgt immer der Spur, nie dem Bild.** Mit einer
               Mindesthöhe und einem Seitenverhältnis rechnete der Browser die
               Mindesthöhe in eine Mindestbreite um — 120 Pixel mal 7,3 sind
               876 —, und die Leiste lief unter 880 Pixel Fenster nach rechts
               über ({C0021}). `max-inline-size: 100%` bindet sie an ihre Spur;
               darunter gibt das Verhältnis nach, die Mindesthöhe bleibt, und
               `cover` beschneidet seitlich — bei einem Panorama die richtige
               Richtung. Die Mindesthöhe ist klein, gerade so, daß Bild und
               Beschriftung lesbar bleiben (Owner, 2. September 2026): 4,5 rem,
               eine Pixelzeile der Beschriftung mit Luft, kein px. */
            aspect-ratio: 4717 / 646;
            max-inline-size: 100%;
            min-block-size: 4.5rem;
            padding: 0 1.25rem;
            cursor: pointer;
            overflow: hidden;
            color: var(--ink-bright);

            /* Ohne Bild bleibt der Biomfarbverlauf stehen — der Hohe Norden hat
               keine Aufnahme, weil es dort nichts zu sehen gibt. */
            /* Der Textträger, als eigene Schicht — genau die Rolle, die im Hero
               der zweite Verlauf spielt.

               Sie fehlte hier, und das war der eigentliche Fehler hinter dem zu
               dunklen Raster: das Raster trug die Arbeit mit, für die es nicht
               gemacht ist. Auf 28 Prozent hochgedreht dunkelte es das ganze Bild
               ab, damit die Schrift trug — und war als Raster nicht mehr zu
               erkennen. Zwei Aufgaben in einer Schicht, und beide schlecht
               erledigt.

               Getrennt kann jede tun, was sie soll: das Raster rastert bei 14
               Prozent, der Träger trägt dort, wo Text steht. Anders als im Hero
               an beiden Rändern, weil hier links der Gebietsname und rechts die
               Zählung steht; die Mitte bleibt frei und zeigt die Aufnahme
               ({K0097}). */
            --leiste-traeger: linear-gradient(90deg,
                oklch(10% 0.01 260 / 0.88) 0%,
                oklch(10% 0.01 260 / 0.62) 22%,
                oklch(10% 0.01 260 / 0.30) 42%,
                oklch(10% 0.01 260 / 0.30) 58%,
                oklch(10% 0.01 260 / 0.62) 78%,
                oklch(10% 0.01 260 / 0.88) 100%);

            --biome-verlauf: linear-gradient(90deg,
                color-mix(in oklab, var(--biome) 35%, var(--surface-0)),
                color-mix(in oklab, var(--biome) 12%, var(--surface-0)));

            /* Ohne Bild bleiben Raster und Farbverlauf — der Hohe Norden hat
               keine Aufnahme. Die Gebiete mit einer schieben ihre Schicht
               dazwischen; die Reihenfolge und die Größen gelten für beide
               Fälle, weil die letzte Größe für jede weitere Schicht wiederholt
               wird. */
            /* Eine Aufhellungsschicht, die jedes Gebiet für sich setzen kann.
               Sie liegt unter dem Textträger und über dem Bild und wird mit
               `screen` verrechnet: das hebt die Tiefen an und läßt die Lichter
               stehen, statt einen grauen Schleier darüberzulegen.

               Für alle Gebiete ist sie durchsichtig und tut nichts — sie steht
               trotzdem in der Grundregel, damit die Zahl der Schichten überall
               dieselbe ist und die Listen darunter für beide Fälle passen. */
            --aufhellung: transparent;

            background-image:
                var(--interlace),
                var(--leiste-traeger),
                linear-gradient(var(--aufhellung), var(--aufhellung)),
                var(--biome-verlauf);
            background-blend-mode: normal, normal, screen, normal, normal;
            /* Drei Werte, obwohl hier nur zwei Schichten liegen: die Gebiete mit
               Aufnahme schieben das Bild dazwischen, und CSS wiederholt bei zu
               wenigen Werten zyklisch — der Farbverlauf hätte dann das Maß des
               Rasters bekommen und wäre gekachelt worden. Überzählige Werte
               werden dagegen einfach nicht benutzt. */
            background-size: var(--interlace-size), auto, auto, cover, cover;
            background-position: center, center, center, center, center;
            background-repeat: repeat, no-repeat, no-repeat, no-repeat, no-repeat;

            box-shadow:
                inset  2px  2px 0 oklch(33% 0.014 var(--hue-surface)),
                inset -2px -2px 0 oklch(12% 0.010 var(--hue-surface));
        }

        /* Das Zeilenraster kommt aus `--interlace` und liegt als Schicht im
           `background` der Leiste — nicht als Auflage in einem `::before`.

           Es stand hier ausgeschrieben, mit einer dunklen Zeile auf zwei bei 28
           Prozent statt einer auf drei bei 14: dreimal so dunkel, und weil bei
           zwei Pixeln Kachel Zeile und Lücke auf einem doppelt auflösenden
           Schirm ineinanderlaufen, kam vom Raster nichts an. Nur ein dunkles
           Bild. Der Kommentar daneben behauptete »wie im Hero der Startseite«
           und deckte den Fehler damit zu \({K0097}\).

           Die Gegenprobe gehört dazu: die Streifen selbst sind in Ordnung. Ein
           frischer Lauf von tools/biome-streifen.py ergibt 88,1 mittlere
           Helligkeit, ausgeliefert liegen 87,9 — das ist die WebP-Kompression.
           Das Skript dunkelt an keiner Stelle ab. */

        [data-part="region-name"],
        [data-part="count"] {
            position: relative;
            text-shadow: 0 1px 3px oklch(0% 0 0 / 0.8);
        }

        /* Die Pixelschrift auf der Gebietsleiste: sie ist die Überschrift des
           Blocks, und was darunter kommt, ordnet sich ihr unter. Vorher trug sie
           die Grundschrift und die Kategorie darunter die Pixelschrift — das
           stellte die Hierarchie auf den Kopf. */
        [data-part="region-name"] {
            font-family: "Press Start 2P", monospace;
            /* Dieselbe Größe und Zeilenhöhe wie `heading` auf Ebene 3 — dort
               stand die Kategorie vorher, und der Owner will sie unverändert
               eine Ebene höher. Eine eigene Zahl hier hieße, dieselbe Schrift
               an zwei Stellen verschieden groß zu setzen. */
            font-size: var(--text-large);
            line-height: 1.55;
            color: var(--ink-bright);
        }

        /* Die Zahl mit ihrer Einheit, rechts. Die Einheit ist kleiner und
           gedämpft: sie sagt, was gezählt wurde, und tritt hinter die Zahl
           zurück. */
        /* An `summary` gebunden, sonst gewinnt die Regel der Gruppe: sie steht
           weiter unten und ist ebenso spezifisch, und bei Gleichstand
           entscheidet die Reihenfolge. Die Zählung auf dem Streifen stand
           dadurch gedämpft statt weiß. */
        [data-part="region"] > summary [data-part="count"] {
            display: flex;
            align-items: baseline;
            gap: 0.5em;
            margin-inline-start: auto;
            /* Weiß wie der Gebietsname: beides steht auf demselben Streifen und
               gehört zusammen. Gedämpft wäre es hier eine zweite Ebene, die es
               nicht gibt. */
            color: var(--ink-bright);
            font-family: "Press Start 2P", monospace;
            font-size: var(--text-large);
            line-height: 1.55;
            font-variant-numeric: tabular-nums;

            i { font-style: normal; }
        }

        /* Am Telefon fällt das Wort weg und die Zahl bleibt. Sie steht dort
           allein und braucht keine Erklärung — der Platz ist knapper als die
           Auskunft wert ist. Mobile first: der Ruhewert ist ohne Wort, ab
           30 rem kommt es dazu — nur `min-width`, wie AW7 es verlangt; hier
           stand die Abfrage andersherum, nach oben begrenzt ({C0022}). */
        [data-part="count"] i { display: none; }

        @media (min-width: 30rem) {
            [data-part="count"] i { display: inline; }
        }

        /* ---- Die offene Fläche ------------------------------------------ */

        [data-part="body"] {
            position: relative;
            /* 1 rem und nicht 1,25: die Rechnung geht sonst nicht auf. Der
               Inhaltsbereich ist bei 1440 px Fenster 1360 breit; abzüglich
               2×16 bleiben 1328, und 5×256 + 4×12 sind genau 1328. */
            padding: 1rem;
            background: var(--surface-1);
            box-shadow:
                inset  2px  2px 0 oklch(33% 0.014 var(--hue-surface)),
                inset -2px -2px 0 oklch(12% 0.010 var(--hue-surface));
            overflow: hidden;
        }

        /* Die Biomfarbe als Verlauf: oben am stärksten, leicht schräg nach
           unten hin durchsichtig. Kein Muster, keine Bewegung — die Fläche soll
           das Gebiet färben und nicht auf sich zeigen.
           
           Hier standen wandernde Linien. Sie waren zu laut für eine Fläche, auf
           der gelesen wird: was sich bewegt, zieht das Auge, und das Auge wurde
           hier für Kartennamen gebraucht.
           
           170 Grad statt 180: gerade so schräg, daß die Kante nicht waagerecht
           liegt und der Verlauf nicht wie ein Streifen wirkt. */
        [data-part="body"]::before {
            content: "";
            position: absolute;
            inset: 0;
            /* Halb so kräftig wie zuerst — 13 statt 26 Prozent oben. Der
               Owner: die Fläche soll gefärbt sein, nicht bemalt.
               
               Und sie wird nicht entfernt, wenn ein Text darauf zu wenig
               Kontrast hat: dann wird der Text fett oder größer. Einen
               vorgegebenen Hintergrund wegzunehmen, um eine Kontrastregel zu
               erfüllen, löst das Falsche. */
            background: linear-gradient(
                170deg,
                color-mix(in oklab, var(--biome) 13%, transparent) 0%,
                color-mix(in oklab, var(--biome)  7%, transparent) 35%,
                color-mix(in oklab, var(--biome)  3%, transparent) 70%,
                transparent 100%);
            pointer-events: none;
        }

        [data-part="body"] > * { position: relative; }

        /* ---- Die Zwischenüberschrift je Gattung -------------------------- */

        [data-part="group"] {
            display: flex;
            justify-content: space-between;
            align-items: baseline;
            gap: 1rem;
            margin: 1.25rem 0 0.5rem;
            padding-block-end: 0.25rem;
            border-block-end: 1px solid color-mix(in oklab, var(--biome) 30%, transparent);

            &:first-child { margin-block-start: 0; }

            /* **Eine Ausnahme vom Element, ausdrücklich hier.** `heading` trägt
               die Pixelschrift; auf dieser Ebene wäre sie zu laut, weil die
               Gebietsleiste darüber sie schon führt. Also die Grundschrift,
               gedämpft und in Versalien — und nur hier. Überall sonst bleibt
               `heading`, wie es ist ({K0054}). */
            /* --ink und nicht --ink-quiet: auf der Biomfläche stand die leise
               Stufe bei 4,29 zu 1 und damit unter AA — gemessen auf einfarbigem
               Grund, also sicher und nicht geschätzt. Die Fläche bleibt, wie sie
               ist; korrigiert wird die Schrift ({K0096}). */
            [data-type="heading"] {
                font-family: inherit;
                font-size: 0.9375rem;
                font-weight: 400;
                color: var(--ink);
                text-transform: uppercase;
                letter-spacing: 0.08em;
            }

            /* Die Zählung hier ohne Pixelschrift, aus demselben Grund — aber
               mit demselben Aufbau: Abstand zwischen Zahl und Einheit, und die
               Einheit aufrecht. Beides stand nur an der Leiste, weil deren Regel
               spezifisch wurde, und hier klebte »27Items« kursiv aneinander. */
            [data-part="count"] {
                display: flex;
                align-items: baseline;
                gap: 0.5em;
                font-family: inherit;
                font-size: 0.9375rem;
                /* Dieselbe Stufe wie die Überschrift daneben, aus demselben
                   Grund: sie steht auf derselben Fläche. */
                color: var(--ink);
                font-variant-numeric: tabular-nums;

                i { font-style: normal; }
            }
        }

        /* Der Filter schaltet ganze Kategorien, nicht einzelne Karten.
           
           Die Kategorien sind kein Akkordeon — sie stehen offen untereinander,
           und der Schalter blendet den Block samt Überschrift aus. Sechs Regeln
           statt einer Bibliothek, und der Zustand steht im Formular.
           
           **Umgekehrte Vorgabe:** was nicht angeschaltet ist, ist fort. Vorher
           war alles sichtbar, solange niemand etwas wählte; jetzt sind es die
           Rohstoffe, weil das Kästchen `checked` trägt. Wer alle ausschaltet,
           sieht nichts — das ist ehrlich und wieder rückgängig. */
        [data-part="group"],
        [data-part="group"] + [data-part="grid"] { display: none; }


        /* Überschrift und Raster gehören zusammen: was verschwindet, ist das
           Paar. Die Karten darin brauchen keine eigene Regel — sie liegen im
           Raster und gehen mit ihm. */
        &:has(#k-material:checked) [data-part="group"][data-kind="material"],
        &:has(#k-tool:checked)     [data-part="group"][data-kind="tool"],
        &:has(#k-armour:checked)   [data-part="group"][data-kind="armour"],
        &:has(#k-weapon:checked)   [data-part="group"][data-kind="weapon"],
        &:has(#k-food:checked)     [data-part="group"][data-kind="food"],
        &:has(#k-building:checked) [data-part="group"][data-kind="building"],
        &:has(#k-misc:checked)     [data-part="group"][data-kind="misc"] {
            display: flex;
        }

        &:has(#k-material:checked) [data-part="group"][data-kind="material"] + [data-part="grid"],
        &:has(#k-tool:checked)     [data-part="group"][data-kind="tool"]     + [data-part="grid"],
        &:has(#k-armour:checked)   [data-part="group"][data-kind="armour"]   + [data-part="grid"],
        &:has(#k-weapon:checked)   [data-part="group"][data-kind="weapon"]   + [data-part="grid"],
        &:has(#k-food:checked)     [data-part="group"][data-kind="food"]     + [data-part="grid"],
        &:has(#k-building:checked) [data-part="group"][data-kind="building"] + [data-part="grid"],
        &:has(#k-misc:checked)     [data-part="group"][data-kind="misc"]     + [data-part="grid"] {
            /* Muß `grid` sein und dasselbe wie in der Grundregel des Rasters.

               Diese Zeile stand binnen einer Stunde auf beiden Werten und war
               beide Male einmal falsch: als Spalten gebaut wurden, ignorierte
               das `grid` hier die `columns`; als das Raster zurückkam,
               ignorierte das `block` die Spaltenvorgabe und legte alle Karten
               untereinander — `grid-template-columns` stand da und tat nichts,
               weil der Anzeigetyp ein anderer war.

               **Der sichtbare Zustand wiederholt den Anzeigetyp der
               Grundregel.** Wer den einen ändert und den anderen vergißt, sieht
               es erst im Bild ({K0099}). */
            display: grid;
        }

        /* Dieselbe Pixelkante wie Karte und Knopf, damit ein Gebietsblock zur
           Sprache gehört und nicht daneben steht. */
        [data-part="region"] {
            background: var(--surface-1);
            box-shadow:
                inset  2px  2px 0 oklch(33% 0.014 var(--hue-surface)),
                inset -2px -2px 0 oklch(12% 0.010 var(--hue-surface));
        }

        /* Der Marker bleibt: er sagt ohne Farbe und ohne Bewegung, ob offen
           oder zu, und das ist die einzige Auskunft, die hier zählt. */
        [data-part="count"] {
            color: var(--ink-quiet);
            font-variant-numeric: tabular-nums;
        }

        /* Das Raster trägt Karten von 256 px. Bei 1440 px Fenster bleiben dem
           Inhalt 1360, abzüglich des Innenabstands 1328 — und 5×256 + 4×12 sind
           genau das.
           
           Die erste Fassung rechnete mit 1440 und 16 px Rinne, kam auf 1344 und
           war damit 24 zu breit: es standen vier Karten je Reihe, jede auf 318
           gestreckt. Gemessen wurde es erst am Bild, nicht am Markup — dort
           sieht ein zu weites minmax genauso aus wie ein passendes.
           
           auto-fill statt fester Spaltenzahl: schmaler wird es von selbst
           weniger, ohne einen Haltepunkt je Stufe. */
        /* Raster, und die offene Karte überlagert statt zu schieben.

           Es waren kurz Spalten. Der Grund dafür war richtig — im Raster ist die
           Zeilenhöhe die der höchsten Karte, also machte eine geöffnete vier
           Nachbarn zu leeren Kästen. Der Preis war es nicht: bei jedem Klick
           verteilte der Spaltensatz seine Karten neu, und die ganze Liste
           sprang. Der Owner: »so fluppt die liste immer hin und her«.

           Jetzt behält die Karte ihre Zelle, und ihr Dossier legt sich über
           das, was darunter steht. Nichts bewegt sich, nichts wird umsortiert.

           Mein Einwand dagegen war falsch herum gedacht: verdeckt wird nicht,
           wonach man als nächstes sieht — man sieht auf die Karte, die man
           gerade geöffnet hat. Was man beim Spaltensatz dagegen bei jedem Klick
           sieht, ist die Umsortierung. */
        [data-part="grid"] {
            display: grid;
            /* min(256px, 100%): unter 256 Pixel Spur — bei 320 Pixel Fenster
               bleiben der Fläche 245 — gibt die Karte nach, statt über den
               Rand zu ragen. Der Breitenlauf sah es erst, als die Leiste nicht
               mehr davor überlief ({C0021}). */
            grid-template-columns: repeat(auto-fill, minmax(min(256px, 100%), 1fr));
            gap: 12px;
            /* Die Karten strecken sich nicht auf die Zeilenhöhe — ohne das
               zöge eine offene Karte ihre Nachbarn mit in die Länge. */
            align-items: start;
            margin: 0;
            padding: 0;
        }

        /* ---- Die Kachel ist ein Widget ({T0073S2}) ------------------------ */
        /* Ihre Regeln stehen in widgets/item_tile/item_tile.css; hier bleibt
           nur, was das Raster der Items-Seite von ihr will: nichts. */
        /* ---- Die Bereiche im Blatt --------------------------------------- */

        /* Die Trennlinie steht über jedem Bereich außer dem ersten: so trennt
           sie und rahmt nicht. */
        /* Auf der eigenen Seite keine graue Haarlinie (Owner, 7. September
           2026): sie trennt im Blatt zwei Abschnitte untereinander, dort
           stehen Herkunft und Werte nebeneinander und eine goldene Linie
           trennt sie. Die Ausnahme steht hier, weil beide Regeln im selben
           Layer liegen und die spaetere gewinnt. */
        /* Auf der eigenen Seite zeigt die Herstellung die Grundstufe
           ({T0056S1}): im Blatt waehlt die Pille, welche Stufe sichtbar ist,
           auf der Seite gibt es keine mehr - und ohne Wahl liefen die Mengen
           aller vier Stufen ineinander, aus 2, 3, 5 und 8 Holz wurde »2358
           Holz«. Steht hier, weil die Regeln zu data-level in dieser Datei
           liegen und im selben Layer die spaetere gewinnt. */
        :scope[data-slice="item"] [data-art="herstellung"] [data-level]:not([data-level="1"]) {
            display: none;
        }

        /* Die Marke der Tester unter der Herstellung ({T0062S3}): Gold, weil
           sie sagt, dass jemand es im Spiel gesehen hat - das ist, was hier
           erinnert werden soll. Gilt im Blatt wie auf der Seite. */
        [data-part="section"] [data-art="geprueft"] {
            color: var(--gold);
        }

        :scope[data-slice="item"] [data-part="section"] + [data-part="section"] {
            border-block-start: none;
            padding-block-start: 0;
        }

        /* Ausser zwischen Herkunft und Herstellung: dort eine goldene Linie
           (Owner, 7. September 2026). Steht hier, weil die Regel darueber sie
           sonst wieder abraeumt - dieselbe Datei, derselbe Layer. */
        :scope[data-slice="item"] [data-part="section"] + [data-part="section"][data-art="herstellung"] {
            /* Spezifischer als die Abschaltung darueber, sonst gewinnt die -
               und dann steht gar keine Linie da, weder grau noch gold. */
            border-block-start: 1px solid var(--gold);
            /* Luft zwischen Linie und Ueberschrift: --space-close, 10,5 Punkte
               (Owner, 7. September 2026, »um 8px« auf die Skala gelegt). Die
               Abschaltung darueber setzte sie auf 0; slice_item.css kam nie
               zum Zug. */
            padding-block-start: var(--space-close);
        }

        [data-part="section"] + [data-part="section"] {
            border-block-start: 1px solid oklch(from var(--ink-quiet) l c h / 0.20);
        }

        [data-part="head"] {
            display: flex;
            align-items: baseline;
            gap: 0.5rem;
            margin: 0;
            padding-block: 0.75rem 0.375rem;
        }

        [data-part="title"] {
            color: var(--ink-bright);
            font-size: 0.8125rem;
            font-weight: 500;
            text-transform: uppercase;
            letter-spacing: 0.07em;
        }

        [data-part="aside"] {
            min-inline-size: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            margin-inline-start: auto;
            color: var(--ink);
            font-size: 0.75rem;
        }

        /* Zwei Spuren: eine für Beschriftungen, eine für das, was dahinter
           steht. Die gewöhnlichen Zeilen spannen beide; die Kostenzeilen legen
           sich per subgrid hinein und teilen sich so die Ankerlinie — »Kosten
           insgesamt« und »Update auf Stufe 4« sind verschieden lang, und die
           Zutaten beginnen trotzdem an derselben Stelle (Owner, 2. September
           2026, nach dem Entwurf). Die Lücke zwischen Beschriftung und Zutat
           und zwischen den Zutaten ist dieselbe: rund 20 Pixel, Vorgabe des
           Owners; sie liegt zwischen --space und --space-open, deshalb hier
           als eigener Wert und nicht als Token. */
        [data-part="rows"] {
            --luecke: 1.25rem;
            display: grid;
            grid-template-columns: max-content 1fr;
            column-gap: var(--luecke);
            row-gap: 0.3125rem;
            padding-block-end: 0.5rem;
            font-size: 0.8125rem;
            line-height: 1.45;

            > * { grid-column: 1 / -1; }

            /* Acht Pixel mehr zwischen Fließtext und Liste, in beide
               Richtungen — grundsätzlich, auch wenn nach einer Liste wieder
               Fließtext folgt (Owner, 2. September 2026). Die Regel hängt am
               Übergang und nicht an einer Zeile: ein Satz vor den Kosten, ein
               Satz nach den Werten, beides dieselbe halbe Stufe. Verborgene
               Geschwister — die Sätze der anderen Stufen — sind von derselben
               Art wie der sichtbare, deshalb trägt `+` auch über sie hinweg. */
            [data-part="text"] + :not([data-part="text"]),
            :not([data-part="text"]) + [data-part="text"] {
                padding-block-start: 0.5rem;
            }
        }

        /* **Ein Grau im Blatt** (Owner, 2. September 2026): alles Gedämpfte
           trägt `--ink`, die hellere der beiden Stufen — Beschriftungen,
           Sätze, das Gebiet oben rechts, die Versionen, die Kosten und die
           leisen Hinweise. Vorher standen drei Grade nebeneinander, gedämpft,
           leise und leiser, und auf einem Blatt von 400 Pixeln las sich das
           als Unordnung, nicht als Ordnung. Was hervortritt, tut es durch
           Weiß und Gold, nicht durch ein drittes Grau. */
        /* **Zwei Sorten von Namen, zwei Farben, eine Stärke.**

           Orte tragen Gold — Gebiete, Fundstellen, Werkbänke. Dieselbe Farbe
           wie Ecke und Rahmen: was gold ist, gehört zu diesem Blatt und führt
           weiter. Gegenstände tragen Weiß, in derselben Schriftstärke (Owner,
           1. September 2026).

           Die Stärke bleibt gleich, weil der Unterschied die Art ist und nicht
           der Rang. Wer die Farbe einmal gelesen hat, erkennt beim nächsten
           Blatt sofort, was ein Ort ist und was ein Ding. */
        /* `row` ist eine Zeile mit Beschriftung — »Produkt: Schmiede« —, `text`
           ist Fließtext: der Satz zur Station, und was {T0020} an Sätzen noch
           bringt. Beide teilen Farbe und Auszeichnung; auseinandergehalten
           werden sie, weil der Abstand am Übergang hängt (siehe rows). */
        [data-part="row"],
        [data-part="text"] {
            margin: 0;
            color: var(--ink);

            /* Die Beschriftung vorn, gedämpft und mit Doppelpunkt: sie sagt,
               was für eine Zeile folgt. Der Doppelpunkt steht im CSS und nicht
               im Text — in sieben Sprachen wäre er sonst siebenmal zu pflegen,
               und im Französischen stünde ein schmales Leerzeichen davor. */
            span:first-child::after { content: ":"; }
            span:first-child { margin-inline-end: 0.375rem; margin-inline-start: 0; }

            b { color: var(--gold); font-weight: 500; }

            /* Das Komma der Aufzählung: gedämpft und aufrecht. Es trennt die
               Namen und gehört nicht zu ihnen — deshalb weder gold noch fett.
               Der Rückzug ist klein: 0,2 em zogen das Komma in den Buchstaben
               davor, weil das Leerzeichen im Markup schmaler ist als gedacht.
               Ein Komma, das am Wort klebt, liest sich wie ein Tippfehler. */
            em {
                margin-inline-start: -0.25ch;
                color: var(--ink);
                font-style: normal;
            }

            /* Gegenstände: weiß, gleiche Stärke. Noch trägt keine Zeile hier
               einen — mit den Zutaten aus {T0020} werden es welche. Die Regel
               steht schon, damit sie nicht als toter Code gestrichen wird. */
            b[data-art="item"] { color: var(--ink-bright); }

            i {
                display: block;
                color: var(--ink);
                font-size: 0.75rem;
            }
        }

        [data-part="stat"] {
            display: flex;
            align-items: baseline;
            gap: 0.5rem;
            margin: 0;
            color: var(--ink);

            b {
                margin-inline-start: auto;
                color: var(--ink-bright);
                font-weight: 500;
                font-variant-numeric: tabular-nums;
            }

            /* Die Zahl je Stufe, aufrecht. Hier stand bis {T0023S4} die
               Steigung als grünes »+50« daneben; jetzt ist sie eingerechnet,
               und die Wahl zeigt den Wert der Stufe. */
            b i { font-style: normal; }
        }

        /* Die Kennung als letzte Zeile der Werte ({T0039S1}): der Prefab-Name,
           den die Konsole des Spiels annimmt. Anders als alles darüber ist er
           keine Zahl, sondern eine Eingabe — feste Schrift, damit er als
           solche zu lesen ist, und markierbar, damit man ihn mit der Maus
           greifen kann. Er ist bis zu dreißig Zeichen lang und darf umbrechen,
           statt die Zeile zu sprengen. */
        /* Eine Leerzeile Abstand über der Kennung (Owner, 7. September 2026):
           sie gehört zu den Werten, ist aber keiner — der Abstand sagt das,
           ohne eine Linie zu ziehen. `lh` ist genau eine Zeilenhöhe; die
           Angabe darüber trägt den Wert für Browser, die die Einheit noch
           nicht kennen. */
        /* Der Name im Kopf des Blattes fuehrt auf die eigene Seite
           ({T0056S1}): kein eigener Ton, keine dauerhafte Linie — erst beim
           Zeigen eine Unterstreichung, und die sagt genug (Owner,
           7. September 2026). */
        [data-part="stat"][data-art="id"] {
            margin-block-start: 1.4em;
            margin-block-start: 1lh;
        }

        [data-part="stat"][data-art="id"] b {
            font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
            font-size: var(--text-small);
            font-weight: 400;
            user-select: all;
            text-align: end;
            word-break: break-word;
        }

        /* Die Versionen: Beschriftung links, die Pillen rechts — ein Raster
           mit zwei Spuren, die Reihe der Pillen darin eine echte Reihe. */
        [data-part="versions"] {
            display: grid;
            grid-template-columns: auto 1fr;
            align-items: center;
            column-gap: 0.5rem;
            margin: 0;
            /* Acht Pixel mehr Luft zu den Werten darunter (Owner, 2. September
               2026): die Pillen sind Bedienung, die Zahlen sind Auskunft, und
               ein halber Schritt trennt das eine vom anderen. Als Innenabstand
               der Zeile, nicht als Rand — die Lücke des Rasters bleibt, wie sie
               ist. */
            padding-block-end: 0.5rem;
            color: var(--ink);

            > span:first-child::after { content: ":"; }

            [data-part="pills"] {
                display: flex;
                justify-content: end;
                gap: 0.375rem;
            }
        }

        /* ---- Die Herstellung ({T0023S3}) --------------------------------- */

        /* Der Satz zur Station ist eine Zeile wie die der Herkunft und nimmt
           deren Regeln: Ort golden, Ding weiß, Rest gedämpft. Er beginnt mit
           Text und nicht mit einer Beschriftung, deshalb greift der
           Doppelpunkt der Zeilenregel hier nicht. */

        /* Die Kostenzeile: Beschriftung vorn, dahinter die Zutaten. Zwei
           Spalten, damit die Zutaten an einer Ankerlinie umbrechen — die
           zweite Zeile beginnt unter der ersten Zutat, nicht unter der
           Beschriftung (Entwurf, Skizze). */
        [data-part="cost"] {
            display: grid;
            grid-template-columns: subgrid;
            align-items: baseline;
            margin: 0;
            color: var(--ink);

            > span:first-child::after { content: ":"; }

            /* Die Beschriftung wechselt je Stufe: ein <i> je Stufe, aufrecht
               und in der Farbe der Beschriftung. Welches sichtbar ist,
               entscheidet die Stufenwahl aus {T0023S4}. */
            > span:first-child i { font-style: normal; }
        }

        /* Acht Pixel mehr Luft zwischen den Gesamtkosten und dem Update
           (Owner, 2. September 2026) — dieselbe halbe Stufe wie unter den
           Versionen: zwei Auskünfte, die man nicht als eine Liste lesen soll.
           Als Innenabstand der zweiten Zeile: entfällt sie auf der höchsten
           Stufe, entfällt die Luft mit ihr. */
        [data-part="cost"] + [data-part="cost"] { padding-block-start: 0.5rem; }

        /* Die Zutaten als umbrechende Reihe: Flex, weil es eine echte
           eindimensionale Reihe ist, und nicht verschachtelt — jede Zutat ist
           innen ein Stück Fließtext, Symbol neben Zahl und Name. */
        [data-part="parts"] {
            display: flex;
            flex-wrap: wrap;
            align-items: baseline;
            gap: 0.125rem var(--luecke);

            /* Die Ausbeute hinter den Zutaten: »ergibt 20«. Kleiner, aber in
               der leisen Stufe und nicht darunter — 0,75 rem sind kein großer
               Text, und die leise Stufe ist die letzte, die auf dieser Fläche
               AA hält (03-tokens.md). */
            > i {
                color: var(--ink);
                font-style: normal;
                font-size: 0.75rem;
            }
        }

        /* Eine Zutat: Symbol, Zahl, Name — weiß wie jeder Gegenstand im
           Blatt, in derselben Stärke wie die Orte ({K0032}, 03-tokens.md).
           Das Symbol wird vom Element so groß wie ein Icon gesetzt; hier
           steht es in der Zeile und nimmt deren Maß — dieselbe Ausnahme wie
           im Kopf und auf der Kachel, ausdrücklich am Aufrufer. */
        /* Eine Zutat ist ein Stück Fließtext: Symbol, Zahl, Name in einer
           Zeile, und die Zeile liegt auf der Grundlinie der Beschriftung.
           Kein Raster und kein Flex darin — als Raster war das Symbol das
           erste Element, sein Unterrand wurde zur Grundlinie der Zutat, und
           der Name stand einen Viertelschritt über der Beschriftung (Owner,
           2. September 2026). */
        [data-part="part"] {
            white-space: nowrap;

            /* Das Symbol im Fließtext: base.css setzt jedes Bild als Block,
               hier steht es in der Zeile. Die Größe folgt der Schrift, nicht
               dem Element — 1,25 em, also die Zeilenhöhe minus etwas Luft —,
               und sitzt mit 0,3 em unter der Grundlinie so, daß es die
               Versalhöhe des Namens einfaßt und die Zeile nicht wachsen läßt.
               Nicht pixelig: das Element rastert, weil es vergrößert; hier
               wird ein 128er auf 16 verkleinert, und nächster Nachbar macht
               daraus Krümel. Alles ausdrücklich am Aufrufer ({K0054}). */
            [data-type="game-icon"] {
                display: inline-block;
                inline-size: 1.25em;
                block-size: 1.25em;
                vertical-align: -0.3em;
                image-rendering: auto;
            }

            b {
                color: var(--ink-bright);
                font-weight: 500;
                font-variant-numeric: tabular-nums;
            }

            /* Die Menge je Stufe, aufrecht. */
            i { font-style: normal; }
        }

        /* Im Blatt nimmt der Knopf die Breite seiner Kachel; auf der eigenen
           Seite waere das die Breite der Seite, und dort steht er rechts und
           so breit wie sein Wort ({T0056S1}). Der Scope gilt seit dem Schnitt
           fuer beide Wurzeln — die Ausnahme steht deshalb hier und nicht
           dort, wo sie im selben Layer gegen diese Regel antreten muesste. */
        [data-part="planner"] {
            margin: 0.875rem 0 0;

            [data-type="button"] { inline-size: 100%; }
        }

        /* Der Haken hinter »Im Bauplan« stand hier von {T0073S8} bis
           {T0073S11}; seit {T0073S12} ist der Knopf ein Verweis ohne Zustand
           (Owner: ein Gegenstand im Planer, kein Haken). */

        :scope[data-slice="item"] [data-part="planner"] [data-type="button"] {
            inline-size: auto;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Rechtsseiten — lesbar, unauffaellig, ohne Zierde. */

@layer slices.slice_legal {
    @scope ([data-slice="legal"]) {
        :scope {
            display: grid;
            gap: var(--space-open);

            /* 68 Zeichen. Ein Rechtstext wird von oben nach unten gelesen und
               nicht ueberflogen; die Zeile darf nicht laenger sein als das Auge
               ohne Sprung zuruecktraegt. In ch, weil die Grenze eine Zahl von
               Zeichen ist und keine Laenge. */
            max-inline-size: 68ch;
            margin-inline: auto;
            padding-block: var(--space-open);
        }

        > h1 {
            font-size: var(--text-large);
            margin: 0;
        }

        section {
            display: grid;
            gap: var(--space-tight);
        }

        h2 {
            font-size: var(--text-base);
            margin: 0;
            color: var(--ink-bright);
        }

        p { margin: 0; }

        /* Der Vorspann traegt den Hinweis auf die massgebliche Sprachfassung
           und steht deshalb leiser als der Text, aber vor ihm. */
        [data-part="intro"] { color: var(--ink-quiet); }

        [data-part="updated"] {
            color: var(--ink-quiet);
            margin-block-start: var(--space-open);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Palette der Weltkarte — **die** Quelle, aus der beide Seiten
   lesen ({T0027S18}).

   Bis hierher standen diese achtzehn Farben in `weltgenerator/src/karte.rs`,
   in Rust einkompiliert. Das Blatt kannte sie nicht: eine Legende hätte sie
   ein zweites Mal schreiben müssen, und ein zweites Farbschema gäbe es nur
   mit neu gerechneten Kacheln.

   Jetzt stehen sie hier, und `tools/palette.py` erzeugt daraus
   `weltgenerator/src/palette.rs` — derselbe Weg, den `tools/ortsarten.py` für
   die Ortsarten geht: eine Quelle, ein Erzeugnis, keine zwei Wahrheiten. Wer
   eine Farbe ändert, ändert sie hier und läßt das Werkzeug laufen.

   **Warum ausgerechnet CSS und nicht eine Datentabelle.** Weil die zweite
   Seite ein Browser ist. Als Custom Properties stehen die Farben jedem
   Element zur Verfügung, ohne daß jemand sie überträgt — eine Legende, ein
   Kartenschlüssel, später das Einfärben einer Bedeutungsebene im Canvas.

   Die Namen sind die der Rust-Konstanten in Kleinschreibung; das Werkzeug
   erwartet sie so. */
:root {
    /* Wasser und Ränder */
    --karte-wasser-flach: #607a8e;
    --karte-wasser-tief: #263648;
    --karte-rand: #767c86;
    --karte-tiefsee: #181e2a;
    --karte-untiefe: #cebA84;

    /* Die Biome */
    --karte-grasland: #60873e;
    --karte-duesterwald: #305428;
    --karte-sumpf: #6a4e34;
    --karte-ebenen: #b29856;
    --karte-nebelland: #403c46;
    --karte-nebelwald: #4a3860;
    --karte-aschenlande: #642820;
    --karte-gebirge: #e2e6ea;
    --karte-tiefer-norden: #bbdfff;

    /* Was darüber liegt */
    --karte-schnee: #e2e6ea;
    /* Das Ufereis des Hohen Nordens ({T0074}) und sein Saum (Owner,
       10. September 2026). Dieselbe Farbe wie die Landflaeche des Nordens:
       Eis ist Eis, ob es traegt oder schwimmt. Der Saum sagt, wo eine Scholle
       aufhoert - ohne ihn verschwimmt sie mit dem Land dahinter. */
    --karte-eis: #bbdfff;
    --karte-eissaum: #3376b1;
    --karte-lava: #e85e1a;
    --karte-wald: #1e401e;
    --karte-heisses-meer: #b02c18;
    --karte-schatten: #1e1812;
    --karte-welle: #483422;
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Kartenband — Leiste und Fenster; das Fenster nimmt, was die
   Seite an Hoehe laesst, und zeigt die ganze Scheibe. */

@layer slices.slice_map {
    @scope ([data-slice="map"]) {
        :scope {
            grid-template-rows: auto auto minmax(0, 1fr);
            row-gap: var(--space-close);
            padding-block: var(--space-close) 0;
            min-block-size: 0;
        }

        /* Der Titel in Weiss, nicht in der Tintenfarbe der anderen
           Ueberschriften: er nennt den Seed, und der soll leuchten wie der
           Spielname auf der Buehne (Owner, 3. September 2026). */
        [data-type="heading"] {
            color: oklch(99% 0.01 90);
        }

        /* Das Fenster endet am Rand des Inhaltsbands wie jedes andere
           Inhaltselement (Owner, 3. September 2026). Es ist der Kartenraum:
           was darin liegt, wird beschnitten, und die Kachelebene bewegt sich
           darin ({T0026S9}).

           Es ist zugleich ein Raster mit genau einer Zelle ({T0028S1}): der
           Griff fuer die grosse Ansicht legt sich mit place-self hinein statt
           mit einem Versatz an die Ecke. Die Ebenen darin bleiben absolut
           gesetzt — sie tragen die Matrix des Skripts und gehoeren in keine
           Zelle. */
        [data-type="map-window"] {
            display: grid;
            grid-template: minmax(0, 1fr) / minmax(0, 1fr);
            position: relative;
            min-block-size: 0;
            overflow: hidden;
            background: var(--ground);
            cursor: grab;
            touch-action: none;
            overscroll-behavior: contain;
        }

        /* Das Fenster ist fokussierbar (Pfeile, Plus, Minus): der Fokusring
           innen, damit ihn overflow hidden nicht abschneidet. */
        [data-type="map-window"]:focus-visible {
            outline: 2px solid var(--link);
            outline-offset: -2px;
        }

        /* Die Kachelebene: ein Punkt ohne eigene Groesse, den das Skript per
           transform verschiebt; die Kacheln haengen mit festen Pixelmassen
           daran. Die Marker liegen in derselben Ebene und damit im selben
           Koordinatenraum — dieselbe Matrix bewegt Karte und Ort, also bleibt
           ein Ort auf seinem Punkt, bei jedem Zoom (K5). */
        [data-type="map-tiles"],
        [data-type="map-markers"] {
            position: absolute;
            inset-block-start: 0;
            inset-inline-start: 0;
            transform-origin: 0 0;
            will-change: transform;
        }

        /* Die Markerebene rechnet in Bildschirmpunkten, nicht im Kartenraum:
           so behaelt ein Marker seine Groesse, waehrend die Karte zoomt, und
           sitzt trotzdem auf seinem Punkt. Beim Schwenken verschiebt das
           Skript die Ebene; beim Zoomen setzt es hier Massstab und
           Bezugspunkt ({T0145}), und jeder Marker rechnet seine Lage daraus -
           drei Zahlen statt tausend Transforms je Zoomschritt. */
        [data-type="map-markers"] {
            inset: 0;
            transform: none;
            z-index: 2;
            pointer-events: none;
            --mass: 0;
            --bezug-x: 0;
            --bezug-y: 0;
        }

        [data-type="map-marker"] {
            position: absolute;
            /* **Die Groesse steht als Zahl, nicht als Laenge** ({T0036S12}):
               eine Groessenregel setzt nur noch --zeichen, und Breite, Hoehe
               und die zentrierenden Raender folgen daraus.

               **Und der Zuwachs ist eine zweite Zahl** ({T0045S1}): unter der
               Maus steht --zuwachs auf 4, die Box waechst um vier Punkte, die
               Raender um zwei je Seite, und das Zentrum bleibt auf dem Punkt.
               Nicht ueber scale: die Eigenschaft wirkt vor transform, der
               Marker sitzt aber per transform: translate auf seinem Ort, und
               so wurde die Verschiebung mitskaliert - das Zeichen wanderte,
               statt zu wachsen. Der Owner sah es im ersten Versuch. */
            --zeichen: 28;
            --zuwachs: 0;
            /* **Und der Feldzuwachs ist die dritte Zahl** ({T0144}, Owner am
               21. September 2026: »8px groesser als bei einer einzelnode«):
               der Marker eines Rohstofffelds steht auf --feld 8 und ist damit
               acht Punkte groesser als der Einzelknoten seiner Art, welche
               Groesse die auch hat - keine Zahl je Art, eine Regel. */
            --feld: 0;
            inline-size: calc((var(--zeichen) + var(--zuwachs) + var(--feld)) * 1px);
            block-size: calc((var(--zeichen) + var(--zuwachs) + var(--feld)) * 1px);
            margin-block-start: calc((var(--zeichen) + var(--zuwachs) + var(--feld)) * -0.5px);
            margin-inline-start: calc((var(--zeichen) + var(--zuwachs) + var(--feld)) * -0.5px);
            /* **Die Lage rechnet das Stylesheet** ({T0145}): --ort-x und
               --ort-y sind der Ort als Bruchteil der Welt, einmal je Marker
               gesetzt; Massstab und Bezugspunkt kommen von der Ebene. Es ist
               dieselbe Formel wie zuSchirm im Skript, Bezug + Ort * Massstab,
               nur dass ein Zoomschritt jetzt drei Zahlen an der Ebene aendert
               und nicht tausend Transforms an den Markern. */
            --ort-x: 0;
            --ort-y: 0;
            transform: translate(
                calc((var(--bezug-x) + var(--ort-x) * var(--mass)) * 1px),
                calc((var(--bezug-y) + var(--ort-y) * var(--mass)) * 1px)
            );
            /* Die Ordnung der Ebenen ({T0045S1}, Owner am 6. September 2026):
               ganz oben der Spawn, darunter die Bosse, darunter die Haendler,
               darunter alles andere - und ein Marker unter der Maus ueber
               allem. Die Werte stehen bei den Ebenen; hier der Boden. */
            z-index: 1;
            pointer-events: auto;
            /* Jeder Marker nimmt die Maus an, auch Boss und Spawn ({T0045S1}) -
               vorher setzte das jede Ebene fuer sich, und diese zwei fehlten.
               Der Container darueber steht auf none, damit die Karte zwischen
               den Markern ziehbar bleibt. */
            background-size: contain;
            background-repeat: no-repeat;
            filter: drop-shadow(0 1px 2px oklch(12% 0.01 260 / 0.7));
        }

        /* Das Schild ueber dem Zeichen ({T0026S38}): weiss, mit dunklem Saum,
           damit es auf hellem wie auf dunklem Grund steht. Es haengt am
           Marker und behaelt deshalb wie dieser seine Bildschirmgroesse -
           vierzehn Punkte, egal wie weit die Karte gezoomt ist.

           Die Groesse steht in px und nicht in einer Stufe der Skala: der
           Owner hat sie so bestellt, und ein Kartenschild folgt der Karte,
           nicht dem Fliesstext der Seite. */
        [data-type="map-marker-name"] {
            position: absolute;
            inset-block-end: 100%;
            inset-inline-start: 50%;
            transform: translateX(-50%);
            margin-block-end: 0.2em;
            font-size: 14px;
            font-weight: 700;
            line-height: 1.1;
            white-space: nowrap;
            color: oklch(99% 0 0);
            text-shadow:
                0 1px 2px oklch(12% 0.01 260 / 0.9),
                0 0 3px oklch(12% 0.01 260 / 0.8);
        }

        /* Die Ebenen: was nicht eingeschaltet ist, ist nicht da ({T0027S1}).
           Die Liste der eingeschalteten steht am Markerlayer; ein Marker ohne
           Ebene - der Spawn - bleibt immer stehen. */
        [data-type="map-markers"]:not([data-ebenen~="bosse"]) [data-ebene="bosse"] {
            display: none;
        }

        /* Die zweite Achse: *wo* ({T0093}). Die Ebenen darüber sagen, was für
           eine Art Ort gezeigt wird; diese neun sagen, in welchem Biom. Beide
           gelten zusammen, und das UND entsteht dabei von selbst — wer in
           einer der beiden Reihen ausgeschaltet ist, trifft auf ein `display:
           none` und ist weg. Keine Zeile Skript entscheidet das mit, und die
           Filterlogik in karte.js bleibt unangetastet (AW13).

           **Ein Marker ohne `data-biom` fällt durch keine dieser Regeln.**
           Das ist die Sicherung, kein Nebeneffekt: das Biom kommt aus der
           Bedeutungsebene, die erst geholt wird, wenn jemand sie braucht.
           Solange sie fehlt, filtert die obere Reihe nicht — statt eine leere
           Karte zu zeigen.

           **Acht und nicht neun: der Ozean hat keinen Schalter** (Owner,
           11. September 2026). Was auf dem Meer liegt, wird immer gezeigt —
           wie der Spawn, der zu keiner Ebene gehört. Deshalb steht hier keine
           `ocean`-Regel: sie würde jeden Ozeanort verstecken, weil `ocean`
           dann in keiner Auswahl je vorkäme. Eine fehlende Regel ist hier die
           Regel, und das ist der Grund, warum es dasteht.

           `region_text` führt neun Biome und `bedeutung.rs` kennt sie unter
           0 bis 8; was daneben liegt, ist `Biome::None` und kein Ort der Welt.

           Der Vergleich mit der Tilde weicht von AW8 ab, und zwar
           absichtlich: er steht achtmal darüber für die Ebenen. Eine zweite
           Schreibweise für dieselbe Sache wäre der teurere Fehler.

           **`data-biome` ist hier eine Liste, in slice_items ist es eines.**
           Derselbe Name, zwei Bedeutungen — auf der Gegenstandsseite sagt er,
           zu welchem Biom ein Block gehört, hier, welche eingeschaltet sind.
           Die Wahl folgt dem Nachbarn: `data-ebenen` und `data-ebene` stehen
           genauso nebeneinander, und ein drittes Schema für dieselbe Sache
           wäre schlimmer als die Nähe. */
        [data-type="map-markers"]:not([data-biome~="meadows"])     [data-biom="meadows"],
        [data-type="map-markers"]:not([data-biome~="swamp"])       [data-biom="swamp"],
        [data-type="map-markers"]:not([data-biome~="mountain"])    [data-biom="mountain"],
        [data-type="map-markers"]:not([data-biome~="blackforest"]) [data-biom="blackforest"],
        [data-type="map-markers"]:not([data-biome~="plains"])      [data-biom="plains"],
        [data-type="map-markers"]:not([data-biome~="ashlands"])    [data-biom="ashlands"],
        [data-type="map-markers"]:not([data-biome~="deepnorth"])   [data-biom="deepnorth"],
        [data-type="map-markers"]:not([data-biome~="mistlands"])   [data-biom="mistlands"] {
            display: none;
        }

        /* Die Bosse tragen ihr eigenes Zeichen und ein rotes Schild - so
           unterscheidet man sie im Vorbeisehen vom Spawn. */
        /* Die Bosse stehen ein Drittel groesser als der Spawn: sie sind die
           Punkte, auf die man zuerst sieht, und ihr Zeichen traegt mehr
           Binnenzeichnung ({T0027S3}). Die Raender ziehen mit, damit das
           Zeichen auf seinem Punkt bleibt und nicht daneben. */
        [data-type="map-marker"][data-ebene="bosse"] {
            background-image: url("/icons/map-boss.webp");
            --zeichen: 37;
        }

        [data-type="map-marker"][data-ebene="bosse"] [data-type="map-marker-name"] {
            color: oklch(72% 0.19 25);
        }

        /* Die Bosse liegen ueber den Haendlern und unter dem Spawn
           ({T0045S1}, Owner am 6. September 2026). Das ersetzt die Ordnung
           vom 4. September, nach der der Boss-Layer immer oben lag und auch
           ein beruehrter Marker darunter blieb: jetzt hebt die Maus jeden
           Marker ueber alles, damit man sieht, was man beruehrt.

           Ohne die Regel entschiede die Reihenfolge im Dokument, und die
           kommt aus der Ortsdatei: die Bosse stehen dort vorn, weil das Spiel
           sie zuerst platziert - und laegen damit unter jedem Grab, das
           spaeter kommt. */
        [data-type="map-marker"][data-ebene="bosse"] {
            z-index: 3;
        }

        [data-type="map-markers"]:not([data-ebenen~="dungeons"]) [data-ebene="dungeons"],
        [data-type="map-markers"]:not([data-ebenen~="hoehlen"]) [data-ebene="hoehlen"] {
            display: none;
        }

        /* Die Dungeons stehen zu Hunderten auf der Karte. Ihr Zeichen ist
           deshalb kleiner als das der Bosse, und ihr Name erscheint erst,
           wenn die Maus darauf steht - stehende Schilder waeren bei tausend
           Punkten eine Wand aus Text (Owner, 4. September 2026)
           ({T0027S6}). */
        [data-type="map-marker"][data-ebene="dungeons"],
        [data-type="map-marker"][data-ebene="hoehlen"] {
            background-image: url("/icons/map-dungeon.webp");
            --zeichen: 22;
        }

        /* Die Gewundenen Tunnel im Mass der uebrigen Dungeons - man geht
           hinein, also traegt das Zeichen so viel Gewicht wie eine Krypta. */
        /* Die Gipfel ({T0112S3}, Owner am 15. September 2026, nach dem
           Vorbild einer Wanderkarte): ein kleines schwarzes Dreieck auf der
           Spitze, darunter die Hoehe in Metern, kein Name. Das Dreieck ist
           eine Form, kein Bild - ein schwarzer Kasten mit clip-path auf dem
           ::before, damit das Schild daneben nicht mit abgeschnitten wird.
           Klein, weil es je Welt Dutzende sind und die Zahl das Wort fuehrt. */
        [data-type="map-marker"][data-kind="gipfel"] {
            --zeichen: 12;
            background-image: none;
        }

        [data-type="map-marker"][data-kind="gipfel"]::before {
            content: "";
            position: absolute;
            inset: 0;
            background-color: oklch(12% 0.01 260);
            clip-path: polygon(50% 0, 100% 100%, 0 100%);
        }

        /* Die Hoehe steht dauerhaft unter dem Dreieck ({T0121}, Owner am
           15. September 2026: »das label immer angezeigt - also ohne hover«).
           T0112 S5 hatte sie unter die Maus gelegt; zurueckgenommen. */
        [data-type="map-marker"][data-kind="gipfel"] [data-type="map-marker-name"] {
            inset-block-end: auto;
            inset-block-start: 100%;
            margin-block-end: 0;
            margin-block-start: 0.15em;
            font-size: 11px;
            font-weight: 600;
        }

        [data-type="map-marker"][data-kind="tunnel"] {
            background-image: url("/icons/map-tunnel.webp");
            /* 36: erst 30, dann 40, dann vier Punkte zurueck (Owner,
               9. September 2026). Dazu ein neues Zeichen, der gluehende
               Schlund statt des steinernen Tors. */
            --zeichen: 36;
        }

        /* Zwei Sorten Eingang, und die Karte sagt seit {T0036S3}, welche:
           die gruen vergitterte Krypta des Sumpfes (`krypta`, im Spiel
           SunkenCrypt) und die Grabkammer des Duesterwalds (`grab`, die drei
           Crypts). Vorher trugen beide das Zeichen ihrer Ebene und sahen
           gleich aus - der Unterschied zwischen einem Gang in den Sumpf und
           einem in den Wald stand nirgends. Die Minen der Nebellande bleiben
           beim Ebenenzeichen. */
        [data-type="map-marker"][data-kind="krypta"] {
            background-image: url("/icons/map-crypt.webp");
        }

        [data-type="map-marker"][data-kind="grab"] {
            background-image: url("/icons/map-grave.webp");
        }

        /* Die dritte Art der Ebene und die letzte ohne eigenes Zeichen: die
           Zwergenminen der Nebellande ({T0036S5}). Damit sagt die Ebene, was
           sie zeigt - gruenes Tor im Sumpf, grauer Dolmen im Wald, violettes
           Tor im Nebelland. */
        [data-type="map-marker"][data-kind="mine"] {
            background-image: url("/icons/map-mine.webp");
        }

        /* `display: none` und nicht `opacity: 0` ({T0027S10}): ein
           durchsichtiges Schild wird trotzdem gesetzt und gemessen, und bei
           viertausend Markern ist dieser Textsatz teurer als alles andere an
           der Ebene. Der Preis ist der Übergang - ein Schild, das erscheint,
           statt eines, das aufblendet. */
        [data-type="map-marker"][data-ebene="dungeons"] [data-type="map-marker-name"],
        [data-type="map-marker"][data-ebene="hoehlen"] [data-type="map-marker-name"] {
            display: none;
        }

        [data-type="map-marker"][data-ebene="dungeons"]:hover [data-type="map-marker-name"],
        [data-type="map-marker"][data-ebene="hoehlen"]:hover [data-type="map-marker-name"] {
            display: block;
        }


        /* Die Hoehlen tragen ihr eigenes Zeichen ({T0027S7}); alles andere -
           Groesse, Hover, Ausblenden - teilen sie mit den Dungeons. */
        [data-type="map-marker"][data-ebene="hoehlen"] {
            background-image: url("/icons/map-cavern.webp");
        }

        /* **Nach** der Ebenenregel und nicht davor ({T0036S6}): beide Selektoren
           wiegen gleich schwer - ein Typ- und ein Merkmalsattribut -, und bei
           gleichem Gewicht gewinnt der spaetere. Die erste Fassung stand
           vorher im Blatt, und die Trollhoehlen trugen weiter das Zeichen der
           Ebene. Am lebenden Objekt gesehen, nicht im Blatt gelesen ({K0145}).

           Zwei Arten, ein Zeichen: eine Hoehle im Fels sieht aus wie eine
           Hoehle im Fels, ob ein Troll darin haust oder ein Morgen. Die
           Eishoehle des Gebirges behaelt das Ebenenzeichen - ein Eingang im
           Schnee ist etwas anderes als einer im Fels. */
        [data-type="map-marker"][data-kind="trollhoehle"],
        [data-type="map-marker"][data-kind="morgenhoehle"] {
            background-image: url("/icons/map-cave.webp");
        }

        [data-type="map-markers"]:not([data-ebenen~="haendler"]) [data-ebene="haendler"] {
            display: none;
        }

        /* Die Haendler sind drei Personen und nicht eine Sorte: Haldor, Hildir
           und die Sumpfhexe tragen deshalb je ihr eigenes Zeichen - die erste
           Ebene, in der das Bild nicht an der Ebene haengt, sondern an der Art
           ({T0027S9}). Ihr Name erscheint beim Zeigen, wie bei Dungeons und
           Hoehlen: dreissig Punkte sind zu viele fuer stehende Schilder und zu
           wenige, um sie stumm zu lassen.

           Sie stehen bei zweiunddreissig Punkten (Owner, 5. September 2026,
           vorher neunzehn): mit zehn Haldor, zehn Hildir und einer Handvoll
           Hexen je Welt sind sie die seltensten Marken der Karte und decken
           niemanden zu - bei neunzehn gingen sie zwischen den Tuermen unter. */
        [data-type="map-marker"][data-ebene="haendler"] {
            --zeichen: 32;
            z-index: 2;
        }

        /* Hildirs Questorte ({T0044S1}): Hoehle, Krypta und Festung, je drei.
           Sie stehen bei den Haendlern, weil sie zu Hildir gehoeren. */
        [data-type="map-marker"][data-kind="quest"] {
            background-image: url("/icons/map-quest.webp");
        }

        /* Dyrnwyns Questorte ({T0046S1}): die drei Orte des Mysteriums, an
           denen Klinge, Heft und Spitze des Schwerts liegen. Ein Auftrag wie
           Hildirs, darum in dieser Ebene. */
        [data-type="map-marker"][data-kind="dyrnwyn"] {
            background-image: url("/icons/map-dyrnwyn.webp");
        }

        [data-type="map-marker"][data-kind="haldor"] {
            background-image: url("/icons/map-haldor.webp");
        }

        [data-type="map-marker"][data-kind="hildir"] {
            background-image: url("/icons/map-hildir.webp");
        }

        [data-type="map-marker"][data-kind="hexe"] {
            background-image: url("/icons/map-witch.webp");
        }

        [data-type="map-marker"][data-ebene="haendler"] [data-type="map-marker-name"] {
            display: none;
        }

        [data-type="map-marker"][data-ebene="haendler"]:hover [data-type="map-marker-name"] {
            display: block;
        }


        /* Die Filterleiste traegt zwei Reihen (Owner, 4. September 2026:
           »schiebe die Optionen, die wir haben, in die obere Reihe - es passen
           ja zwei Reihen untereinander - und mache als erstes unten rechts den
           Switch fuer Rohstoffe«). Fuenf Spalten, so viele wie die erste Reihe
           Schalter hat; der sechste bekommt die letzte Spalte der zweiten
           Reihe. Das steht hier und nicht im Streifen selbst: der ist ein
           Widget und gehoert auch der Itemseite ({K0095}). */
        @container (min-width: 35rem) {
            [data-type="control-bar"] [data-part="gruppe"] {
                display: grid;
                grid-template-columns: repeat(5, auto);
                column-gap: 1.5rem;
                row-gap: 0.5rem;
            }

            /* Der sechste Schalter stand als einziger der zweiten Reihe
               rechts unter dem fuenften (Owner, 4. September 2026: »unten
               rechts«). Mit dem siebten faengt die Reihe an zu wachsen, und
               eine feste Spalte je Schalter waere ein Eintrag mehr bei jedem
               weiteren Layer. Die Reihe laeuft deshalb von links wie die
               erste ({T0027S13}). */
        }

        /* Was die Orbitalansicht zurueckhaelt, sagt sie auch ({T0052}, Owner,
           7. September 2026: »es wirkt doch eher wie ein fehler wenn sie an
           und aus geschaltet werden, aber nichts passiert«). Das nimmt die
           Entscheidung von {T0051} zurueck, nichts zu schreiben und nichts zu
           dimmen - aus dem Grund, den erst die Bedienung liefert: ein Regler,
           der klickt und nichts bewirkt, sieht aus wie ein Defekt.

           Zwei Zeichen. Die goldene Zeile steht unter den Reglern - dort,
           wo man nach dem Klick hinsieht - und sie steht **immer**, nicht nur
           im Orbit (Owner, 7. September 2026: »der Hinweistext bleibt immer
           stehen, sonst fluppt es ja hin und her«): sie nimmt eine eigene
           Zeile des Streifens, und ein- und auszublenden hiesse, alles um sie
           herum bei jedem Schwellenwechsel springen zu lassen. Der Satz gilt
           ohnehin dauernd - er nennt eine Regel der Karte, keinen Zustand.
           Was wechselt, ist die Deckung der Regler, die im Orbit nichts
           bewirken: halbtransparent, und das verschiebt nichts.

           Karte.js setzt data-orbit am Streifen und disabled an den Reglern;
           ihr gespeicherter Zustand bleibt unberuehrt - checked ist
           unabhaengig von disabled, und im Sinkflug stehen sie wieder voll
           deckend da, wie sie der Nutzer gelassen hat. */
        [data-type="control-bar"] [data-part="options-note"] {
            color: var(--gold);
            font-size: var(--text-small);
        }

        [data-type="control-bar"] [data-type="checkbox"]:has(input:disabled) {
            opacity: 0.5;
        }

        /* Die Schaetze ({T0027S14}): jeder Ort, der im Spiel eine Truhe
           traegt - aus den Location-Prefabs erhoben. Das Zeichen steht auf dem
           Ort, nicht auf der Truhe: wo genau sie darin steht, haengt an der
           Drehung, mit der das Spiel die Location setzt. */
        [data-type="map-markers"]:not([data-ebenen~="schaetze"]) [data-ebene="schaetze"] {
            display: none;
        }

        [data-type="map-marker"][data-ebene="schaetze"] {
            --zeichen: 15;
        }

        /* Die eingefrorenen Schiffe des Nordens ({T0074}). Groesser als der
           Eisteich: ein Schiff im Eis ist eine Marke, die man ansteuert, ein
           Teich eine, an der man vorbeikommt. */
        [data-type="map-marker"][data-kind="frostschiff"] {
            background-image: url("/icons/map-frostschiff.webp");
            --zeichen: 34;
        }

        /* Die Schiffswracks ({T0044S1}): hundert je Welt an Kuesten und in
           Untiefen. Sie standen als Truhe wie alles in dieser Ebene. */
        [data-type="map-marker"][data-kind="wrack"] {
            background-image: url("/icons/map-wrack.webp");
        }

        /* Die Brunnen mit Schatz ({T0044S1}): am Grund wartet eine Kiste -
           fuenfzig je Welt, im Gebirge und im Sumpf. */
        [data-type="map-marker"][data-kind="brunnen"] {
            background-image: url("/icons/map-brunnen.webp");
        }

        /* Die Runensteine ({T0050}): 695 je Welt in allen Biomen, von den
           Betatestern als vermisst gemeldet. Ebenengroesse, weil haeufig. */
        [data-type="map-marker"][data-kind="runenstein"] {
            background-image: url("/icons/map-runenstein.webp");
        }

        [data-type="map-marker"][data-kind="truhe"] {
            background-image: url("/icons/map-chest.webp");
        }

        /* Die Axtkopf-Truhe ({T0127}): dieselbe Truhe mit lila Schein, an den
           zwei Wiesenhaeusern, deren Truhe einen Kopf der fruehen Axt
           wuerfeln kann. */
        [data-type="map-marker"][data-kind="axttruhe"] {
            background-image: url("/icons/map-axttruhe.webp");
            /* In Maibaumgroesse ({T0128}): die Ebene zeichnet mit 15, und bei
               15 px ist der Schein ein Saum von zwei Pixeln. Mit 26 traegt er,
               gemessen im Sichtvergleich 15 / 26 / 30 auf Wiesengrund. */
            --zeichen: 26;
        }

        [data-type="map-marker"][data-ebene="schaetze"] [data-type="map-marker-name"] {
            display: none;
        }

        [data-type="map-marker"][data-ebene="schaetze"]:hover [data-type="map-marker-name"] {
            display: block;
        }

        /* Die Spawns ({T0027S13}): wo das Spiel Gegner nachliefert. Bisher
           das Drachennest des Gebirges; der Owner fuellt die Ebene weiter.
           Groesse und Verhalten wie die Rohstoffe. */
        [data-type="map-markers"]:not([data-ebenen~="spawns"]) [data-ebene="spawns"] {
            display: none;
        }

        [data-type="map-marker"][data-ebene="spawns"] {
            --zeichen: 15;
        }

        /* Der Eisteich - eine Landmarke ohne Beute, darum kleiner als alles,
           was man betritt. */
        [data-type="map-marker"][data-kind="eisteich"] {
            background-image: url("/icons/map-eisteich.webp");
            /* 28: von 24 auf 32 und wieder zurueck auf 28 (Owner,
               9. September 2026). */
            --zeichen: 28;
        }

        /* Das Monument of Torment der Aschenlande ({T0036S14}): der Stein, aus
           dem die verbrannten Skelette kommen - 350 je Welt und damit die
           haeufigste Art dieser Ebene. */
        [data-type="map-marker"][data-kind="monument"] {
            background-image: url("/icons/map-monument.webp");
        }

        /* Die Nester der Graulinge ({T0036S13}): die zweite Art in der Ebene
           der Spawner, und die einzige des Duesterwalds. */
        [data-type="map-marker"][data-kind="grauzwerg"] {
            background-image: url("/icons/map-greydwarf.webp");
        }

        [data-type="map-marker"][data-kind="nest"] {
            background-image: url("/icons/map-nest.webp");
        }

        /* Die Geiernester der Aschenlande ({T0040S1}): 350 je Welt, so haeufig
           wie das Monument nebenan, und die vierte Art dieser Ebene. Sie liegt
           flach am Boden, haengt also richtig mit ihrer Mitte am Punkt. */
        [data-type="map-marker"][data-kind="geiernest"] {
            background-image: url("/icons/map-geiernest.webp");
        }

        /* Die Draugr-Gräber des Sumpfes ({T0042S1}): dieselben 200 Orte, die in
           der Ebene der Schätze als Truhe stehen — hier als das, was aus ihnen
           herauskommt. Ein Ort mit zwei Schlüsseln steht in zwei Ebenen. */
        [data-type="map-marker"][data-kind="draugr"] {
            background-image: url("/icons/map-draugr.webp");
            --zeichen: 16;
        }

        [data-type="map-marker"][data-ebene="spawns"] [data-type="map-marker-name"] {
            display: none;
        }

        [data-type="map-marker"][data-ebene="spawns"]:hover [data-type="map-marker-name"] {
            display: block;
        }

        [data-type="map-markers"]:not([data-ebenen~="rohstoffe"]) [data-ebene="rohstoffe"] {
            display: none;
        }

        /* Die Rohstoffe stehen so gross wie die Siedlungen - ein Drittel
           kleiner als die Dungeons (Owner, 4. September 2026: »die Icons der
           Ressourcen alle ca 1/3 kleiner«). Es sind viele, und sie sollen die
           Karte nicht zudecken. */
        [data-type="map-marker"][data-ebene="rohstoffe"] {
            --zeichen: 15;
        }

        /* Der versteinerte Frosttroll: ein Rohstoffort im Mass der Teergrube. */
        [data-type="map-marker"][data-kind="frosttroll"] {
            background-image: url("/icons/map-frosttroll.webp");
            /* 36: von 27 ueber 36, 48 und 40 hierher (Owner, 9. und
               10. September 2026). Der versteinerte Troll ist ein
               Rohstoffort, den man ansteuert. */
            --zeichen: 36;
        }

        [data-type="map-marker"][data-kind="teer"] {
            background-image: url("/icons/map-tar.webp");
        }

        [data-type="map-marker"][data-kind="flametal"] {
            background-image: url("/icons/map-flametal.webp");
        }

        /* Die Nebellande geben zwei Rohstoffe her, die man von weitem sieht:
           die Riesenskelette, aus denen der schwarze Marmor kommt, und die
           Riesenschwerter ({T0027S11} QS). */
        [data-type="map-marker"][data-kind="skelett"] {
            background-image: url("/icons/map-skelleton.webp");
        }

        [data-type="map-marker"][data-kind="schwert"] {
            background-image: url("/icons/map-sword.webp");
        }

        /* Die Surtlinge sitzen an den Feuerloechern des Sumpfes, die Bienen in
           elf der dreizehn Holzhaeuser - beide im Prefab nachgesehen
           ({T0027S11} QS). Die Haeuser stehen damit in zwei Ebenen. */
        [data-type="map-marker"][data-kind="surtling"] {
            background-image: url("/icons/map-surtling.webp");
        }

        [data-type="map-marker"][data-kind="bienen"] {
            background-image: url("/icons/map-bee.webp");
        }

        /* Die Vegetation ({T0027S12}): Erze, Triebe, Ruestungen. Sie stehen
           erst ab der mittleren Stufe und nur im Fenster - achtzigtausend
           Punkte sind kein Marker-Teppich, sondern ein Ausschnitt. Silber
           seit {T0144}; Obsidian ist gerechnet, wartet aber noch auf sein
           Zeichen. */
        /* Die Vegetationszeichen tragen keinen Schlagschatten: sie stehen zu
           Tausenden im Bild, und jeder Schatten ist eine eigene Rasterung.
           Auf der feinsten Stufe kostete das gemessen ein Drittel der Zeit je
           Bild ({T0027S12}). */
        [data-type="map-marker"][data-kind="kupfer"],
        [data-type="map-marker"][data-kind="zinn"],
        [data-type="map-marker"][data-kind="silber"],
        [data-type="map-marker"][data-kind="ruestung"] {
            filter: none;
        }

        /* Die beiden Erze des Duesterwalds stehen ein Drittel kleiner als die
           uebrigen Rohstoffe - 10 statt 15 Punkte (Owner, 4. September 2026).
           Sie sind die dichtesten der Ebene: Zinn allein zehntausendmal. */
        [data-type="map-marker"][data-kind="kupfer"],
        [data-type="map-marker"][data-kind="zinn"] {
            --zeichen: 10;
        }

        /* Der Guck ({T0047S3}): am befallenen Baum des Sumpfes, der ihn in
           seinem Prefab traegt - 700 je Welt, ein Ort, keine Faltung. Er
           steht in der Groesse seiner Ebene. */
        [data-type="map-marker"][data-kind="guck"] {
            background-image: url("/icons/map-guck.webp");
        }

        [data-type="map-marker"][data-kind="kupfer"] {
            background-image: url("/icons/map-copper.webp");
        }

        [data-type="map-marker"][data-kind="zinn"] {
            background-image: url("/icons/map-tin.webp");
        }
        /* Silber ({T0144}): eine Ader je Gebirgszone, Hunderte je Welt, nie
           gefaltet - in der Groesse seiner Ebene, 15, nicht in der der beiden
           dichten Erze. Entschieden am Bildschirm auf Gebirgsgrund. */
        [data-type="map-marker"][data-kind="silber"] {
            background-image: url("/icons/map-silver.webp");
        }
        /* Die Rohstofffelder ({T0144}, Owner am 21. September 2026: »wie aus
           atlanten, wo rohstoffe im boden als flaechiger bereich dargestellt
           werden«): eine SVG-Ebene ueber den Kacheln, viewBox in Weltmetern,
           faehrt mit der Kachelebene mit. Sie zeigt sich mit dem Schalter der
           Rohstoffe; ohne ihn ist sie weg wie die Marker der Ebene. Die Farbe
           ist die des Rohstoffs, nicht eine der drei Rollenfarben der Seite -
           das Blatt hat seine eigene Palette ({K0234}). Die Strichstaerke
           bleibt beim Zoomen gleich (vector-effect), die Flaeche ist
           durchscheinend, damit das Gelaende darunter lesbar bleibt. */
        [data-type="map-felder"] {
            position: absolute;
            inset: 0;
            inline-size: 100%;
            block-size: 100%;
            /* Ueber allen Kachelstufen: das Skript gibt jeder Stufe ihre
               Nummer als z-index (0, 1, 2); mit 1 lag die Ebene unter den
               Kacheln der Stufen 1 und 2 - gesehen am 21. September 2026,
               605 Pfade im DOM und nichts im Bild. */
            z-index: 10;
            pointer-events: none;
            overflow: visible;
        }
        [data-type="map-felder"]:not([data-ebenen~="rohstoffe"]),
        [data-type="map-felder"][hidden] {
            display: none;
        }
        /* Die Flaeche nimmt den Zeiger an - die Ebene selbst nicht, damit die
           Karte zwischen den Feldern ziehbar bleibt -, und unter dem Zeiger
           tritt sie hervor; ihr Text (»Zinnerz · 37«) steht dann in der
           Auskunft unter dem Zeiger. Ein Marker in der Feldmitte ist weg
           (Owner, 21. September 2026: »bei zinn keine zinn ore mehr
           abbilden, dafuer wenn man ueber den bereich hovert den text«). */
        [data-type="map-felder"] path {
            --feld: oklch(70% 0.05 250);
            /* Ein Feld ist eine Sammlung von Ringen - Inseln und Loecher -,
               evenodd zeichnet ein Loch als Loch. */
            fill-rule: evenodd;
            fill: color-mix(in oklch, var(--feld) 28%, transparent);
            stroke: var(--feld);
            stroke-width: 1.5;
            stroke-dasharray: 5 3;
            vector-effect: non-scaling-stroke;
            pointer-events: visiblePainted;
        }
        /* Das Hervortreten haengt an data-zeigt, das das Skript an genau
           einen Pfad setzt - nicht an :hover ({T0145}): mit path:hover war
           jede Zeigerbewegung eine Stilfrage ueber alle 5 755 Pfade. */
        [data-type="map-felder"] path[data-zeigt] {
            fill: color-mix(in oklch, var(--feld) 55%, transparent);
            stroke-dasharray: none;
        }
        /* Auf Stufe 1 nur die Cluster ({T0145}): eine Einzelzone von 24 m ist
           dort zwei Pixel, und vier Fuenftel der Pfade sind Einzelzonen -
           beim Zoomen rastert der Browser sie sonst umsonst. */
        [data-type="map-felder"][data-stufe="1"] path[data-einzel] {
            display: none;
        }
        /* Der Marker eines Felds ({T0144}, Owner am 21. September 2026: »in
           clusterbereichen doch die ore wieder anzeigen, aber 8px groesser
           als bei einer einzelnode« - und dasselbe fuer alles, was die
           Rohstoffebene ein- und ausblendet): das Zeichen der Art, acht
           Punkte groesser als ihr Einzelknoten ueber --feld in der Grundregel
           (Zinn 10 → 18, Guck 15 → 23, Leviathan 22 → 30), das Schild mit
           der Zahl beim Zeigen auf den Marker oder auf die Flaeche (das
           Skript setzt dann data-zeigt). */
        [data-type="map-marker"][data-feld] {
            --feld: 8;
        }
        [data-type="map-marker"][data-feld][data-zeigt] [data-type="map-marker-name"] {
            display: block;
        }
        /* Die Farbe eines Felds ist die dominante Farbe seines Zeichens
           (Owner, 21. September 2026) - gerechnet von tools/feldfarben.py aus
           dem Archivbild der Art, nach jedem Tausch eines Zeichens neu, wie
           der Schein. Was hier steht, ist erzeugt; die Vorgabe darueber
           greift fuer eine Art ohne Zeichen. */
        /* FELDFARBEN ANFANG - erzeugt von tools/feldfarben.py, nicht von Hand */
        [data-type="map-felder"] path[data-kind="teer"] {
            --feld: oklch(37% 0.005 301);
        }
        [data-type="map-felder"] path[data-kind="flametal"] {
            --feld: oklch(42% 0.086 25);
        }
        [data-type="map-felder"] path[data-kind="frosttroll"] {
            --feld: oklch(40% 0.070 254);
        }
        [data-type="map-felder"] path[data-kind="skelett"] {
            --feld: oklch(62% 0.008 315);
        }
        [data-type="map-felder"] path[data-kind="schwert"] {
            --feld: oklch(50% 0.077 66);
        }
        [data-type="map-felder"] path[data-kind="surtling"] {
            --feld: oklch(67% 0.266 317);
        }
        [data-type="map-felder"] path[data-kind="bienen"] {
            --feld: oklch(65% 0.130 84);
        }
        [data-type="map-felder"] path[data-kind="guck"] {
            --feld: oklch(49% 0.160 142);
        }
        [data-type="map-felder"] path[data-kind="kupfer"] {
            --feld: oklch(49% 0.081 46);
        }
        [data-type="map-felder"] path[data-kind="zinn"] {
            --feld: oklch(83% 0.022 264);
        }
        [data-type="map-felder"] path[data-kind="silber"] {
            --feld: oklch(69% 0.115 248);
        }
        [data-type="map-felder"] path[data-kind="ruestung"] {
            --feld: oklch(50% 0.077 66);
        }
        [data-type="map-felder"] path[data-kind="leviathan"] {
            --feld: oklch(42% 0.007 49);
        }
        /* FELDFARBEN ENDE */

        [data-type="map-marker"][data-kind="ruestung"] {
            background-image: url("/icons/map-helmet.webp");
        }

        /* Der Leviathan ({T0094}): der Ruecken mit den Seepocken, aus dem
           Wasser ragend. Er steht groesser als die uebrigen Rohstoffe, weil
           die Skala ein Rang ist und kein Geschmack — selten und entscheidend
           ist gross. Chitin gibt es nur hier, und es wird vor den Ebenen
           gebraucht.

           Die Regel steht **nach** der Ebenenregel, sonst traegt er deren
           fuenfzehn Punkte ({K0157}). Und anders als Kupfer und Zinn behaelt
           er seinen Schlagschatten: die beiden Erze verzichten darauf, weil
           sie zu Tausenden im Bild stehen und jeder Schatten eine eigene
           Rasterung kostet — Leviathane sind eine Handvoll je Welt, und auf
           dunklem Wasser ist der Schatten das, was den grauen Ruecken
           ueberhaupt vom Grund abhebt. */
        [data-type="map-marker"][data-kind="leviathan"] {
            background-image: url("/icons/map-leviathan.webp");
            --zeichen: 22;
        }

        [data-type="map-marker"][data-ebene="rohstoffe"] [data-type="map-marker-name"] {
            display: none;
        }

        [data-type="map-marker"][data-ebene="rohstoffe"]:hover [data-type="map-marker-name"] {
            display: block;
        }

        [data-type="map-markers"]:not([data-ebenen~="siedlungen"]) [data-ebene="siedlungen"] {
            display: none;
        }

        /* Siedlungen und Ruinen ({T0027S10}): die volle Ebene, mit ueber
           viertausend Punkten die dichteste der Karte. Ihre Zeichen stehen
           deshalb ein Drittel kleiner als die der Dungeons - 15 statt 22
           Punkte (Owner, 4. September 2026: »diese sind 1/3 kleiner als die
           anderen Icons darzustellen«). Sechs Gruppen, sechs Bilder: Lager,
           Hafen, Dvergrbau, Holzhuette, Aussenposten, Festung. */
        [data-type="map-marker"][data-ebene="siedlungen"] {
            --zeichen: 15;
        }

        [data-type="map-marker"][data-kind="morkhalla"] {
            background-image: url("/icons/map-morkhalla.webp");
            --zeichen: 48;
        }

        /* Das Holzfaellerlager: Axt im Stumpf. Im Mass des Lagers, mit dem es
           die Ebene teilt. */
        [data-type="map-marker"][data-kind="holzlager"] {
            background-image: url("/icons/map-holzlager.webp");
            --zeichen: 29;
        }

        /* Die beiden Doerfer des Graslands ({T0043S1}): dasselbe Dorf aus drei
           verfallenen Haeusern, einmal still und einmal mit gruen leuchtenden
           Fenstern. Sie standen bis hierher unter dem Zeichen der Blockhuette. */
        /* Der Maibaum ({T0112}): ein bis zwei je Welt, an einem Hof des
           Graslands, das ganze Jahr. Selten und gesucht, also groesser als
           das Dorf, an dem er steht; das Bild stammt vom Owner. */
        [data-type="map-marker"][data-kind="maibaum"] {
            background-image: url("/icons/map-maibaum.webp");
            --zeichen: 26;
        }

        /* Der Maibaum steht ohne Ebene und damit immer ({T0119}); sein Schild
           aber erst unter der Maus wie bei Siedlungen und Gipfeln ({T0120},
           Owner am 15. September 2026): zwei Maibaeume am selben Hof trugen
           sonst zwei Schilder uebereinander. */
        [data-type="map-marker"][data-kind="maibaum"] [data-type="map-marker-name"] {
            display: none;
        }

        [data-type="map-marker"][data-kind="maibaum"]:hover [data-type="map-marker-name"] {
            display: block;
        }

        [data-type="map-marker"][data-kind="dorf"] {
            background-image: url("/icons/map-dorf.webp");
            /* 33 seit {T0047S5} - davor 29, 22 und 20, Owner am 6. September. */
            --zeichen: 33;
        }

        [data-type="map-marker"][data-kind="befallen"] {
            background-image: url("/icons/map-befallen.webp");
            /* 33 seit {T0047S5} - davor 29, 22 und 20, Owner am 6. September. */
            --zeichen: 33;
        }

        [data-type="map-marker"][data-kind="lager"] {
            background-image: url("/icons/map-camp.webp");
            /* 29 statt 30 seit {T0047S4}, Owner am 6. September. */
            --zeichen: 29;
        }

        [data-type="map-marker"][data-kind="hafen"] {
            background-image: url("/icons/map-dverger-harbor.webp");
        }

        /* Der Dvergr-Hafen bleibt bei zweiunddreissig Punkten (Owner,
           5. September 2026: »aber nicht die Dvergerhaefen, die passen so«). */
        [data-type="map-marker"][data-kind="hafen"] {
            --zeichen: 32;
        }

        /* Fulinglager und Dvergr-Siedlung stehen bei dreissig Punkten - genau
           zwischen der Grundregel und dem Hafen (Owner: 35, 32, 28 - »och noe,
           jetzt sind die Dvergersiedlungen wieder so winzig« -, dann 32 und
           schliesslich »genau dazwischen waere es ja«). Sie sind Orte, an
           denen jemand wohnt, und stehen damit ueber der Huette, aber unter
           dem Aussenposten, der die Landmarke ist. */
        [data-type="map-marker"][data-kind="dvergr"] {
            --zeichen: 30;
        }

        [data-type="map-marker"][data-kind="dvergr"] {
            background-image: url("/icons/map-dverger-home.webp");
        }

        /* Die befallenen Dvergr-Bauten ({T0067}): der verfallene Wachturm mit
           gruenem Leuchten und Speeren, wo Seeker stehen. Dieselben dreissig
           Punkte wie die Siedlung - derselbe Rang, ein anderer Zustand. Die
           Regel steht hinter der Ebenenregel, sonst gewinnt die Ebene ({K0157}). */
        [data-type="map-marker"][data-kind="dvergrbefallen"] {
            background-image: url("/icons/map-dverger-infested.webp");
            --zeichen: 30;
        }

        /* Die leeren Dvergr-Bauten ({T0069}): der verfallene Turm mit der
           Laterne, ohne Gruen - Wachturm 3 verfallen und der Leuchtturm, in
           denen niemand steht. Dieselben dreissig Punkte, derselbe Rang. */
        [data-type="map-marker"][data-kind="dvergrleer"] {
            background-image: url("/icons/map-dverger-empty.webp");
            --zeichen: 30;
        }

        [data-type="map-marker"][data-kind="huette"] {
            background-image: url("/icons/map-hut.webp");
        }

        /* Der Aussenposten steht ein Viertel groesser als die Grundregel -
           35 statt 28 Punkte (Owner, 5. September 2026, nach 37, 32 und der
           Rueckkehr auf 28 mit neuer Zeichnung). Er ist die Landmarke in
           fremdem Gebiet und darf ueber der Huette stehen, aber unter dem
           Bosszeichen (37). */
        [data-type="map-marker"][data-kind="posten"] {
            background-image: url("/icons/map-post.webp");
            /* 32 seit {T0047S5}, davor 34 und 35 - Owner am 6. September. */
            --zeichen: 32;
        }

        /* Die Bauwerke der Aschenlande stehen doppelt so gross wie die
           uebrigen Zeichen der Karte - seit dem 5. September 54 Punkte, ein
           Drittel mehr als die 40 vom Vortag (Owner, 4. September 2026:
           »die gehen dort voellig unter«). Der Grund der Aschenlande ist
           rotbraun und unruhig, und ein graues Zeichen verschwindet darin.
           Damit ist die Festung groesser als die Bosszeichen, und das ist
           gewollt: es sind die groessten Anlagen des Spiels. */
        [data-type="map-marker"][data-kind="festung"] {
            --zeichen: 54;
        }

        /* Moerkhalla, die Jotun-Burg des Nordens ({T0074}). Sie teilt die
           Ebene mit der Festung der Aschenlande, steht aber sechs Punkte
           kleiner (Owner, 10. September 2026): ihr Zeichen traegt mehr
        /* Die Ruinen stehen ein Drittel kleiner (Owner, 5. September 2026).
           Sie sind mit achtundsechzig Stueck in einem Ausschnitt die haeufigste
           Anlage der Aschenlande, und was haeufig ist, darf nicht so gross
           sein wie das, was selten ist: die vierzehn Festungen sollen zwischen
           ihnen zu finden sein. */
        [data-type="map-marker"][data-kind="ruine"] {
            --zeichen: 27;
        }

        /* Zwei Bauwerke, zwei Zeichen ({T0027S10} QS): CharredFortress ist
           die Festung, die man einnimmt - zwanzig in der Welt und
           prioritisiert platziert -, FortressRuins sind die hundert
           zerfallenen. Sie standen zuerst unter einem Bild, und damit war
           auf der Karte nicht zu sehen, welche welche ist (Owner,
           4. September 2026). */
        [data-type="map-marker"][data-kind="festung"] {
            background-image: url("/icons/map-fortress.webp");
        }

        [data-type="map-marker"][data-kind="ruine"] {
            background-image: url("/icons/map-ruin.webp");
        }

        [data-type="map-marker"][data-ebene="siedlungen"] [data-type="map-marker-name"] {
            display: none;
        }

        [data-type="map-marker"][data-ebene="siedlungen"]:hover [data-type="map-marker-name"] {
            display: block;
        }


        [data-type="map-marker"][data-kind="spawn"] {
            /* Unter /icons und nicht unter /karte: das Kartenverzeichnis
               gehoert dem Dienst und wird aufgeraeumt ({T0026S12}) - ein
               Symbol darin waere irgendwann weg. */
            background-image: url("/icons/map-spawn.webp");
            /* Ganz oben: es gibt ihn einmal je Welt ({T0045S1}). */
            z-index: 4;
        }

        /* Ein Bild je Aufloesungsstufe, alle uebereinander in voller Groesse
           der Ebene ({T0026S11}). Welche man sieht, sagt die Deckkraft; der
           Wechsel ist ein Uebergang und kommt erst, wenn das Bild geladen ist
           — so blitzt beim Zoomen nichts auf und nichts fehlt. Pixelig
           vergroessert, wie die Karte es sein soll, wenn man ueber ihre Stufe
           hinaus zoomt.

           `prefers-reduced-motion` schaltet den Uebergang ab: dann wechselt
           das Bild hart, was hier keine Auskunft kostet (AW18). */
        /* `max-inline-size: none` ist hier keine Zierde: die Grundregel der
           Site deckelt jedes Bild auf die Breite seines Elternteils, und die
           Kachelebene hat keine — sie ist ein Punkt, an dem die Kacheln
           hängen. Ohne diese Zeile sind alle Kacheln null Pixel breit
           (gemessen, 3. September 2026). */
        [data-type="map-tiles"] img {
            position: absolute;
            inset: 0;
            display: block;
            inline-size: 100%;
            block-size: 100%;
            max-inline-size: none;
            max-block-size: none;
            /* Der Ausgangswert, weil das erste Bild immer hochskaliert ist;
               von da an setzt karte.js ihn je nach Richtung der Skalierung —
               Bloecke beim Vergroessern, glatt beim Verkleinern
               ({T0026S13}). */
            image-rendering: pixelated;
            opacity: 0;
            transition: opacity 0.35s ease-out;
        }

        /* **Eine Kachel wird sichtbar, wenn sie dekodiert ist** ({T0027S29},
           zuvor {T0027S17} QS). Die Regel darueber setzt jedes Bild der
           Ebene auf `opacity: 0`, weil das Skript die Deckkraft je Stufe
           steuert; die Kacheln im Behaelter schaltet es einzeln mit
           `data-da`, das erst nach `decode()` kommt. So blendet jede Kachel
           in 0,35 s von der groben Unterlage zur scharfen Karte ueber, statt
           im Augenblick des Dekodierens hart zu erscheinen - und die grobe
           Stufe bleibt darunter, bis jede Kachel im Blick so weit ist. */
        /* Was man beruehrt, kommt nach oben und waechst ({T0045S1}, Owner am
           6. September 2026) - jeder Marker, gleich welcher Ebene. Die Regel
           steht hinter allen Ebenenregeln und traegt ein Attribut mehr, damit
           sie jede von ihnen schlaegt; vorher setzte jede Ebene ihren eigenen
           Hover-Wert, und der lag unter den Bossen. Der Zuwachs geht in die
           Grundregel ein, siehe dort: vier Punkte, das Zentrum bleibt. */
        [data-type="map-markers"] [data-type="map-marker"]:hover {
            z-index: 5;
            --zuwachs: 4;
        }

        /* Die Auskunft unter dem Zeiger ({T0027S19}): unten links in der
           Karte, so unaufdringlich wie eine Statuszeile. Sie steht nur, wenn
           die Maus ueber der Karte ist. */
        [data-type="map-auskunft"] {
            position: absolute;
            inset-block-end: var(--space-tight);
            inset-inline-start: var(--space-tight);
            z-index: 3;
            padding: 0.15rem 0.5rem;
            font-size: 12px;
            font-weight: 600;
            color: oklch(99% 0 0);
            background: oklch(12% 0.01 260 / 0.65);
            border-radius: 2px;
            pointer-events: none;
            white-space: nowrap;
        }

        [data-type="map-auskunft"][hidden] {
            display: none;
        }

        [data-type="map-tiles"] [data-kacheln] img[data-da] {
            opacity: 1;
        }

        /* Der Behaelter selbst blendet ein wie ein Stufenbild: erscheint die
           feine Stufe als Ganzes ueber der Unterlage, ist das ein Uebergang
           von grob nach scharf und kein Sprung ({T0027S29}). */
        [data-type="map-tiles"] [data-kacheln] {
            transition: opacity 0.35s ease-out;
        }

        @media (prefers-reduced-motion: reduce) {
            [data-type="map-tiles"] img,
            [data-type="map-tiles"] [data-kacheln] { transition: none; }
        }

        /* Die Ladeschicht: eine verschwommene Glasscheibe ueber der ganzen
           Karte, solange der Dienst rechnet, darauf die Zeile »[4/18] Schritt«
           und der Balken (Owner, 3. und 8. September 2026). Sie verdeckt eine
           halbfertige Karte, statt sie zu zeigen. Die Schrittliste, die hier
           bis {T0068S2} stand, blitzte nur Millisekunden auf und ist weg. */
        [data-type="map-loading"] {
            position: absolute;
            display: grid;
            gap: var(--space-close);
            z-index: 3;
        }
        [data-type="map-loading"][data-form="voll"] {
            inset: 0;
            place-content: center;
            justify-items: center;
            backdrop-filter: blur(8px) saturate(0.7);
            background: light-dark(oklch(100% 0 0 / 0.35), oklch(12% 0.01 260 / 0.45));
        }
        /* Danach: klein in der rechten oberen Ecke, ohne Scheibe - die Karte
           steht ja schon, nachgeladen wird nur noch. Die Zeile oben, darunter
           der Balken, drei Punkte hoch und so breit wie die Zeile: so bleibt
           die einzige Kante, die sich bewegt, seine Fuellung ({T0068S2}). */
        /* Der Anzeigebereich ist immer zweihundert Punkte breit, mit oder
           ohne Balken, egal wie lang das Wort ({T0068S4}, {T0068S5}, Owner):
           ein Kasten, der mit dem Text atmet, wackelt bei jedem Schritt. Erst
           hundert, dann zweihundert - der Owner hatte von Anfang an
           zweihundert gemeint. Der Rand kommt dazu. */
        [data-type="map-loading"][data-form="ecke"] {
            inset-block-start: var(--space-close);
            inset-inline-end: var(--space-close);
            gap: var(--space-hair);
            justify-items: start;
            box-sizing: content-box;
            inline-size: 200px;
            padding: 0.25rem 0.5rem;
            background: light-dark(oklch(100% 0 0 / 0.7), oklch(12% 0.01 260 / 0.7));
            box-shadow: inset 1px 1px 0 oklch(33% 0.014 260), inset -1px -1px 0 oklch(12% 0.01 260);
        }
        /* Der Balken der Ecke ({T0068S3}, Owner nach dem ersten Blick): so
           breit wie der Anzeigebereich, drei hoch, kein Goldrand um den leeren Teil -
           der ist ein leichtes Grau, gefuellt wird gold. Der Text steht links
           buendig darueber, laeuft nach rechts ueber den Balken hinaus und
           bekommt am Ende eine Ellipse, wenn die Ecke zu schmal wird. */
        [data-form="ecke"] > progress {
            block-size: 3px;
            inline-size: 100%;
            border: 0;
            background: oklch(70% 0.01 260 / 0.35);
        }
        [data-form="ecke"] > progress::-webkit-progress-bar {
            background: oklch(70% 0.01 260 / 0.35);
        }
        [data-form="ecke"] [data-type="map-phase"] {
            text-align: start;
            inline-size: 100%;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        [data-type="map-loading"][hidden] {
            display: none;
        }
        [data-type="map-phase"] {
            margin: 0;
            font-size: var(--text-small);
            letter-spacing: 0.04em;
            color: var(--ink-bright);
            text-align: center;
            white-space: nowrap;
            font-variant-numeric: tabular-nums;
        }
        /* Die Zeile nach einem Fehler: nicht rot, sondern ruhig anders - der
           Text sagt, was fehlt; die Farbe sagt nur, dass es kein Laden mehr ist. */
        [data-type="map-loading"][data-fehler] [data-type="map-phase"] {
            color: var(--ink-quiet);
        }
        [data-type="map-loading"][data-fehler] > progress {
            visibility: hidden;
        }
    }
}

/* Der Griff fuer die grosse Ansicht ({T0028S1}).

   Er liegt in der einen Zelle des Fensters, unten rechts, und ist ein Label
   ueber einem Kontrollkaestchen: geschaltet wird ohne Skript, wie die
   Ebenenfilter. Das Kaestchen liegt darunter und bleibt fokussierbar — es
   traegt den Zustand, den die has-Regel am Blatt liest.

   Das Zeichen sind zwei Eckklammern, gezeichnet aus vier Innenschatten in den
   Ecken eines leeren Kastens: kein SVG fuer vier Striche, keine zweite Datei,
   und es traegt die Themefarbe von selbst. In der grossen Ansicht kehren sich
   die Ecken nach innen — dasselbe Zeichen sagt dann »verkleinern«. */
@layer slices.slice_map {
    @scope ([data-slice="map"]) {
        /* Die Knopfgruppe oben rechts ({T0084}). Sie sitzt in derselben
           Rasterzelle wie der Vollbildschalter, nur an der anderen Kante, und
           traegt dessen Optik: gleiche Groesse, gleiche Pixelkanten, gleicher
           halbdurchsichtiger Grund. Zwei Werkzeuge an einer Karte sollen wie
           zwei Knoepfe desselben Geraets aussehen und nicht wie zwei Entwuerfe. */
        /* Solange die Karte rechnet, liegt die Schrittanzeige in derselben
           Ecke — und es gaebe ohnehin nur ein halbes Bild zu sichern. Also
           weichen die Werkzeuge nicht aus, sie warten: das Skript setzt
           `hidden` an ihnen, wo es den Ladebalken schaltet (ladenZeigen).
           **Nicht per :has() am Kartenfenster** ({T0145}): die Regel
           `[data-type="map-window"]:has([data-type="map-loading"]:not([hidden])) [data-type="map-tools"]`
           liess Blink bei jedem Wechsel des Ladebalkens - beim Schwenken je
           Bild - alle Nachfahren des Fensters mit einem data-type neu rechnen,
           31 640 Elemente in acht Bildern; auf dem Handy Sekunden je Verschiebung. */

        [data-type="map-tools"] {
            place-self: start end;
            z-index: 4;
            display: flex;
            gap: var(--space-close);
            margin: var(--space-close);
        }

        [data-type="map-tool"] {
            display: grid;
            place-items: center;
            inline-size: 2.5rem;
            block-size: 2.5rem;
            padding: 0;
            border: 0;
            cursor: pointer;
            background: color-mix(in oklch, var(--surface-0) 72%, transparent);
            box-shadow:
                inset 2px 2px 0 oklch(from var(--surface-4) calc(l + 0.12) c h),
                inset -2px -2px 0 oklch(from var(--surface-0) calc(l - 0.06) c h);
        }

        [data-type="map-tool"]:hover {
            background: color-mix(in oklch, var(--link) 6%, var(--surface-0) 72%);
            box-shadow:
                inset 2px 2px 0 oklch(from var(--surface-0) calc(l - 0.06) c h),
                inset -2px -2px 0 oklch(from var(--surface-4) calc(l + 0.12) c h);
        }

        [data-type="map-tool"]:focus-visible {
            outline: 2px solid var(--link);
            outline-offset: 2px;
        }

        /* Waehrend das Bild entsteht, ist der Knopf nicht bedienbar: ein
           zweiter Klick baute ein zweites Bild und lieferte beide. */
        [data-type="map-tool"][aria-busy="true"] { cursor: progress; opacity: 0.6; }

        /* Die Zeichen, gezeichnet statt gesetzt. Sichern: ein Pfeil nach unten
           ueber einer offenen Schale. Teilen: drei Punkte, zwei Verbindungen. */
        [data-type="map-tool"] [data-part="zeichen"] {
            inline-size: 1.125rem;
            block-size: 1.125rem;
            background-repeat: no-repeat;
        }

        [data-zeichen="sichern"] {
            --strich: var(--ink-bright);
            background:
                /* Schaft */
                linear-gradient(var(--strich), var(--strich)) 50% 0 / 2px 0.62rem,
                /* Spitze, zwei Schraegen */
                linear-gradient(45deg, transparent 46%, var(--strich) 46% 54%, transparent 54%)
                    0.16rem 0.34rem / 0.4rem 0.4rem,
                linear-gradient(-45deg, transparent 46%, var(--strich) 46% 54%, transparent 54%)
                    0.56rem 0.34rem / 0.4rem 0.4rem,
                /* Schale */
                linear-gradient(var(--strich), var(--strich)) 0 100% / 2px 0.34rem,
                linear-gradient(var(--strich), var(--strich)) 100% 100% / 2px 0.34rem,
                linear-gradient(var(--strich), var(--strich)) 0 100% / 100% 2px;
        }

        [data-zeichen="teilen"] {
            --strich: var(--ink-bright);
            --punkt: 0.34rem;
            background:
                /* zwei Verbindungslinien, dann die drei Punkte darueber */
                linear-gradient(-32deg, transparent 45%, var(--strich) 45% 55%, transparent 55%)
                    0.1rem 0.1rem / 0.95rem 0.5rem,
                linear-gradient(32deg, transparent 45%, var(--strich) 45% 55%, transparent 55%)
                    0.1rem 0.52rem / 0.95rem 0.5rem,
                radial-gradient(circle, var(--strich) 48%, transparent 50%)
                    100% 0 / var(--punkt) var(--punkt),
                radial-gradient(circle, var(--strich) 48%, transparent 50%)
                    100% 100% / var(--punkt) var(--punkt),
                radial-gradient(circle, var(--strich) 48%, transparent 50%)
                    0 50% / var(--punkt) var(--punkt);
        }

        /* Das Rueckmeldeblatt. Popover statt eigener Ueberlagerung: der Browser
           bringt Fokusfalle, Schliessen mit Escape und die oberste Ebene mit. */
        /* Die beiden Blaetter der Karte tragen die Gestalt des Gegenstandsblatts
           ({T0084}): goldener Rahmen, dunkle Flaeche, klebender Kopf mit dem
           Schliesszeichen rechts. Ein zweites Aussehen fuer dieselbe Sache
           waere eine zweite Sprache auf derselben Seite.

           Das Widget selbst laesst sich nicht aufrufen: es verlangt Zeichen,
           Name und Weg eines Gegenstands, und ein Ausfuhrdialog hat keines
           davon. Uebernommen sind darum die Masse und die Toene, mit dieser
           Zeile als Herkunftsnachweis — wer dort etwas aendert, aendert es
           hier mit. Wenn ein dritter Aufrufer kommt, ist die Huelle als
           eigenes Element faellig ({K0054}). */
        [data-type="map-sheet"] {
            position: fixed;
            inset: 0;
            margin: auto;
            z-index: var(--sheet-layer);
            inline-size: var(--sheet-inline-size);
            max-block-size: var(--sheet-max-block-size);
            overflow-y: auto;
            padding: 0;
            border: var(--sheet-edge) solid var(--gold);
            background: var(--surface-4);
            color: var(--ink);
            box-shadow: var(--sheet-shadow);
        }

        [data-type="map-sheet"]::backdrop {
            background: var(--sheet-scrim);
        }

        [data-type="map-sheet"] > header {
            position: sticky;
            inset-block-start: 0;
            display: grid;
            align-items: center;
            gap: 0.75rem;
            padding: 0.875rem 1rem;
            background: var(--surface-4);
        }

        /* Das Schliesszeichen sitzt in der Ecke, nicht in einer Rasterspalte
           ({T0086}, Owner am 11. September 2026). In der Spalte schob der
           Innenabstand des Kopfes es nach innen, und es stand einen Finger
           breit von der Kante entfernt — im Gegenstandsblatt liegt es in der
           Ecke. Dieselbe Stellung, damit beide Blaetter sich gleich schliessen
           lassen, ohne dass man erst sucht.

           Vier Punkte Abstand, wie dort: der Bezug ist das Blatt, das `fixed`
           steht, nicht der Kopf. */
        [data-type="map-sheet"] [data-rolle="schliessen"] {
            position: absolute;
            inset-block-start: 4px;
            inset-inline-end: 4px;
            z-index: 1;
        }

        [data-type="map-sheet"] > header b {
            font-size: var(--text-large, 1.125rem);
        }

        [data-type="map-sheet"] [data-part="dossier"] {
            padding: 0 1rem 0.5rem;
        }

        [data-type="map-sheet"] [data-part="head"] {
            display: flex;
            flex-wrap: wrap;
            gap: 0.5rem;
            align-items: baseline;
        }

        [data-type="map-sheet"] [data-part="title"] {
            font-weight: 700;
        }

        [data-type="map-sheet"] [data-part="aside"],
        [data-type="map-sheet"] [data-part="masse"] {
            font-size: var(--text-small, 0.875rem);
            color: var(--ink-muted, var(--ink));
        }

        /* Der Ausschnitt um den Spawn: er sagt in einem Blick, welche Welt
           gemeint ist. Ohne Bild bleibt das Feld weg statt leer zu stehen. */
        [data-type="map-sheet"] [data-part="vorschau"] {
            inline-size: 100%;
            /* 16:9 — das Mass, in dem ein geteiltes Bild in den Netzen
               angezeigt wird. Der Ausschnitt liegt um den Spawn, nicht um die
               Kachelmitte: sonst faellt der Startplatz heraus. */
            aspect-ratio: 16 / 9;
            margin-block-end: var(--space-close);
            background: var(--surface-0);
        }

        [data-type="map-sheet"] [data-part="text"] {
            inline-size: 100%;
            resize: none;
            font: inherit;
            font-size: var(--text-small, 0.875rem);
            padding: var(--space-close);
            border: 1px solid var(--surface-0);
            background: var(--surface-0);
            color: var(--ink);
        }

        /* Der Knopf sitzt unten im Blatt und fuellt die Breite — dieselbe
           Geste wie im Gegenstandsblatt, wo der Weg in den Bauplan so steht. */
        /* Das Vorschaufenster im Sichern-Blatt ({T0084}, Owner am
           11. September 2026). Die Leinwand ist 4096 Punkte breit und wird
           hier auf Blattbreite gezeigt — der Browser rechnet das herunter,
           und weil er es tut, sieht man die Kacheln einlaufen, ohne daß
           irgendwo ein zweites Bild entsteht.

           Quadratisch, weil die Welt es ist. Die Flaeche darunter ist die
           dunkelste des Entwurfs: bevor die erste Kachel liegt, soll dort
           nicht Weiss leuchten. */
        [data-type="map-sheet"] [data-part="bild"] {
            inline-size: calc(100% - 2rem);
            aspect-ratio: 1;
            margin: 0 1rem var(--space-close);
            background: var(--surface-0);
        }

        /* Der Ladestand ueber dem Knopf ({T0084}, Owner am 11. September
           2026). Er sitzt im selben Rand wie der Knopf darunter, damit beide
           eine Kante bilden, und nimmt die volle Breite: der Balken der Ecke
           ist hundert Punkte breit, weil dort kein Platz ist — hier ist
           welcher, und ein Prozent je Pixel liest sich leichter, je laenger
           die Strecke. */
        [data-type="map-sheet"] [data-part="fortschritt"] {
            margin: 0 1rem var(--space-close);
        }

        [data-type="map-sheet"] [data-part="fortschritt"] [data-type="progress"] {
            inline-size: 100%;
        }

        [data-type="map-sheet"] [data-type="button"] {
            display: block;
            inline-size: calc(100% - 2rem);
            margin: 0 1rem 1rem;
        }

    }
}

/* Der Panelbehaelter ({T0028S3}).

   In der kleinen Ansicht ist er fuer das Layout nicht da: `display: contents`
   laesst seine Kinder direkte Rasterkinder des Bandes bleiben, also aendert
   sich dort nichts. Die Lasche gehoert zur grossen Ansicht und steht hier
   nicht. */
@layer slices.slice_map {
    @scope ([data-slice="map"]) {
        [data-part="panel"],
        [data-part="inhalt"] { display: contents; }
        [data-type="map-tab"] { display: none; }

        /* Und seine Kinder brauchen die Spur selbst ({T0028S3} QS): die
           Grundregel `[data-slice] > *` trifft nur direkte Kinder, und mit
           `display: contents` ist der Behaelter keines mehr — seine Kinder
           sind es. Ohne diese Zeile landet die Ueberschrift in der Rinne, 40
           Pixel breit und 214 hoch, und die Karte in einer impliziten Zeile
           von 61 Pixeln. Gemessen an der laufenden Seite; der Owner hat es
           gesehen, bevor ein Bildschirmfoto es zeigte. */
        [data-part="inhalt"] > * { grid-column: content; }
    }
}

/* Der Stern und sein Blatt ({T0030S1}).

   Der Stern steht in der kleinen Ansicht neben dem Titel und in der grossen in
   derselben Rasterzelle an dessen Ende. Er ist ein Knopf ohne Flaeche: das
   Zeichen traegt die Aussage, und gefuellt heisst gemerkt.

   Zwei Sterne aus einem Zeichen waeren zwei Dateien; einer aus zwei
   Hintergruenden ist keiner. Also steht er als Text — ein Zeichen der Schrift,
   gefaerbt: leer in der Tintenfarbe, gefuellt in Gold. */
@layer slices.slice_map {
    @scope ([data-slice="map"]) {
        /* Titel und Stern sind eine Zeile ({T0030S1} QS): der Stern steht
           hinter dem Namen, auf seiner Grundlinie und in seiner Laufhoehe.
           `baseline` und nicht `center` — ein Zeichen neben Text richtet sich
           an der Schrift aus, nicht an der Kastenmitte. */
        [data-part="titelzeile"] {
            display: flex;
            align-items: baseline;
            gap: 0.4em;
            min-inline-size: 0;
        }

        [data-part="titelzeile"] > [data-type="heading"] {
            min-inline-size: 0;
            overflow-wrap: anywhere;
        }

        /* Der Stern: der Zeichenknopf des Hauses in der Laufhoehe des Titels.
           Er erbt sie nicht, weil er ein Geschwister ist und keine Zeile in ihm.
           Gefuellt heisst gemerkt, und gefuellt ist Gold - die zweite Farbe des
           Hauses, nicht eine dritte. */
        [data-part="titelzeile"] > [data-type="icon-button"] {
            flex: 0 0 auto;
            font-size: var(--text-title);
            line-height: 1;
            color: var(--ink);
            anchor-name: --karte-stern;
        }

        [data-part="titelzeile"] > [data-type="icon-button"][data-gemerkt="ja"],
        [data-part="titelzeile"] > [data-type="icon-button"]:hover {
            color: var(--gold);
        }

        /* Das Blatt haengt am Stern: Anchor Positioning statt einer gerechneten
           Ecke (platform-features.md, Newly seit Maerz 2026). Faellt der Anker
           aus, steht es mittig — das ist der Ruhewert des Top-Layers und keine
           Notloesung, die etwas verdeckt. */
        [data-type="map-fav"] {
            /* `position: fixed` steht ausdruecklich da, obwohl der Browser es
               einem Popover selbst gibt — und `display` steht **nicht** da.
               Ein `display: grid` an dieser Stelle ueberschreibt das
               `display: none`, mit dem der Browser ein geschlossenes Popover
               aus dem Fluss nimmt: Autorenregeln schlagen die des Browsers
               ohne Ansehen der Spezifitaet. Gemessen: das geschlossene Blatt
               stand als dritte Rasterzeile von 186 Pixeln im Panel, unsichtbar
               und raumgreifend. Das Raster kommt erst mit dem Oeffnen. */
            position: fixed;
            position-anchor: --karte-stern;
            position-area: block-end span-inline-start;
            position-try-fallbacks: flip-block, flip-inline;
            margin: var(--space-tight);
            inline-size: min(22rem, calc(100vi - 2 * var(--space-close)));
            gap: var(--space-snug);
            padding: var(--space-close);
            /* Der Token und nicht die Zahl ({T0097}): sie stand hier
               ausgeschrieben, waehrend die beiden Blaetter daneben im selben
               Slice `--sheet-edge` lesen. Derselbe Wert, zwei Schreibweisen —
               und die zweite haette beim ersten Aendern gefehlt. */
            border: var(--sheet-edge) solid var(--gold);
            background: var(--surface-1);
            color: var(--ink-bright);

            /* Die Hausform fuer alles, was aufpoppt ({K0101}): der Ruhewert ist
               zu, overlay geht mit ueber, und der z-index steht daneben, weil
               overlay den Moment des Schliessens nicht ueberall ueberbrueckt. */
            opacity: 0;
            transition-property: opacity, overlay, display;
            transition-duration: 300ms;
            transition-behavior: allow-discrete;
            z-index: 9999;
        }

        [data-type="map-fav"]:popover-open { display: grid; opacity: 1; }

        @starting-style {
            [data-type="map-fav"]:popover-open { opacity: 0; }
        }

        @media (prefers-reduced-motion: reduce) {
            [data-type="map-fav"] { transition-duration: 1ms; }
        }

        [data-type="map-fav"] > header {
            display: grid;
            grid-template-columns: 1fr auto;
            align-items: center;
            gap: var(--space-snug);
        }

        /* Das Formular: Feld, Sichern, Entfernen in einer Zeile, auf der
           Unterkante ausgerichtet - das Feld traegt seine Beschriftung darueber,
           die Knoepfe nicht. Ihre Gestalt bringen die Bausteine mit; hier steht
           nur die Anordnung. */
        [data-type="map-fav"] [data-part="merken"] {
            display: grid;
            grid-template-columns: 1fr auto auto;
            gap: var(--space-snug);
            justify-items: end;
        }

        /* Das Feld hat die ganze Zeile fuer sich, die Knoepfe stehen darunter
           rechts. In einer Zeile mit beiden liessen sie dem Feld sechzig
           Pixel - gemessen im Bild, »Mein« war zu lesen und sonst nichts. */
        [data-type="map-fav"] [data-part="merken"] > [data-type="field"] {
            grid-column: 1 / -1;
            justify-self: stretch;
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Kennzahlenband — abgesetzt, oben und unten begrenzt. */

@layer slices.slice_metrics {
    @scope ([data-slice="metrics"]) {
        :scope {
            padding-block: var(--space-open);
            background: var(--surface-2);

            /* Hier eine Linie und nicht nur die Fläche: das Band sitzt zwischen
               zwei Bändern ähnlicher Helligkeit, und ohne Kante verschwämme die
               Grenze. Die Ausnahme von {K0031} ist begründet, nicht bequem. */
            border-block: 1px solid var(--surface-4);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Was neu ist — zwei Spalten, auf schmalem Schirm untereinander. */

@layer slices.slice_neues {
    @scope ([data-slice="neues"]) {
        :scope {
            padding-block: var(--space-open);
        }

        /* Der Behaelter sitzt in der Inhaltsspur des Seitenrasters ({K0031})
           und traegt darin sein eigenes. */
        [data-part="spalten"] {
            grid-column: content;

            /* **`auto-fit` statt einer Stufenleiter** (AW6). Zwei Spalten, wo
               Platz fuer zwei ist, sonst eine — und die Grenze findet das
               Raster selbst aus der Mindestbreite. Eine Fensterabfrage
               muesste sie raten und waere bei jedem Umbau falsch; genau
               deshalb steht hier kein `@media`, obwohl der Nachbar-Slice
               eines hat. */
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
            gap: var(--space-wide);
        }

        [data-part="spalte"] {
            display: grid;
            gap: var(--space-close);
            align-content: start;
        }

        /* Kein `list-style`: die Aufzaehlung liegt in der Sache und braucht
           keinen Punkt davor — das Datum vorn ist die Marke. */
        [data-part="liste"] {
            display: grid;
            gap: var(--space-close);
            margin: 0;
            padding: 0;
            list-style: none;
        }

        [data-part="liste"] > li {
            display: grid;
            grid-template-columns: auto 1fr;
            gap: var(--space-snug);
            align-items: baseline;
        }

        /* Das Datum in der Etikettenschrift des Hauses — Monospace, damit
           untereinander stehende Daten auch untereinander stehen. */
        [data-part="datum"] {
            font-family: ui-monospace, monospace;
            font-size: var(--text-small);
            color: var(--ink-quiet);
            white-space: nowrap;
        }

        [data-part="wort"] { color: var(--ink); }

        [data-part="leer"] {
            margin: 0;
            color: var(--ink-quiet);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Statusfenster — eine Zeile je Dienst, und die Ampel daran. */

@layer slices.slice_status {
    @scope ([data-slice="status"]) {
        /* **Der Abstand liegt hier und nicht zwischen den Slices** \({K0031}\):
           Slices stossen aneinander, und der Abstand entsteht im Slice als
           Innenraum. `--space-open` ist 1,618 der Grundstufe — die Stufe der
           Leiter, die einen eigenen Block traegt, ohne ihn wie die Einleitung
           freizustellen \(die hat `--space-wide`\). */
        :scope {
            padding-block: var(--space-open);
            padding-inline: var(--gutter);
        }

        /* Titel links, Spielerzahl rechts — und bei Schmalheit untereinander.
           `wrap` statt Fensterabfrage: der Umbruch faellt, wenn der Platz
           faellt, und nicht bei einer geratenen Breite \(AW6\). */
        [data-part="kopf"] {
            display: flex;
            flex-wrap: wrap;
            align-items: baseline;
            justify-content: space-between;
            gap: var(--space-close);
        }

        /* Titel und Stand als Block, damit der Stand unter der Ueberschrift
           sitzt und nicht neben ihr — er gehoert zur Ueberschrift, nicht in
           die Reihe mit der Spielerzahl. */
        [data-part="titel"] {
            display: flex;
            flex-direction: column;
            gap: var(--space-hair);
        }

        /* Kleingedrucktes: es soll da sein, wenn man es sucht, und nicht mit
           den Zeilen konkurrieren. */
        [data-part="stand"] {
            margin: 0;
            color: var(--ink-quiet);
            font-size: var(--text-micro);
        }

        /* Die Zahl im Duktus des Kennzahlenbandes darunter: sie ist die
           Aussage, das Wort ordnet sie ein. */
        [data-part="spieler"] {
            display: flex;
            align-items: baseline;
            gap: var(--space-tight);
            margin: 0;
        }

        [data-part="zahl"] {
            color: var(--link);
            font-size: var(--text-large);
            font-weight: 600;
            font-variant-numeric: tabular-nums;
        }

        [data-part="spieler"] [data-part="wort"] {
            color: var(--ink-quiet);
            font-size: var(--text-small);
        }

        [data-part="alt"] {
            margin-block: var(--space-close) 0;
            color: var(--ink-quiet);
            font-size: var(--text-small);
        }

        /* Eine Zeile je Dienst \(Owner, 12. September 2026\). Das Paar aus Name
           und Zustand liegt in einem Raster mit zwei Spuren: der Name nimmt,
           was er braucht, der Zustand steht rechts davon. `dl` bringt eigene
           Abstaende mit, die hier nichts zu suchen haben. */
        [data-part="dienste"] {
            margin-block: var(--space-close) 0;
        }

        [data-part="zeile"] {
            display: grid;
            grid-template-columns: 1fr auto;
            align-items: center;
            gap: var(--space-close);
            padding-block: var(--space-tight);
        }

        /* Eine Linie zwischen den Zeilen, keine um sie herum: der Slice hat
           seine Flaeche, und ein Kasten im Kasten waere einer zuviel \(AW1\). */
        [data-part="zeile"] + [data-part="zeile"] {
            border-block-start: 1px solid var(--surface-4);
        }

        [data-part="name"] {
            margin: 0;
            color: var(--ink);
        }

        [data-part="zustand"] {
            display: flex;
            align-items: center;
            gap: var(--space-tight);
            margin: 0;
        }

        [data-part="zustand"] [data-part="wort"] {
            color: var(--ink-quiet);
            font-size: var(--text-small);
        }

        /* Das Licht ist rund und klein und traegt **keine** Aussage allein:
           daneben steht das Wort \(AW4\). Deshalb `aria-hidden` am Punkt — er
           wiederholt nur, was schon dasteht, und ein Screenreader, der »Punkt«
           sagt, hilft niemandem. */
        [data-part="licht"] {
            inline-size: 0.625rem;
            block-size: 0.625rem;
            border-radius: 50%;
            background: var(--licht, var(--ink-quiet));
            flex: none;
        }

        /* **Die drei Farben der Ampel sind die drei der Palette** \({K0234}\):
           das Primaergruen fuer »laeuft«, das Sekundaergold fuer »gestoert«,
           und das dunkle Rot der Marke fuer »aus« — gerechnet aus `--danger`
           und hier statt im `:root`, weil AW10 und {K0004} das so wollen: ein
           abgeleiteter Ton steht am Kontext, nicht an der Wurzel. Kein vierter
           Wert, keine eigene Ampelpalette. Was nicht gemessen wurde, bleibt gedaempft —
           es ist kein Zustand des Dienstes, sondern einer unserer Messung. */
        [data-zustand="gruen"]  { --licht: var(--link); }
        [data-zustand="gelb"]   { --licht: var(--gold); }
        [data-zustand="rot"]    { --licht: oklch(from var(--danger) 0.50 c h); }

        /* Der Zustand faerbt auch das Wort, wenn er von »laeuft« abweicht: bei
           vier gruenen Zeilen soll nichts schreien, bei einer roten schon. */
        [data-zustand="gelb"]  [data-part="wort"] { color: var(--gold); }
        [data-zustand="rot"]   [data-part="wort"] { color: var(--danger); }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Werkzeugkarten — einspaltig, zweispaltig, dreispaltig. */

@layer slices.slice_tools {
    @scope ([data-slice="tools"]) {
        /* Eigener Grund, eine Stufe unter der Seite, mit Kante nach oben: das
           Band hebt die Karten heraus, ohne sie einzurahmen. Aus dem Entwurf. */
        :scope {
            padding-block: 2.5rem;
            background: var(--surface-1);
            border-block-start: 1px solid oklch(30% 0.014 var(--hue-surface));
        }

        /* Die Karten teilen die content-Spur unter sich auf. Zweierlei ist dabei
           noetig: minmax\(0, 1fr\) statt 1fr, damit ein langes Wort im Titel die
           Spur nicht aufdehnt — und eine gerechnete Rinne statt minmax\(gutter,
           1fr\), weil sonst fuenf flexible Spuren um denselben Platz streiten und
           die Karten in die Mitte ruecken \({K0057}\). */
        :scope > [data-type="tool-card"] {
            grid-column: content;
        }

        :scope {
            row-gap: 1.25rem;
        }

        @media (min-width: 35rem) {
            :scope {
                grid-template-columns:
                    [full-start] var(--rand)
                    [content-start] minmax(0, 1fr) 1.25rem minmax(0, 1fr)
                    [content-end] var(--rand)
                    [full-end];
            }
            :scope > [data-type="tool-card"] { grid-column: auto; }
            :scope > [data-type="tool-card"]:first-child { grid-column: 2; }
            :scope > [data-type="tool-card"]:nth-child(2) { grid-column: 4; }
            :scope > [data-type="tool-card"]:nth-child(3) { grid-column: 2 / 5; }
        }

        @media (min-width: 60rem) {
            :scope {
                grid-template-columns:
                    [full-start] var(--rand)
                    [content-start] minmax(0, 1fr) 1.25rem
                                    minmax(0, 1fr) 1.25rem minmax(0, 1fr)
                    [content-end] var(--rand)
                    [full-end];
            }
            :scope > [data-type="tool-card"]:nth-child(2) { grid-column: 4; }
            :scope > [data-type="tool-card"]:nth-child(3) { grid-column: 6; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Der Bauplan ({T0073S4}) - Leiste, Goldstreifen, Flaeche, Baum auf
   der Biom-Achse ({T0073S7}). */

@layer slices.slice_tree {
    @scope ([data-slice="tree"]) {
        :scope {
            /* Die Hoehe einer Reihe des Baums: der Kreis, ein bis zwei Zeilen
               Name, wenig Luft - der Owner fand die erste Fassung zu locker
               ({T0073S7}); vorher 6.5 und 7 rem. */
            --reihe: 4.5rem;
            padding-block: 2.5rem;
            display: grid;
            row-gap: 1.25rem;
            /* Drei Spalten wie das Band selbst (base.css: Rand, Inhalt, Rand)
               - eine Zuweisung mit nur einer Spalte legte alles in die
               Randspur, und die Seite stand am Telefon in 40 px Breite. */
            grid-template-areas:
                ". lead  ."
                ". board .";
        }

        :scope > * { grid-column: content; }

        [data-part="lead"] { grid-area: lead; }

        [data-part="intro"] {
            margin: 0.5rem 0 0;
            max-inline-size: 66ch;
            color: var(--ink-quiet);
        }

        /* ---- Die Flaeche ----------------------------------------------- */

        /* Die Flaeche ist ein Container: die Kreise nehmen ihr Mass von
           ihrer Breite (cqi), damit sieben Knoten in einer Reihe auch am
           Telefon nebeneinander stehen und nichts rollt ({T0073S7}). */
        [data-part="board"] {
            grid-area: board;
            container-type: inline-size;
            display: grid;
            row-gap: 0.75rem;
            align-content: start;
            min-block-size: 20rem;
            padding: 1rem;
            /* Der Goldstreifen: zwei Pixel, eine Haarlinie und darum in px. */
            border-inline-start: 2px solid var(--gold);
            /* Kein eigener Grund mehr ({T0073S9}, Owner): der Verlauf rosa,
               orange, lila aus {T0073S4} ist heraus, die Farbe tragen die
               Baender der Gebiete. */
        }

        [data-part="empty"] {
            display: grid;
            gap: 0.75rem;
            justify-items: start;
            align-self: center;
            padding: 1.5rem 0;

            p { margin: 0; color: var(--ink); }
            b { color: var(--ink-bright); font-weight: 500; }

            /* Ein Ziel, das die Datenbank nicht kennt ({T0073S13}): die
               Leiste, die es vorher sagte, gibt es nicht mehr. */
            [data-part="missing"] {
                font-size: var(--text-small);
                color: var(--ink-quiet);

                b { color: var(--ink); font-weight: 500; }
            }
        }

        /* ---- Der Baum -------------------------------------------------- */

        /* Der Baum fuellt die Flaeche in der Breite und waechst nach unten:
           mehr Knoten heissen mehr Reihen, nie eine Rollbreite (Owner:
           keine Scrollbar). */
        [data-part="tree"] {
            display: grid;
        }

        /* Baender, Linien und Knoten in **einer** Zelle: die Flaechen der
           Gebiete zuunterst, die Zeichnung darueber, die Knoten oben, alle so
           gross wie der Baum. */
        [data-part="tree"] > * {
            grid-area: 1 / 1;
            min-inline-size: 0;
            block-size: calc(var(--reihen) * var(--reihe));
        }

        /* ---- Die Flaechen der Gebiete ({T0073S9}) ---------------------- */

        /* Dieselben Zeilen wie das Raster der Knoten; ein Band spannt seine
           Zeilen und die ganze Breite. */
        [data-part="bands"] {
            display: grid;
            grid-template-rows: repeat(var(--reihen), var(--reihe));
            pointer-events: none;
        }

        /* Der Verlauf je Gebiet, wie ihn die Leiste der Itemliste traegt
           (Owner, 8. September 2026: »diese Verlaeufe«): von links 35 Prozent
           Biomfarbe auf dem Grund nach rechts 12 Prozent - dieselbe Formel wie
           --biome-verlauf in slice_items.css, dort an der Leiste definiert und
           deshalb hier noch einmal; die Farbe kommt aus dem Theme. Die
           Ableitung steht an dem Kontext, der --biome setzt, nicht im :root
           ({K0004}). */
        [data-part="bandflaeche"] {
            --biome: var(--surface-4);
            grid-row: var(--von) / var(--bis);
            grid-column: 1 / -1;
            background: linear-gradient(90deg,
                color-mix(in oklab, var(--biome) 35%, var(--surface-0)),
                color-mix(in oklab, var(--biome) 12%, var(--surface-0)));

            &[data-biome="meadows"]     { --biome: var(--biome-meadows); }
            &[data-biome="blackforest"] { --biome: var(--biome-forest); }
            &[data-biome="swamp"]       { --biome: var(--biome-swamp); }
            &[data-biome="mountain"]    { --biome: var(--biome-mountain); }
            &[data-biome="plains"]      { --biome: var(--biome-plains); }
            &[data-biome="mistlands"]   { --biome: var(--biome-mistlands); }
            &[data-biome="ashlands"]    { --biome: var(--biome-ashlands); }
            &[data-biome="deepnorth"]   { --biome: var(--biome-deepnorth); }
            &[data-biome="ocean"]       { --biome: var(--biome-ocean); }
        }

        [data-part="edges"] {
            inline-size: 100%;
            overflow: visible;
            pointer-events: none;

            /* Zwei Farben ({T0073S11}, Owner): grau der Schritt, gruen der
               Weg - das Gruen der Aktion, mit Deckung, damit es traegt und
               nicht schreit. Eine Rueckkante aus einem Kreis gestrichelt. */
            [data-part="edge"] {
                stroke: oklch(from var(--ink-quiet) l c h / 0.5);
                stroke-width: 1px;
            }

            [data-part="path"] {
                stroke: oklch(from var(--link) l c h / 0.7);
                stroke-width: 1.5px;
            }

            line[data-rueck] { stroke-dasharray: 4 3; }

            /* Die Trennung der Gebiete: gestrichelt, leiser als die Linien,
               ueber die ganze Breite ({T0073S7}, Owner). */
            [data-part="band"] {
                stroke: oklch(from var(--ink-quiet) l c h / 0.45);
                stroke-width: 1px;
                stroke-dasharray: 2 4;
            }

            /* Die Menge an der Linie: kleine Schrift mit dunklem Saum, damit
               sie auf der Linie und auf dem Verlauf lesbar bleibt. */
            text {
                fill: var(--ink-bright);
                font-size: var(--text-micro);
                font-weight: 500;
                text-anchor: middle;
                dominant-baseline: middle;
                paint-order: stroke;
                stroke: var(--surface-0);
                stroke-width: 3px;
                stroke-linejoin: round;
            }

            /* Der Name des Gebiets: senkrecht in der Rinne links, neben dem
               Goldstreifen, von der Oberkante seines Bands abwaerts - eine
               Zeitachse am Rand. Waagerecht im Band stand er dort, wo die
               Mengen der letzten Reihe des Bands darueber stehen. */
            /* Waagerecht in der oberen linken Ecke des Bands ({T0073S11}): mit
               drei Spalten ist links neben der Rohstoffspalte Platz, und ein
               Band von einer Zeile ist zu kurz fuer einen senkrechten Namen -
               »Sumpfland« lief ins Gebirge. */
            text[data-part="bandname"] {
                text-anchor: start;
                dominant-baseline: hanging;
                translate: 0.4rem 0.3rem;
                fill: var(--gold);
                font-size: var(--text-label);
                font-weight: 400;
                letter-spacing: 0.08em;
                text-transform: uppercase;
            }

            /* Zwei Mengen an einer Zutat, deren Linien in der Luecke noch
               fast zusammenlaufen: die linke steht links ihrer Linie, die
               rechte rechts - ein Drittel Geviert Abstand zur Linie. */
            text[data-anker="end"]   { text-anchor: end;   translate: -0.35em 0; }
            text[data-anker="start"] { text-anchor: start; translate:  0.35em 0; }
        }

        /* Das Raster der Knoten: drei Spalten - Rohstoffe, Stationen,
           Erzeugnisse ({T0073S11}) -, eine Zeile je Reihe. Zeile und Spalte
           eines Knotens stehen an ihm als Custom Properties - ein
           typisiertes attr() truege Safari nicht (platform-features.md). */
        [data-part="nodes"] {
            display: grid;
            grid-template-columns: repeat(3, minmax(0, 1fr));
            grid-template-rows: repeat(var(--reihen), var(--reihe));
        }

        /* Das Mass der Plakette: 2.75 rem, auf schmaler Flaeche ein Anteil
           von ihr. **An den Kindern der Flaeche, nicht an ihr selbst**: `cqi`
           loest gegen den naechsten Vorfahren mit `container-type` auf, und
           das ist die Flaeche - ein Element kann seine eigene Containergroesse
           nicht lesen. Beide Kinder brauchen es, die Knoten und die Legende
           ({T0073S15}): dort stand `var(--kreis)` ohne Wert, und die Zeichen
           blieben winzig. */
        [data-part="nodes"],
        [data-part="legend"] {
            --kreis: min(2.75rem, 14cqi);
        }

        [data-part="node"] {
            grid-row: var(--r);
            grid-column: var(--c);
            display: grid;
            justify-items: center;
            align-content: center;
            row-gap: 0.2rem;
            min-inline-size: 0;
            padding-inline: 0.15rem;
            color: inherit;
            text-decoration: none;
            /* Ein Knoten fuellt seine Rasterzelle, sichtbar sind nur Kreis
               und Name: der Zeiger trifft den Knopf also auch in der Luft
               daneben, und das Blatt ging beim Fahren ueber die Flaeche auf
               ({T0073S9}, Owner). Der Knoten selbst nimmt keine Zeiger an,
               seine Kinder schon - ein Klick auf Kreis oder Name bleibt ein
               Klick auf den Knopf. */
            pointer-events: none;

            > * { pointer-events: auto; }

            /* Die Plakette ({T0073S13}, Owner mit Bild): der Rand grau wie
               die Linien - Gold traegt nur noch das Eck -, gruen beim Ziel;
               der Grund die Farbe des Gebiets, aus dem der Gegenstand kommt
               ({T0073S11}) - nicht des Bands, in dem er steht. Ohne Gebiet
               die erhoehte Flaeche. */
            [data-part="mark"] {
                --rand: var(--ink-quiet);
                position: relative;
                display: grid;
                place-items: center;
                inline-size: var(--kreis);
                block-size: var(--kreis);
                border-radius: 50%;
                border: 2px solid var(--rand);
                background: color-mix(in oklab, var(--biome, var(--surface-4)) 60%, var(--surface-2));
                box-shadow: 0 0 0 3px oklch(from var(--surface-0) l c h / 0.6);

                [data-type="game-icon"] {
                    inline-size: 72%;
                    block-size: 72%;
                    image-rendering: pixelated;
                }
            }

            b {
                font-size: var(--text-micro);
                font-weight: 500;
                line-height: 1.15;
                text-align: center;
                color: var(--ink-bright);
                overflow-wrap: anywhere;
                max-inline-size: 100%;
                /* Zwei Zeilen, dann Punkte: ein Name, der nicht passt, laeuft
                   nicht in die naechste Reihe. */
                display: -webkit-box;
                -webkit-box-orient: vertical;
                -webkit-line-clamp: 2;
                overflow: hidden;
            }
        }

        /* **Die goldene Ecke: das Zeichen fuer »poppt auf«** ({T0073S13},
           Owner mit Bild). Dieselbe Sprache wie an der Kachel der Items-Seite
           ({K0054}) - hier im Mass der Plakette statt in festen Pixeln, weil
           sie mit der Flaeche waechst. Die Ecke oben rechts wird dafuer
           eckig, das Eck legt sich ueber die graue Kante. Nur ein Knoten mit
           Blatt ist ein <button>, also traegt nur er sie. */
        button[data-part="node"] [data-part="mark"] {
            border-start-end-radius: 0;
        }

        button[data-part="node"] [data-part="mark"]::after {
            content: "";
            position: absolute;
            inset-block-start: -2px;
            inset-inline-end: -2px;
            inline-size: calc(var(--kreis) * 0.3);
            block-size: calc(var(--kreis) * 0.3);
            border-block-start: calc(var(--kreis) * 0.09) solid var(--gold);
            border-inline-end: calc(var(--kreis) * 0.09) solid var(--gold);
            pointer-events: none;
        }

        button[data-part="node"]:where(:hover, :focus-visible) [data-part="mark"]::after {
            border-color: oklch(from var(--gold) calc(l + 0.08) c h);
        }

        /* Das Ziel: gruen, die Farbe der Aktion - es ist, worum es geht. */
        [data-part="node"][data-role="target"] [data-part="mark"] {
            --rand: var(--link);
        }

        /* Die Farbe des Gebiets am Knoten, dieselbe Zuordnung wie an den
           Baendern und in der Itemliste. */
        [data-part="node"][data-biome="meadows"]     { --biome: var(--biome-meadows); }
        [data-part="node"][data-biome="blackforest"] { --biome: var(--biome-forest); }
        [data-part="node"][data-biome="swamp"]       { --biome: var(--biome-swamp); }
        [data-part="node"][data-biome="mountain"]    { --biome: var(--biome-mountain); }
        [data-part="node"][data-biome="plains"]      { --biome: var(--biome-plains); }
        [data-part="node"][data-biome="mistlands"]   { --biome: var(--biome-mistlands); }
        [data-part="node"][data-biome="ashlands"]    { --biome: var(--biome-ashlands); }
        [data-part="node"][data-biome="deepnorth"]   { --biome: var(--biome-deepnorth); }
        [data-part="node"][data-biome="ocean"]       { --biome: var(--biome-ocean); }

        /* Ein Gegenstand ist ein Knopf auf sein Blatt, ohne Blatt ein
           Verweis auf seine Seite ({T0073S8}). Der Knopf tritt als Knoten auf
           und nicht als Knopf des Hauses: keine Flaeche, keine Kante. */
        button[data-part="node"] {
            appearance: none;
            border: 0;
            background: none;
            font: inherit;
            cursor: pointer;
        }

        /* Beim Zeigen hebt sich der Name, und der Fokus liegt auf dem
           Kreis. */
        :is(a, button)[data-part="node"]:where(:hover, :focus-visible) b {
            text-decoration: underline;
            text-underline-offset: 0.15em;
        }

        :is(a, button)[data-part="node"]:focus-visible {
            outline: none;

            [data-part="mark"] { outline: 2px solid var(--link); outline-offset: 2px; }
        }

        /* In der Leiste poppt jetzt etwas: die goldene Ecke bleibt. Die
           Regel dagegen aus {T0073S4} ist damit hinfaellig; sie steht oben
           nicht mehr. */

        /* Die Station: ein Kasten statt eines Kreises, weil das Spiel fuer
           sie kein Symbol liefert. Der Name steht darin, die Stufe darunter
           als eigene Zeile. */
        [data-part="node"][data-role="station"] [data-part="mark"] {
            background: color-mix(in oklab, var(--ink-quiet) 12%, var(--surface-2));
            inline-size: auto;
            min-inline-size: var(--kreis);
            max-inline-size: 100%;
            block-size: auto;
            min-block-size: var(--kreis);
            /* Schmal in der Breite: bei sechs Kaesten in einer Reihe am
               Telefon bleiben 60 px, und »Werkbank« brach mit 0.5 rem
               Innenabstand mitten im Wort. */
            padding: 0.3rem 0.3rem;
            border-radius: 0.375rem;
            font-size: var(--text-micro);
            font-weight: 500;
            line-height: 1.15;
            text-align: center;
            color: var(--ink-bright);
            overflow-wrap: anywhere;

            i {
                display: block;
                font-style: normal;
                font-size: var(--text-label);
                color: var(--gold);
                letter-spacing: 0.04em;
            }
        }

        /* Die Erweiterung: ein kleiner gestrichelter Kreis - sie ist kein
           Ding, das man in die Hand nimmt, sondern etwas, das an der Station
           haengt. */
        [data-part="node"][data-role="extension"] [data-part="mark"] {
            background: color-mix(in oklab, var(--ink-quiet) 12%, var(--surface-2));
            inline-size: calc(var(--kreis) / 2);
            block-size: calc(var(--kreis) / 2);
            border-style: dashed;
            box-shadow: none;
        }

        /* ---- Das Ergebnis ({T0073S14}) --------------------------------- */

        /* Die Kachel des Ziels unter dem Baum, mittig und so breit wie auf
           der Items-Seite: 279 px, in rem, damit sie mit der Schrift waechst.
           Auf einer schmalen Flaeche nimmt sie, was da ist. */
        [data-part="result"] {
            display: grid;
            justify-items: center;
            justify-content: center;
            row-gap: 0.35rem;
            padding-block-start: 0.5rem;

            [data-type="item-tile"] { inline-size: min(17.4375rem, 100%); }
        }

        [data-part="result-head"] {
            margin: 0;
            color: var(--gold);
            letter-spacing: 0.06em;
            text-transform: uppercase;
            font-size: var(--text-label);
        }

        /* ---- Die Rechnung ({T0073}) --------------------------------- */

        /* Quer unter dem Baum, wie der Owner sie skizziert hat: Kopf mit dem
           Zaehlfeld, darunter die Posten in einem Fluss, unten der Hinweis.
           Ein Rahmen oben trennt sie vom Baum - sie ist ein eigener Zettel und
           nicht die letzte Reihe des Plans. */
        [data-part="bill"] {
            display: grid;
            row-gap: 0.5rem;
            margin-block-start: 0.75rem;
            padding-block-start: 0.75rem;
            border-block-start: 1px solid oklch(from var(--ink-quiet) l c h / 0.35);
        }

        [data-part="bill-head"] {
            display: flex;
            align-items: baseline;
            justify-content: space-between;
            gap: 1rem;
            flex-wrap: wrap;

            > p {
                margin: 0;
                color: var(--gold);
                letter-spacing: 0.06em;
                text-transform: uppercase;
                font-size: var(--text-label);
            }
        }

        /* Das Zaehlfeld. Schmal genug fuer drei Ziffern und keinen Zeichen
           mehr - max="999" steht am Element, die Breite sagt dasselbe noch
           einmal mit den Augen. */
        [data-part="bill-count"] {
            display: flex;
            align-items: center;
            gap: 0.5rem;
            font-size: var(--text-small);
            color: var(--ink-quiet);

            input {
                inline-size: 4.5rem;
                padding: 0.2rem 0.4rem;
                font: inherit;
                font-variant-numeric: tabular-nums;
                color: var(--ink);
                background: var(--surface-2);
                border: 1px solid oklch(from var(--ink-quiet) l c h / 0.35);
                border-radius: 0.25rem;
            }

            input:focus-visible { outline: 2px solid var(--link); outline-offset: 1px; }
        }

        /* Die Posten in einem Raster, das sich selbst fuellt: am Telefon zwei
           Spalten, am Schreibtisch so viele, wie die Flaeche traegt. Kein
           auto-flow column - die Rechnung waechst mit dem Ziel, und ein
           Bronzeschwert hat drei Posten, ein Langhaus dreissig. */
        [data-part="bill-items"] {
            display: grid;
            /* Gedeckelte Spuren und links buendig: mit `1fr` zog eine Rechnung
               aus zwei Posten sie ueber 1300 px auseinander, und die Zahl stand
               eine Handbreit vom Namen. 20 rem ist die Breite, ab der eine
               Zeile aus Bild, Namen und Zahl zu zerfallen beginnt. */
            grid-template-columns: repeat(auto-fill, minmax(min(13rem, 100%), 20rem));
            justify-content: start;
            gap: 0.35rem 1rem;
            margin: 0;
            padding: 0;
            list-style: none;

            a {
                display: flex;
                align-items: center;
                gap: 0.5rem;
                padding: 0.25rem 0.4rem;
                border-radius: 0.25rem;
                color: inherit;
                text-decoration: none;
                font-size: var(--text-small);
            }

            a:hover, a:focus-visible { background: var(--surface-2); }

            [data-type="game-icon"] {
                flex: none;
                inline-size: 1.75rem;
                block-size: 1.75rem;
            }
        }

        /* Der Name gibt nach, die Zahl nie: sie steht rechts an der Kante,
           mit Tabellenziffern, damit die Spalte bei jeder Stueckzahl ruhig
           bleibt. */
        [data-part="bill-name"] {
            flex: 0 1 auto;
            min-inline-size: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        /* Die Fuehrungspunkte einer Speisekarte (Owner: »wie in einem
           restaurant«). Sie sind der Grund, warum die Luecke zwischen Namen
           und Zahl wie Absicht aussieht und nicht wie ein Satzfehler - und sie
           dehnen sich, damit die Zahlen einer Spalte untereinander stehen. */
        [data-part="bill-dots"] {
            flex: 1 1 auto;
            min-inline-size: 0.75rem;
            align-self: center;
            block-size: 0;
            border-block-end: 1px dotted oklch(from var(--ink-quiet) l c h / 0.55);
        }

        [data-part="bill-amount"] {
            flex: none;
            color: var(--gold);
            font-variant-numeric: tabular-nums;
        }

        [data-part="bill-hint"] {
            margin: 0;
            font-size: var(--text-small);
            color: var(--ink-quiet);
        }

        /* ---- Die Legende ({T0073S15}) ---------------------------------- */

        /* Zwei Zeilen, spaltenweise gelesen (Owner mit Skizze): Ziel und
           Noetig, Station und Erweiterung, der Weg und die Zutat - und rechts
           ueber beide Zeilen die Plakette in voller Groesse. `auto-flow:
           column` fuellt Spalte fuer Spalte; auf schmaler Flaeche darf sie
           umbrechen, dann stehen die Paare untereinander. */
        [data-part="legend"] {
            display: grid;
            row-gap: 0.5rem;
            padding-block-start: 0.25rem;
        }

        [data-part="legend-head"] {
            margin: 0;
            color: var(--gold);
            letter-spacing: 0.06em;
            text-transform: uppercase;
            font-size: var(--text-label);
        }

        /* Am Telefon umbrechende Spalten, ab 44 rem Flaechenbreite die zwei
           Zeilen des Owners: mit `auto-flow: column` allein stand die Legende
           auf 664 px und schob die Seite auf (Breitenlauf, AW20). Eine
           Container-Abfrage, keine Fensterstufe - die Legende antwortet auf
           die Flaeche, in der sie steht (AW6). */
        [data-part="legend-items"] {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(min(12rem, 100%), 1fr));
            gap: 0.5rem 2rem;
            font-size: var(--text-small);
            color: var(--ink-quiet);

            > span {
                display: flex;
                align-items: center;
                gap: 0.5rem;
                min-block-size: 1.6rem;
            }

            /* Die Zeichen im Mass der Plakette, halbiert: gross genug, dass
               Form und Farbe zu erkennen sind (Owner), klein genug fuer eine
               Textzeile. */
            i {
                flex: none;
                inline-size: calc(var(--kreis) / 2);
                block-size: calc(var(--kreis) / 2);
                border-radius: 50%;
                border: 2px solid var(--ink-quiet);
                background: var(--surface-2);
            }

            [data-role="target"] i { border-color: var(--link); }
            [data-role="station"] i { border-radius: 0.25em; }
            [data-role="extension"] i {
                border-style: dashed;
                inline-size: calc(var(--kreis) / 3);
                block-size: calc(var(--kreis) / 3);
            }

            /* Die Linien: ein Strich in ihrer Farbe und ihrer Staerke. */
            [data-role="step"] i,
            [data-role="path"] i {
                inline-size: calc(var(--kreis) * 0.9);
                block-size: 0;
                border: 0;
                background: none;
                border-block-start: 2px solid oklch(from var(--ink-quiet) l c h / 0.7);
                border-radius: 0;
            }

            [data-role="path"] i {
                border-block-start-width: 3px;
                border-block-start-color: oklch(from var(--link) l c h / 0.9);
            }

            /* **Die Plakette in voller Groesse** (Owner: »das vollgrosse
               Plakette mit sichtbaren Farben«): derselbe Grund wie an einem
               Knoten - 60 Prozent Gebietsfarbe auf der erhoehten Flaeche -,
               nur aus zwei Gebieten an scharfer Kante, damit man den Wechsel
               als Farbe liest und nicht als ein Gebiet. Sie spannt beide
               Zeilen. */
            [data-role="biome"] {
                align-items: center;
            }

            [data-role="biome"] i {
                inline-size: var(--kreis);
                block-size: var(--kreis);
                border-color: var(--ink-quiet);
                border-start-end-radius: 0;
                background: linear-gradient(100deg,
                    color-mix(in oklab, var(--biome-meadows) 60%, var(--surface-2)) 50%,
                    color-mix(in oklab, var(--biome-mistlands) 60%, var(--surface-2)) 50%);
            }
        }

        @container (min-width: 44rem) {
            [data-part="legend-items"] {
                grid-template-columns: none;
                grid-template-rows: repeat(2, auto);
                grid-auto-flow: column;
                grid-auto-columns: max-content;
                justify-content: start;

                /* Die Plakette ueber beide Zeilen, als letzte Spalte. */
                [data-role="biome"] { grid-row: span 2; }
            }
        }

        /* Ab tablet: die Leiste links, die Flaeche rechts, beide in einer
           Zeile - nur die Zuweisung aendert sich ({K0009}). */
        @media (min-width: 35rem) {
            [data-part="board"] { padding: 1.25rem; }
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Die Kartenseite ist ein Bildschirm, keine Seite ({T0026S3},
   engine/spec/karte-programm.md §3): Kopf, Fenster, Fuß in drei Zeilen auf der
   Höhe des Fensters; das Fenster nimmt, was bleibt, und die Seite rollt nicht.
   dvb, weil die Leisten der Telefonbrowser mitwachsen sollen — Blockachse, wie
   die Regel es erlaubt. */

@layer layouts.karte {
    @scope ([data-layout="karte"]) {
        :scope {
            display: grid;
            grid-template-rows: auto minmax(0, 1fr) auto;
            block-size: 100dvb;
            overflow: hidden;
        }

        /* main und das Band darin dürfen kleiner werden als ihr Inhalt —
           sonst wächst das Bild die Zeile und mit ihr die Seite.

           **Zwei Zeilen seit {T0095}: die Bühne, dann das Fenster.** Die
           Bühne zog mit der Umstellung aus dem Kopf in den Inhalt (Owner:
           »der hero block ist teil des contents nicht des headers«), und mit
           nur einer Zeile teilten sich beide dieselbe — die Bühne stand
           gemessen 65 Punkte hoch statt 159. `auto` gibt ihr, was sie
           braucht; das Fenster nimmt weiterhin, was bleibt. */
        [data-region="content"] {
            display: grid;
            grid-template-rows: auto minmax(0, 1fr);
            min-block-size: 0;
        }

        /* **Die Zeile wird zugewiesen, nicht gezaehlt.** Im Vollbild faellt
           die Buehne mit `display: none` weg, und ein Rasterkind zaehlt dann
           nicht mehr mit: das Kartenband rutschte in die erste Zeile und
           bekam `auto` statt `1fr` — gemessen 408 Punkte statt 898, die
           Karte gestaucht und darunter schwarzer Rest. Mit fester Zuweisung
           bleibt es in Zeile zwei, ob die Buehne dasteht oder nicht; ist sie
           weg, ist Zeile eins null hoch und Zeile zwei bekommt alles. */
        [data-region="content"] > [data-type="header-banner"] { grid-row: 1; }
        [data-region="content"] > [data-slice="map"]          { grid-row: 2; }
    }
}

/* Am Telefon gehört der Platz der Karte: **der Fuß** entfällt auf dieser
   einen Seite, und das Fenster wächst um seine Höhe (Owner, 3. September
   2026: »in der mobilen Ansicht würde den orangen Header nach oben und den
   Footer nach unten wegschieben, dann fixieren und die Karte im neuen
   größeren Bereich anzeigen«). Der schmale Kopf mit Logo, Sprache und Menü
   bleibt — ohne ihn käme man nirgends hin.

   **Die Bühne stand hier bis {T0100} mit** und steht es nicht mehr: seit die
   Karte immer die volle Ausdehnung hat, entfällt sie auf jeder Breite, und
   diese Abfrage hätte sie ein zweites Mal ausgeblendet. Eine Regel, die
   dasselbe zweimal sagt, ist die, bei der später eine von beiden falsch
   wird.

   GEFANGENE STUFE — die eine zulässige Abfrage nach unten ({K0149}, AW7).
   Die Ansicht gehört dieser Stufe exklusiv und kehrt oben in keiner Form
   wieder: oberhalb steht die Seite wie überall.

   Die Prüfreihenfolge ist durchlaufen, und beide Stufen davor tragen nicht:

   1  Rasterzuweisung. Sie trüge, wenn beide eigene Zeilen der umordnenden
      Ebene wären — sind sie nicht. Die Bühnenleiste ist ein Kind des Kopfes
      und keine Zeile des Seitenrasters, und ein Fuß ohne Area landet in
      einer impliziten Spur statt zu verschwinden.
   2  Mobile first mit min. Hieße: hier verstecken, ab 35rem wiederherstellen
      — und dabei den Anzeigetyp kennen. Die Bühnenleiste ist ein Raster, der
      Fuß auch, und display: revert wüßte nicht, welches. Genau diese
      Doppelführung stand hier einmal binnen einer Stunde auf beiden Werten
      und war beide Male einmal falsch ({K0099}). */
@layer layouts.karte {
    @scope ([data-layout="karte"]) {
        /* GEFANGENE STUFE, 34.99rem, exklusiv fuers Telefon — oben kehrt die
           Regel in keiner Form wieder. Die Pruefreihenfolge und warum die
           beiden Stufen davor nicht tragen, steht im Kommentar ueber diesem
           Block; hier wiederholt, weil AW7 vier Zeilen weit sucht. */
        @media (max-width: 34.99rem) {
            [data-slice="page-footer"] {
                display: none;
            }
        }
    }
}

/* **Die Karte nimmt die Seite — und zwar immer** ({T0100}, Owner am
   12. September 2026: »es gibt nur noch die volle ausdehnung version -
   direkt wenn man den link klickt«). Sie nimmt alles unter dem Kopf, ueber
   die volle Fensterbreite, und Titel und Leiste legen sich darauf statt
   darueber.

   **Es gab bis dahin zwei Ansichten und einen Schalter dazwischen**
   ({T0028S1}, 5. September 2026). Das ist zurueckgebaut, und der Anlass war
   kein Geschmack: seit die Filterleiste mit {T0093} zwei Reglerreihen traegt,
   nahm das Panel in der kleinen Ansicht 423 von 580 Punkten des Kartenbands.
   Bei 1000 Punkten Fensterhoehe blieben der Karte 82, bei 900 **null** — und
   bei 640 stand auch der Vergroesserungsknopf ausserhalb des Bildes. Wer auf
   einem kleinen Schirm kam, sah keine Karte und hatte keinen Weg zu einer.

   Der Rueckbau macht die Regeln einfacher, nicht komplizierter: sie standen
   als Zweig hinter `:has(#karte-voll:checked)` und sind jetzt der
   Normalzustand. Eine Ansicht, ein Zustand, kein Gedaechtnis.

   **Umgeordnet, nicht neu gebaut.** Es gibt kein zweites Template und keine
   zweite Route: dieselben Knoten bekommen eine andere Zuweisung, wie {K0009}
   es verlangt. */
@layer layouts.karte {
    @scope ([data-layout="karte"]) {
        /* Ohne die Buehne, auf jeder Breite ({T0070}, Owner am 8. September
           2026 mit Bildschirmabzug: »den orangen Hero-Titel auch bei Tablet
           und Screen ausblenden im Vollbildmodus, analog mobile«). Sie ist
           auf dieser einen Seite nicht das Bild, sondern der Platz, den die
           Karte braucht. Der schmale Kopf mit Logo, Sprache und Menue bleibt.

           Die gefangene Stufe oben ({K0149}) sagte dasselbe fuer das Telefon
           und war bis {T0100} der zweite von zwei Schaltern. Jetzt gilt diese
           Regel auf jeder Breite, und die Abfrage dort traegt nur noch den
           Fuss. */
        [data-type="header-banner"] {
            display: none;
        }
        [data-slice="map"] {
            /* Eine Zeile statt drei: Titel, Leiste und Fenster teilen sie
               sich, und wer oben liegt, sagt die Reihenfolge im Baum. */
            grid-template-rows: minmax(0, 1fr);
            row-gap: 0;
            padding-block: 0;
        }

        /* Randlos ueber die volle Breite - die benannte Spur des Slices
           heisst full ({K0031}); ohne sie stuende die Karte weiter im
           Inhaltsband. */
        [data-slice="map"] > * {
            grid-column: full;
            grid-row: 1;
        }

        /* **Zwei Spalten: links der Inhalt, rechts der Anfasser**
           (Owner, 5. September 2026). Die zweite Spalte ist genau so breit
           wie der Anfasser, und er fuellt sie ganz — in Breite und Hoehe.
           Damit haengt keine Spalte mehr am Inhalt, und nichts kann sie
           auseinanderschieben.

           Das Panel selbst traegt die Breite: 42rem waren die volle Zeile
           aus zwanzig Zeichen Pixelschrift, dazu der Stern dahinter, der
           Rand und der Anfasser — gemessen 560 + 30 + 6 + 32 + 36. Ein
           laengerer Seed bricht um statt zu kuerzen ({T0028S3} QS); am
           schmalen Fenster gilt der Platz.

           **Seit {T0093} sechs rem mehr** (Owner, 11. September 2026:
           »bitte das panel um 100px breite auf dem desktop«). Die
           Rechnung oben bleibt die Untergrenze und wird nicht
           unterschritten; der Zuwachs geht ganz an die Reglerspalten, die
           mit der zweiten Reihe eng geworden waren — »Hoher Norden« brach
           um. 48 statt 48,25 rem, weil die Leiter in ganzen Stufen laeuft:
           vier Pixel unter der genannten Zahl und dafuer eine, die man
           ohne Taschenrechner wiederfindet.

           Gefahren wird ueber translate, nie ueber Breite oder inset — nur
           das laesst der Compositor allein laufen (AW18). Der Weg ist
           `Anfasserbreite minus 100 Prozent minus Rand`: Prozent bezieht
           sich bei translate auf die eigene Groesse, also faehrt genau der
           Inhalt hinaus und der Anfasser bleibt an der Kante. */
        [data-slice="map"] > [data-part="panel"] {
            --lasche: 2.25rem;
            display: grid;
            /* Zwei Spalten, **eine** Zeile. Der Anfasser ueberspannt nichts
               mehr - er hat seine Zelle und fuellt sie. Genau das war der
               Bruch: `grid-row: 1 / -1` zaehlt explizite Linien, und sobald
               die Zeilen implizit wurden, endete er nach der ersten. */
            /* Drei Spalten seit {T0106}: Inhalt, Streifen, Anfasser. Der
               Streifen ist offen null breit und eingefahren 6,25 rem - die
               hundert Pixel des Owners bei sechzehn Pixel Grundschrift, in
               rem, weil AW16 kein px kennt. Die Spalte wechselt beim
               Klappen, nicht animiert: animiert wird nur translate (AW18). */
            --schnell: 0rem;
            grid-template-columns: minmax(0, 1fr) var(--schnell) var(--lasche);
            place-self: start start;
            z-index: 3;
            /* `100%` und nicht `100vi` ({T0100}, AW16). Die
               Viewport-Einheit rechnet ohne Bildlaufleiste: bei 430 Punkten
               Fensterbreite kam das Panel auf 421 und der Slice endete bei
               415 — sechs Punkte hinaus, gemessen. Prozent bezieht sich auf
               den Slice, der die Leiste schon abgezogen hat. Genau der Fall,
               den AW16 mit »laufen deshalb am Schreibtisch alle gleich ueber«
               beschreibt; die Zeile stand hier seit {T0028} und fiel erst
               auf, als das Panel auf jeder Breite schwebt. */
            inline-size: min(48rem, calc(100% - 2 * var(--space-close)));
            margin-block-start: var(--space-close);
            margin-inline-start: var(--space-close);
            transition: translate 280ms ease;
            position: relative;
            /* **Fünfundachtzig Prozent, nicht fünfzig** ({T0100}). Das Panel
               schwebt über der Karte, und die ist hell und gemustert: bei
               halber Deckkraft misst das goldene »ORTE« 2,14 zu 1 und die
               Hinweiszeile 2,52 — nötig sind 4,5 (AW3). Das war im alten
               Vollbildmodus schon so und fiel nicht auf, weil ihn niemand
               sehen musste; seit er der einzige ist, gilt es für jeden.

               Die Karte bleibt dahinter sichtbar — es ist eine Scheibe, kein
               Deckel —, aber sie traegt den Text nicht mehr mit. */
            background: oklch(8% 0.008 260 / 0.85);
            box-shadow:
                inset  2px  2px 0 oklch(33% 0.014 var(--hue-surface)),
                inset -2px -2px 0 oklch(12% 0.010 var(--hue-surface));
        }

        /* Die goldene Ecke, unten rechts — dieselbe Sprache wie an der
           Gegenstandskachel, nur an der anderen Ecke ({K0054}: beim dritten
           Aufrufer wird ein Element daraus). Sie haengt am Behaelter und
           faehrt mit ihm. */
        [data-slice="map"] > [data-part="panel"]::after {
            content: "";
            position: absolute;
            inset-block-end: 0;
            inset-inline-end: 0;
            inline-size: 15px;
            block-size: 15px;
            border-block-end: 5px solid var(--gold);
            border-inline-end: 5px solid var(--gold);
            pointer-events: none;
        }

        /* **Der Inhalt ist eine Box mit eigenem Raster**: eine Spalte, die
           `content` heisst. In der kleinen Ansicht sind seine Kinder
           Rasterkinder des Bandes und stehen in dessen Spur `content`;
           hier stehen sie in seiner eigenen, die genauso heisst. Ein
           Liniename, den es im Raster nicht gibt, wuerde sonst mit einer
           impliziten Spur beantwortet - gemessen `76px 36px 0px 560px`,
           und das Panel zerfiel. Jede Box hat ihr Raster, und jede Regel
           findet ihre Spur. */
        [data-part="panel"] > [data-part="inhalt"] {
            grid-column: 1;
            min-inline-size: 0;
            display: grid;
            grid-template-columns: [content-start] minmax(0, 1fr) [content-end];
        }

        [data-part="inhalt"] > [data-part="titelzeile"] {
            padding: var(--space-close) var(--space-close) var(--space-snug);
        }

        [data-part="inhalt"] > [data-type="control-bar"] {
            inline-size: auto;
            /* Der Bedienstreifen legt Suche und Regler nebeneinander; hier
               gibt es keine Suche, also nimmt der Reglerblock die Breite. */
            display: block;
            background: none;
            box-shadow: none;
            padding: 0 var(--space-close) var(--space-close);
        }

        /* Der Anfasser hat seine Zelle und fuellt sie - `stretch` ist im
           Raster der Ruhewert, es braucht weder Hoehe noch Spanne. */
        [data-part="panel"] > [data-type="map-tab"] {
            grid-column: 3;
            align-self: stretch;
            display: grid;
            place-items: center;
            /* Schutzrand ueber und unter dem Text ({T0108}): eingefahren gibt
               die Lasche die Hoehe des Panels vor, und ohne Rand stiess der
               Text an beide Kanten. */
            padding-block: var(--space-close);
            cursor: pointer;
            background: oklch(20% 0.008 260 / 0.62);
            box-shadow:
                inset  2px  2px 0 oklch(38% 0.014 var(--hue-surface)),
                inset -2px -2px 0 oklch(14% 0.010 var(--hue-surface));
        }

        [data-part="panel"] > [data-type="map-tab"]:hover {
            background: color-mix(in oklch, var(--gold) 10%, oklch(20% 0.008 260 / 0.62));
            box-shadow:
                inset  2px  2px 0 oklch(14% 0.010 var(--hue-surface)),
                inset -2px -2px 0 oklch(38% 0.014 var(--hue-surface));
        }

        [data-type="map-tab"] input {
            grid-area: 1 / 1;
            inline-size: 100%;
            block-size: 100%;
            margin: 0;
            appearance: none;
            opacity: 0;
            cursor: pointer;
        }

        [data-type="map-tab"]:has(:focus-visible) {
            outline: 2px solid var(--link);
            outline-offset: -2px;
        }

        [data-type="map-tab"] [data-part="wort"] {
            grid-area: 1 / 1;
            writing-mode: vertical-rl;
            rotate: 180deg;
            white-space: nowrap;
            font-size: var(--text-small);
            letter-spacing: 0.06em;
            color: var(--gold);
            text-shadow: 1px 1px 0 oklch(0% 0 0 / 0.9), -1px 1px 0 oklch(0% 0 0 / 0.9);
            pointer-events: none;
        }

        /* **Die Schalter sind eine umbrechende Flex-Reihe** (Owner,
           5. September 2026, zum zweiten Mal empfohlen und diesmal auch am
           Telefon gemessen). `auto-fit` mit zehn rem Mindestspalte liess
           dort eine Spalte uebrig und acht Zeilen mit leerem Rand.

           Jeder Schalter hat dieselbe Grundlage von acht rem und darf
           wachsen: was in eine Zeile passt, teilt sie sich gleich, und
           weil acht Schalter sich am Telefon in zwei und am Schreibtisch
           in vier je Zeile ordnen, stehen sie in beiden Faellen in Spalten
           uebereinander. Keine Abfrage, keine Spaltenzahl je Stufe. */
        /* Seit {T0093} ordnet das <fieldset> die Schalter und nicht mehr
           das Formular: `> *` haette sonst das fieldset selbst getroffen
           und die Schalter darin gar nicht mehr. */
        [data-part="panel"] [data-part="gruppe"] {
            display: flex;
            flex-wrap: wrap;
            gap: 0.5rem 1.25rem;
        }

        [data-part="panel"] [data-part="gruppe"] > [data-type="checkbox"] {
            flex: 1 1 8rem;
        }

        [data-part="panel"] [data-type="checkbox"] {
            color: var(--ink-bright);
            text-shadow: 1px 1px 0 oklch(0% 0 0 / 0.9), -1px 1px 0 oklch(0% 0 0 / 0.9);
        }

        [data-part="panel"] [data-part="titelzeile"] [data-type="heading"],
        [data-type="map-tab"] [data-part="wort"] {
            text-shadow: 1px 1px 0 oklch(0% 0 0 / 0.9), -1px 1px 0 oklch(0% 0 0 / 0.9);
        }

        [data-part="panel"] [data-type="seed-form"] {
            background: none;
            box-shadow: none;
            padding-inline: 0;
            padding-block-start: 0;
        }

        /* Der Streifen ({T0106}): offen nicht da, eingefahren die zweite
           Spalte, zwei Knoepfe uebereinander, oben beginnend. Der
           Hintergrund ist der des Panels, das ihn traegt - er braucht
           keinen eigenen. */
        [data-part="panel"] > [data-part="schnell"] {
            grid-column: 2;
            display: none;
            align-content: start;
            justify-items: center;
            /* Aus dem Entwurf des Owners gemessen: Rand und Abstand je rund
               zehn Pixel, die Knoepfe fuellen den Rest des Streifens. */
            gap: var(--space-close);
            padding: var(--space-close);
            /* Halb so breit wie der Streifen abzueglich Rand, quadratisch,
               und nur so hoch wie das Zeichen braucht ({T0108}, Owner am
               14. September 2026: »halb so breit, auf die fuer den text
               notwendige hoehe, top left des panels bleibt gleich«). Die
               Zeilen sind so hoch wie ihr Inhalt und beginnen oben links -
               die erste Fassung liess zwei Knoepfe die ganze Hoehe teilen. */
            /* Die Knopfbreite ist gesetzt, der Streifen folgt ihr (unten in der
               Klappregel), nicht umgekehrt: 2,5 rem, halb so breit wie die
               erste Fassung, und der Streifen ist genau Knopf plus Rand
               (Owner, 14. September 2026: »das panel wird so breit wie die
               buttons ihn brauchen«). */
            --quick-size: 2.5rem;
            /* Die zwei Knoepfe teilen sich die Hoehe des eingefahrenen Panels
               in zwei gleichen Zeilen; die gibt die Lasche vor, denn der
               Inhalt ist eingefahren aus dem Fluss (unten). */
            grid-template-rows: 1fr 1fr;
            align-content: stretch;
            justify-items: start;
            --quick-block: 100%;
        }
        /* Zugeklappt: der Behaelter faehrt hinaus, bis Streifen und Anfasser
           bleiben ({T0106}; bis dahin nur der Anfasser). Der Inhalt wird
           dabei unsichtbar und unerreichbar - `visibility` nimmt ihn aus
           der Tastaturreihe, ohne das Raster zu aendern -, und zwar erst,
           wenn die Fahrt zu Ende ist, damit er nicht vor dem Panel
           verschwindet. Beim Oeffnen sofort. Der Stern der Titelzeile gibt
           seinen Ankernamen ab, damit das Blatt am sichtbaren Stern des
           Streifens haengt und nicht an einem, der hinausgefahren ist. */
        /* Keine Fahrt beim ersten Bild ({T0106}): bis das Skript nach dem
           ersten Bild data-bereit setzt, gibt es keinen Uebergang - der
           Zustand aus Adresse oder Speicher wird gesetzt, nicht gefahren.
           Danach animiert jeder Klick wie zuvor.

           **Der Zustand steht am Kartenabschnitt, nicht im Kaestchen**
           ({T0145}). Bis dahin lasen diese Regeln `:has(#karte-panel:checked)`
           vom Layout-Wurzelelement aus, dem body. Eine :has()-Regel am body
           macht jede Einfuegung und jede Entfernung im Dokument zu einer
           Neubewertung des Ankers, und ihre Invalidierungsmenge traegt die
           Merkmale der Subjekte - [data-type], [data-part], [data-slice] -,
           also jedes Element mit data-type: 8 900 Divs und 8 500 Spans je
           Durchlauf, bei jedem Marker, der beim Schwenken hereinkommt. Der
           Server rendert data-panel="zu" wie das Kaestchen, das Skript
           spiegelt jeden Klick (panelSetzen), data-bereit steht daneben. */
        [data-slice="map"]:not([data-bereit]) > [data-part="panel"] {
            transition: none;
        }
        [data-slice="map"][data-panel="zu"] > [data-part="panel"] {
            /* So breit wie ein Knopf plus Rand beiderseits ({T0108}); die
               hundert Pixel der ersten Fassung sind damit Geschichte. */
            --schnell: calc(2.5rem + 2 * var(--space-close));
            translate: calc(var(--lasche) + var(--schnell) - 100% - var(--space-close)) 0;
        }
        /* Eingefahren ist der Inhalt nicht nur unsichtbar, sondern aus dem
           Fluss ({T0108}, Owner am 14. September 2026: »das panel und auch
           die schaltflaeche im minimierten zustand nur auf die notwendige
           hoehe fuer den buttontext reduzieren«): mit `visibility` hielt er
           die ganze Hoehe, und der Streifen stand leer unter den Knoepfen.
           Ohne ihn gibt die Lasche mit ihrem Text die Hoehe vor. Der Wechsel
           ist hart, nicht gefahren - `display` kennt keinen Uebergang, und
           die Erlaubnisliste keinen weichen fuer ihn; das Panel wird beim
           Zuklappen erst kurz, dann faehrt es hinaus. */
        [data-slice="map"][data-panel="zu"] [data-part="panel"] > [data-part="inhalt"] {
            display: none;
        }
        [data-slice="map"][data-panel="zu"] [data-part="panel"] > [data-part="schnell"] {
            display: grid;
        }
        [data-slice="map"][data-panel="zu"] [data-part="titelzeile"] > [data-type="icon-button"] {
            anchor-name: none;
        }
        [data-slice="map"][data-panel="zu"] [data-part="schnell"] > [data-rolle="merken"] {
            anchor-name: --karte-stern;
        }

        @media (prefers-reduced-motion: reduce) {
            [data-slice="map"] > [data-part="panel"] { transition-duration: 1ms; }
        }

        /* Der Fuss schrumpft auf eine Zeile ({T0028S2}, Owner am
           5. September 2026): links der Name, mittig das Copyright, rechts
           die Spieleliste. Der Markenhinweis und die Technikzeile stehen
           hier nicht — der Owner hat beide im Bildschirmabzug
           durchgestrichen.

           Dieselben Knoten, andere Zuweisung: kein zweiter Fuss und keine
           Kopie eines Satzes. Zwei Fassungen desselben Textes waeren zwei
           Wahrheiten, und die Werkbanknamen haben das einmal gekostet
           ({K0102}).

           Die Regel steht in der Layer layouts und ueberschreibt damit die
           Tabletregel des Fussbandes in slices — die Reihenfolge steht in
           style.css und ist der Grund, warum hier keine Spezifitaet
           nachgeholfen werden muss.

           **Der Spendenknopf steht mit im Streifen** ({T0134}), gleich nach
           dem Namen und so niedrig, dass der Streifen nicht waechst: die
           Karte ist die Seite, auf der der Server sichtbar arbeitet, und
           gerade hier soll der Knopf nicht fehlen. Die Hoehe gibt der
           Kontext vor, die Form rechnet der Knopf selbst daraus. Seine
           Spalte ist die vierte; `align-items` setzt die Texte auf seine
           Mitte statt an die Oberkante. */
        [data-slice="page-footer"] {
            --spende-hoehe: 1.625rem;
            --spende-schrift: 0.75rem;
            padding-block: var(--space-tight);
            row-gap: 0;
            align-items: center;
            border-block-start: none;
            grid-template-areas: none;
            grid-template-columns:
                [full-start] var(--rand)
                [content-start] auto var(--space-close) auto var(--space-close)
                1fr var(--space-close) auto
                [content-end] var(--rand)
                [full-end];
        }

        /* Bis screen traegt die Zeile nur den Knopf und das Copyright. Die
           drei Texte zusammen sind 95 Zeichen; bei 560 Pixeln brechen sie um,
           und aus der einen Zeile werden vier — gemessen 142 statt 34 Pixeln.
           Mittig steht, was auf jeder Seite steht; Name und Spieleliste
           kommen dazu, sobald Platz ist. Der Knopf steht bis dahin vorn, wo
           sonst der Name steht. */
        [data-slice="page-footer"] > [data-type="prose"],
        [data-slice="page-footer"] > [data-type="page-footer-nav"] {
            display: none;
        }

        [data-slice="page-footer"] > [data-type="donate-button"] {
            grid-column: 2;
            grid-row: 1;
        }

        [data-slice="page-footer"] > [data-type="footer-legal"] {
            grid-column: 6;
            grid-row: 1;
            justify-self: center;
            margin-block-start: 0;
            margin-inline: 0;
        }

        /* In der Kartenansicht traegt die Mitte nur den Vermerk ({T0077},
           Owner am 9. September 2026). Markenhinweis und Bauzeile sind
           Saetze und keine Zeile; sie stehen auf jeder anderen Seite und
           fehlen hier nicht, weil die Karte das ganze Fenster fuellt und
           der Fuss ein Streifen darunter ist. Sie liegen eine Ebene tiefer
           als die Regel darueber greift — deshalb hier noch einmal. */
        [data-slice="page-footer"] [data-type="footer-legal"] [data-type="prose"] {
            display: none;
        }

        @media (min-width: 60rem) {
            [data-slice="page-footer"] > [data-type="prose"],
            [data-slice="page-footer"] > [data-type="page-footer-nav"] {
                display: block;
            }

            [data-slice="page-footer"] > [data-type="prose"] {
                grid-column: 2;
                grid-row: 1;
                justify-self: start;
            }

            [data-slice="page-footer"] > [data-type="donate-button"] {
                grid-column: 4;
            }

            [data-slice="page-footer"] > [data-type="page-footer-nav"] {
                grid-column: 8;
                grid-row: 1;
                justify-self: end;
            }
        }

        /* Was hier nicht steht, ist auch fuer den Vorleser weg: sichtbar
           und hoerbar stimmen ueberein (AW3). Auf jeder anderen Seite und
           in der kleinen Ansicht stehen beide Saetze unveraendert. */
        [data-slice="page-footer"] [data-type="footer-legal"] [data-type="prose"] {
            display: none;
        }
        }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Was allein auf der Einstiegsseite gilt. */

@layer layouts.landing {
    @scope ([data-layout="landing"]) {
        /* Fast nichts — und das ist der Punkt. Die Slices ordnen sich im
           einspaltigen Raster des Inhaltsbereichs von selbst, und jeder bringt
           seine Flaeche und seinen Innenraum mit. Was hier stuende, waere eine
           Ausnahme, und eine Ausnahme braucht einen Grund.

           Die Einleitung ist die einzige: sie traegt auf der Einstiegsseite mehr
           Luft als anderswo, weil ueber ihr die Buehnenleiste steht und darunter
           die Kacheln beginnen. */
        [data-slice="intro"] {
            padding-block: var(--space-wide);
        }
    }
}

/* Copyright 2026 Vivian Voss. Code by Vivian Voss. Licensed under the BSD-3-Clause.
   SPDX-License-Identifier: BSD-3-Clause
   Scope: Das Basisgerüst — drei Regionen übereinander, der Inhalt einspaltig. */

@layer templates.vga-page {
    @scope ([data-template="vga-page"]) {
        :scope {
            display: grid;
            grid-template-areas: "header" "content" "footer";
            grid-template-rows: auto 1fr auto;
            min-block-size: 100svb;
        }

        /* Zuordnung über das Element, nicht über eine Klasse: die Region ist eine
           Eigenschaft des Gerüsts, und der Slice, der sie füllt, soll nichts davon
           wissen müssen. Ein zweites Template ordnet dieselben Slices anders an,
           indem es allein die Areas ändert. */
        > header { grid-area: header; }
        > main   { grid-area: content; }
        > footer { grid-area: footer; }

        /* Der Inhaltsbereich ist ein Raster mit einer Spalte; jede Zeile darin ist
           ein Slice und so breit wie die Seite \({K0031}\). Kein gap: Slices stoßen
           aneinander, weil ihre Flächen sich abwechseln sollen — der Abstand
           entsteht im Slice selbst als Innenraum, nicht zwischen zweien als Lücke.
           Eine Lücke ließe den Grund der Seite durchscheinen und aus zwei Bändern
           drei machen. */
        > main {
            display: grid;
            grid-template-columns: 1fr;

            /* **Die Slices stehen oben, sie fuellen nicht** ({T0095}). Ein
               Raster ohne Zeilendefinition legt implizite `auto`-Zeilen an,
               und `align-content` steht dabei auf `normal` — was hier
               `stretch` heisst: ist der Inhalt kuerzer als die Seite, wird der
               Rest auf alle Zeilen verteilt. `min-block-size: 100svb` am
               Geruest sorgt dafuer, dass es diesen Rest fast immer gibt.

               Sichtbar wurde es, als die Buehne aus dem Kopf in den Inhalt
               zog: auf der Bauplanseite, die wenig traegt, stand sie 188
               Punkte hoch, auf der Gegenstandsseite 159 — dasselbe Widget,
               dieselben Werte, nur mehr Rest zu verteilen. Ein Bauteil, dessen
               Hoehe davon abhaengt, wieviel *daneben* steht, ist keines. */
            align-content: start;
        }

    }
}

