/* =================================================================
   Marcel Schreyer – Stylesheet
   1 Tokens · 2 Basis · 3 Kopf · 4 Filter · 5 Karten · 6 Artikel
   7 Zipfel/Flyout · 8 Fuß · 9 Responsive
   ================================================================= */

/* --- 1 Tokens ---------------------------------------------------- */
:root{
  /* Schrift auf Flaeche. --ink-faint ist die Farbe aller Kleinschrift: Labels,
     Metazeilen, Tabellenkoepfe, Bereichskoepfe. Sie muss deshalb 4,5:1 auf
     Weiss erreichen - #8A8A8A kam nur auf 3,45:1 und liess damit jede
     Kleinschrift der Website durchfallen. #6E6E6E erreicht 5,1:1. */
  --ink:#131313;         /* 18,6:1 auf Weiss */
  --ink-soft:#5C5C5C;    /*  6,7:1 */
  --ink-faint:#6E6E6E;   /*  5,1:1 - Untergrenze fuer Text ist 4,5:1 */

  --paper:#FFFFFF;
  --surface:#FFFFFF;

  /* Zwei Randfarben. --line ist die Haarlinie zwischen Flaechen, sie darf
     hell sein, weil man sie nicht bedient. --line-strong umrandet Eingabe- und
     Auswahlfelder: dort verlangt WCAG 1.4.11 mindestens 3:1, weil der Rand
     das Feld ueberhaupt erst als Feld erkennbar macht. */
  --line:#E6E6E6;        /*  1,25:1 - nur Trennlinien und Kartenraender */
  --line-strong:#949494; /*  3,03:1 - Formularfelder */

  --accent:#B4124E;      /*  6,7:1 als Schrift und als Flaeche mit weisser Schrift */
  --accent-ink:#FFFFFF;
  --accent-glow:rgba(180,18,78,.16);   /* Akzentfarbe mit 16% Deckkraft, Schein um das Laempchen */

  --code-bg:#F4F4F4;           /* Hintergrund von Code und des FAQ-Zeichens beim Ueberfahren */
  --placeholder-bg:#F1F1F1;    /* Flaeche von Portraet und Avatar, steht auch als fill in den SVG */
  --lamp:#CDCDCD;              /* Laempchen im Ruhezustand */
  --lamp-hover:#AFAFAF;        /* Laempchen beim Ueberfahren */

  /* Vier Eckenradien, jeder mit einer Aufgabe. Frueher standen sieben
     verschiedene Werte im Stylesheet. */
  --radius-code:5px;     /* Code im Fliesstext */
  --radius-s:8px;        /* Eingabe- und Auswahlfelder */
  --radius-m:12px;       /* Kaesten im Text, Inhaltsverzeichnis, Codebloecke, Bilder, Plot, Zipfel */
  --radius-l:14px;       /* Karten und die Ueber-mich-Karte */
  --radius-pill:999px;

  /* Fuenf Schattenstufen, jede fuer einen Zustand. --lift und --lift-pressed
     gelten fuer alles, was man druecken kann. Die drei Drawer-Werte gehoeren
     zur Schublade "Ueber mich": sie liegt vor der Seite, ihr Schatten kommt
     deshalb von rechts. */
  --lift:0 2px 5px rgba(18,18,18,.10), 0 1px 2px rgba(18,18,18,.06);
  --lift-pressed:0 1px 2px rgba(18,18,18,.10);
  --lift-drawer:-2px 2px 6px rgba(18,18,18,.10);
  --lift-drawer-pressed:-1px 1px 3px rgba(18,18,18,.10);
  --lift-drawer-open:-6px 6px 24px rgba(18,18,18,.12);

  --flyout-w:min(330px,86vw);   /* Breite der Ueber-mich-Karte, der Zipfel faehrt um genau diesen Wert mit */

  --wrap:1080px;
  /* Lesebreite in Pixeln, nicht in ch. Die Null in Space Grotesk ist 0,641em
     breit, das durchschnittliche Zeichen eines Artikels 0,492em - ein ch sind
     hier also 1,30 echte Zeichen. Die frueheren 68ch waren 748px und trugen
     im Browser gemessen 87 Zeichen je Zeile, deutlich ueber dem Korridor von
     45 bis 75. 600px tragen rund 70. Der Innenabstand kommt ueber --gutter
     dazu, er gilt fuer den Text selbst. */
  --read:600px;
  --gutter:24px;  /* seitlicher Innenabstand jedes Containers */
  --box-pad:24px; /* seitlicher Innenabstand jedes Kastens im Text - und der Betrag, um den seine Kante nach aussen tritt */
  --gap:22px;

  --sans:"Space Grotesk", system-ui, -apple-system, sans-serif;
  --mono:"Space Mono", ui-monospace, SFMono-Regular, monospace;

  --ease:cubic-bezier(.2,.7,.3,1);
}

/* Selbst gehostete Schriften, siehe doku/designsystem.md, Abschnitt "Die Schriften". Die
   Dateien liegen in assets/fonts/ und sind auf latin und latin-ext verkleinert;
   Space Grotesk ist variabel mit dem Gewichtsbereich 400 bis 600. Keine Datei
   kommt von einem fremden Anbieter - darauf ist die Datenschutzerklaerung
   geschrieben. */
@font-face{font-family:"Space Grotesk";src:url("fonts/space-grotesk.woff2") format("woff2");
  font-weight:400 600;font-style:normal;font-display:swap}
@font-face{font-family:"Space Mono";src:url("fonts/space-mono-400.woff2") format("woff2");
  font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Space Mono";src:url("fonts/space-mono-700.woff2") format("woff2");
  font-weight:700;font-style:normal;font-display:swap}
/* Space Mono 700 braucht nur die Toolseite (.merk-name, .param-liste dt). Er
   muss trotzdem als eigene Datei da sein - sonst rechnet sich der Browser einen
   Fettdruck aus. Vorgeladen wird er nicht, er steht dort weit unten. */

/* --- 2 Basis + Papierstruktur ------------------------------------ */
*,*::before,*::after{box-sizing:border-box}
[hidden]{display:none !important}

html{-webkit-text-size-adjust:100%;background:var(--paper)}
@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}
body{
  position:relative;margin:0;
  background:var(--paper);color:var(--ink);
  font-family:var(--sans);font-size:17px;line-height:1.6;
  -webkit-font-smoothing:antialiased;
}

/* Feine Faserung, liegt hinter dem Inhalt. Karten sind deckend und
   bleiben glatt – das trennt die Ebenen ohne zweite Farbe. */
body::before{
  content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  opacity:.042;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
}

a{color:inherit;text-decoration:none}
img{max-width:100%;display:block}
button{font:inherit;color:inherit}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:4px}

.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gutter)}
/* --read ist die Breite des Textes. Bei .wrap liegt der Innenabstand innerhalb
   der Maximalbreite und muss deshalb aufgeschlagen werden - ohne das war der
   Text auf diesen Seiten 48px schmaler als im Artikel, wo --read allein fuer
   den Text gilt. */
.wrap-read{max-width:calc(var(--read) + 2 * var(--gutter))}

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

.skip{position:absolute;left:-9999px;top:0;background:var(--ink);color:var(--paper);padding:10px 16px;z-index:100}
.skip:focus{left:12px;top:12px}

/* Der Name eines Skills steht kursiv, wie ein Werktitel. Im Deutschen ist
   jedes Substantiv gross geschrieben, ein Eigenname hat dadurch kein
   eigenes Zeichen - "die Denkhuete" liest sich sonst wie eine Gattung.
   Space Grotesk hat keinen kursiven Schnitt: die Variable Font veraendert
   nur die Strichstaerke. Der Browser neigt die aufrechte Schrift deshalb
   selbst, und die Neigung laesst sich nicht einstellen - font-style mit
   Gradangabe (oblique 10deg) laesst die Schrift aufrecht, synthetisiert
   wird nur bei schlichtem italic. Die Vorgabe sieht in der Ueberschrift wie
   im Fliesstext sauber aus, gilt aber nur fuer den Namen selbst: ein ganzer
   geneigter Absatz haelt das nicht aus. */
.skillname{font-style:italic}

/* --- 3 Kopfbereich ----------------------------------------------- */
.masthead{padding:36px 0 56px;text-align:center}  /* 36px oben: darueber stehen die 52px der Seitenmarke */
.masthead h1{
  margin:0;font-size:clamp(2.1rem,6vw,3rem);line-height:1.1;
  font-weight:600;letter-spacing:-.03em;
}
.masthead p{margin:14px auto 0;max-width:46ch;font-size:1.0625rem;color:var(--ink-soft)}

/* Portraet im Kopf der Startseite. Rund und klein - es markiert die Person
   hinter der Uebersicht. Das grosse Bild steht auf "Ueber mich" im Fliesstext,
   dort als gewoehnliche figure. */
.portraet{
  display:block;                                 /* das img traegt die Klasse selbst: ohne block zentriert margin auto nicht
                                                    und der Grundlinienabstand haenge 4px unter das Bild */
  margin:32px auto 0;width:104px;height:104px;   /* 32px Abstand zum Untertitel, 104px Kantenlaenge */
  border-radius:50%;overflow:hidden;background:var(--placeholder-bg);
}
.portraet svg,.portraet img{width:100%;height:100%;object-fit:cover}

/* Das Zeichen der Seite. Kreis und Quadrat greifen ineinander: der runde Punkt
   steht auf dieser Website fuer Text zum Lesen, das Quadrat fuer ein Tool zum
   Benutzen - und damit fuer die beiden Seiten der Arbeit, das Denken und das
   Machen. Die Fuge zwischen beiden ist im Zeichen 2,4 von 32 Einheiten breit;
   schmaler laeuft sie bei 16px Kantenlaenge im Browsertab zu.

   Es steht als erstes Element der Seite in der oberen linken Ecke, auf jeder
   Seite an derselben Stelle: 24px vom oberen Rand, 24px vom linken. Bewusst
   nicht an der Kante des Inhalts - die liegt auf der Startseite (1080px breite
   Uebersicht) und auf den Unterseiten (Lesespalte) verschieden weit innen, das
   Zeichen wuerde von Seite zu Seite springen. In der Ecke markiert es die Seite
   und gehoert zu keinem Block.

   Die Farben kommen aus den Tokens, damit es nur eine Quelle dafuer gibt. Als
   Favicon liegt dasselbe Zeichen auf einer dunklen Kachel (favicon.svg): im Tab
   braucht es eine Flaeche, auf der weissen Seite stoert sie. */
.seitenmarke{display:block;line-height:0;padding:24px 0 0 var(--gutter)}  /* 24px oben, --gutter (24px) links */
.seitenmarke svg{display:block;width:28px;height:28px}  /* 28px Kantenlaenge des Zeichens.
   display:block, weil ein Inline-SVG sonst auf der Schriftlinie sitzt und das
   Zeichen zwei Pixel hoeher stuende als der Innenabstand vorgibt. */
a.seitenmarke{width:max-content;transition:transform .18s var(--ease)}
a.seitenmarke:hover{transform:translateY(-2px)}   /* hebt sich beim Ueberfahren 2px an */

/* Kontaktzeichen in der oberen rechten Ecke, Gegenstueck zum Seitenzeichen
   links. Es steht absolut, weil die Website keine Kopfleiste hat: ohne
   gemeinsame Zeile liegen die Zeichen trotzdem auf einer Hoehe. Die Rechnung
   dahinter: Das Seitenzeichen hat 24px Innenabstand oben und ist 28px hoch,
   seine Mitte liegt also 38px unter der Oberkante. Die Kontaktflaechen sind
   ebenfalls 28px hoch, mit top:24px liegt ihre Mitte auf demselben Wert.

   Absolut gesetzt reserviert der Block keine Hoehe - er braucht auch keine:
   Das Seitenzeichen haelt mit seinem Innenabstand schon 52px frei, und der
   Block endet bei 52px. Deshalb ueberlappt er auf keiner Seite den Inhalt. */
.kontakt{
  position:absolute;top:24px;right:var(--gutter);   /* 24px von oben, --gutter (24px, auf dem Handy 20px) von rechts */
  display:flex;gap:4px;                             /* 4px zwischen den beiden Flaechen */
  z-index:2;
}
/* 28px Kantenlaenge der Klickflaeche - WCAG 2.5.8 nennt 24px als Untergrenze
   fuer eine Zielgroesse, das Zeichen darin ist 18px hoch. Farbe wie jede
   Kleinschrift, beim Ueberfahren Akzentfarbe und 2px Anhebung: dieselbe
   Bewegung wie beim Seitenzeichen, dieselbe Farbe wie bei der Marke einer
   Karte. */
.kontakt-link{
  display:flex;align-items:center;justify-content:center;
  width:28px;height:28px;
  color:var(--ink-soft);
  transition:color .16s var(--ease), transform .18s var(--ease);
}
.kontakt-link svg{display:block;width:18px;height:18px}   /* 18px Kantenlaenge des Zeichens */
/* Das LinkedIn-Zeichen ist ein gefuellter Buchstabenglyph, das Kuvert eine
   Strichzeichnung mit 1,5 Einheiten Strichstaerke. Bei gleicher Kantenlaenge
   wirkt die gefuellte Form deutlich schwerer. 16px statt 18px gleicht das aus -
   gemessen sind beide Zeichen dann rund 12px breit. */
.kontakt-in svg{width:16px;height:16px}   /* 16px Kantenlaenge des Zeichens */
.kontakt-link:hover{color:var(--accent);transform:translateY(-2px)}

/* Unterseiten haben keine Kopfleiste. Zurueck geht es ueber das Zeichen und den
   Ruecklink daneben, die zusammen als erstes Element der Seite stehen. */
.backlink{
  display:inline-flex;align-items:center;gap:8px;
  font-family:var(--mono);font-size:.8125rem;color:var(--ink-soft);
}
.head-wrap > .backlink,
.wrap-read > .backlink{margin-bottom:24px}  /* 24px Abstand zur Ueberschrift */
.backlink svg{width:14px;height:14px;transition:transform .18s var(--ease)}
.backlink:hover svg{transform:translateX(-4px)}

/* --- 4 Filter + Kategoriezeile ----------------------------------- */
.filters-block{padding-bottom:34px;border-bottom:1px solid var(--line);margin-bottom:34px}

/* Drei Gruppen mit 28px Abstand zueinander: der Umschalter "Alle", die beiden
   Textkategorien, die Tools. Innerhalb einer Gruppe 10px. "Alle" steht
   allein, weil es beide Sorten meint und nicht zu den Texten gehoert. */
.filters{display:flex;flex-wrap:wrap;justify-content:center;gap:28px}
.filter-group{display:flex;flex-wrap:wrap;justify-content:center;gap:10px}

.pill{
  display:inline-flex;align-items:center;gap:8px;   /* 8px zwischen Laempchen und Text */
  font-family:var(--mono);font-size:.8125rem;line-height:1;
  padding:11px 16px;border:1px solid var(--line);border-radius:var(--radius-pill);
  background:var(--surface);color:var(--ink-soft);cursor:pointer;box-shadow:var(--lift);
  transition:transform .16s var(--ease), box-shadow .16s var(--ease),
             border-color .16s var(--ease), color .16s var(--ease);
}
.pill:hover,.pill:active{transform:translateY(2px);box-shadow:var(--lift-pressed)}

/* Ausgewaehlt: Rand und Schrift werden dunkel, die Pille bleibt gedrueckt.
   Bewusst ohne Wechsel der Schriftstaerke - fetter Mono ist breiter als
   normaler, die Pillenreihe wuerde bei jedem Klick seitlich springen.
   Der zweite Selektor ist der Umschalter auf der Seite "Ueber mich": dort
   traegt ein <label> die Klasse .pill, den Zustand haelt das Radiofeld
   davor. Beides teilt sich diese Regeln, damit es nicht auseinanderlaeuft. */
.pill[aria-pressed="true"],
.mode input:checked + .pill{
  border-color:var(--ink);color:var(--ink);
  transform:translateY(2px);box-shadow:var(--lift-pressed);
}

/* Laempchen. Im Ruhezustand grau, ausgewaehlt leuchtet es in der Akzentfarbe -
   damit ist die Filterreihe farblos, solange nichts gewaehlt ist.
   Die Form sagt, welche Sorte Eintrag dahinter liegt:
   runder Punkt = Text zum Lesen, Quadrat = Tool zum Benutzen.
   Die Pille "Alle" filtert kein Kriterium und hat deshalb kein Laempchen. */
.lamp{
  width:7px;height:7px;border-radius:50%;flex:none;   /* 7px Durchmesser */
  background:var(--lamp);
  transition:background-color .16s var(--ease), box-shadow .16s var(--ease);
}
.lamp-tool{width:6px;height:6px;border-radius:1px}    /* 6px Kantenlaenge, 1px Eckenradius */
.pill:hover .lamp{background:var(--lamp-hover)}
.pill[aria-pressed="true"] .lamp,
.mode input:checked + .pill .lamp{
  background:var(--accent);
  box-shadow:0 0 0 3px var(--accent-glow);            /* 3px breiter Schein um das Laempchen */
}

/* Kategoriezeile. Alle Texte stehen gleichzeitig im Kasten, uebereinander in
   derselben Rasterzelle; sichtbar ist immer nur einer. Der Kasten ist damit so
   hoch wie der laengste Text, und der Abstand von den Pillen zur Trennlinie
   bleibt gleich - egal welche Kategorie gewaehlt ist und ob ihr Text bei der
   aktuellen Fensterbreite ein-, zwei- oder dreizeilig umbricht. */
.filter-hint{
  display:grid;
  margin:18px auto 0;    /* 18px Abstand zu den Pillen */
  min-height:1.6em;      /* Reserve ohne JavaScript: eine Zeilenhoehe */
  max-width:52ch;text-align:center;
  font-size:.9375rem;color:var(--ink-soft);
}
.filter-hint span{
  grid-area:1 / 1;
  opacity:0;visibility:hidden;
  transition:opacity .14s ease, visibility .14s ease;
}
.filter-hint span[data-aktiv="true"]{opacity:1;visibility:visible}

/* --- 5 Karten ---------------------------------------------------- */
.grid{
  display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap);
  padding-bottom:96px;list-style:none;margin:0;
}

.card{
  display:flex;flex-direction:column;gap:18px;min-height:190px;padding:22px;
  background:var(--surface);border-radius:var(--radius-l);box-shadow:var(--lift);
  transition:transform .16s var(--ease), box-shadow .16s var(--ease);
}
.card:hover,.card:active{transform:translateY(2px);box-shadow:var(--lift-pressed)}

.card-meta{
  display:flex;justify-content:space-between;align-items:center;gap:12px;
  font-family:var(--mono);font-size:.75rem;color:var(--ink-faint);
}
.card-meta .left{display:inline-flex;align-items:center;gap:8px}  /* 8px zwischen Zeichen und Label */
.card-meta strong{font-weight:400;color:var(--ink-soft)}

/* Dieselbe Zeichensprache wie in der Filterreihe - Punkt fuer Text,
   Quadrat fuer Tool -, auf der Karte im Ruhezustand grau: die Akzentfarbe
   bleibt in der Teaserkarte allein auf dem Pfeil. */
.mark{
  width:6px;height:6px;border-radius:50%;background:var(--ink-faint);flex:none;
  transition:background-color .16s var(--ease);
}
.mark-tool{border-radius:1px}
/* Beim Zeigen auf die Karte leuchtet die Marke auf - dasselbe Signal wie beim
   Laempchen in der Filterreihe, nur hier ohne Schein. */
.card:hover .mark,
.card:focus-visible .mark{background:var(--accent)}

.card h2{margin:0;font-size:1.125rem;line-height:1.35;font-weight:500;letter-spacing:-.015em}

.card-link{
  margin-top:auto;display:inline-flex;align-items:center;gap:8px;
  font-size:.9375rem;font-weight:500;
}
.card-link svg{width:15px;height:15px;color:var(--accent);transition:transform .18s var(--ease)}
.card:hover .card-link svg{transform:translateX(4px)}

/* Externe Artikel: Diagonalpfeil, Quelle steht im Linktext */
.card[data-external] .card-link .source{font-weight:600}
.card[data-external]:hover .card-link svg{transform:translate(3px,-3px)}

/* Tool-Karte. Unter dem Namen steht ein Satz, was das Ding tut - ein Toolname
   allein erklaert nichts. Dieser Rhythmusbruch macht die Karte beim
   Ueberfliegen erkennbar, ganz ohne zweite Farbe. */
.card-tool h2{font-size:1.0625rem}
.card-desc{margin:-8px 0 0;font-size:.9375rem;line-height:1.45;color:var(--ink-soft)}
/* Download statt Sprung: der Pfeil zeigt nach unten und bewegt sich auch dorthin */
.card-download:hover .card-link svg{transform:translateY(3px)}

/* Einblenden beim Scrollen – nur mit aktivem JS. Die ersten drei Karten
   bleiben sichtbar: sie stehen im ersten Bildschirm und sind damit das
   Element, an dem Google die Ladezeit festmacht (LCP). Ein Element mit
   Deckkraft null zaehlt dafuer nicht - die Messung begaenne erst, wenn das
   Skript gelaufen ist. Eingeblendet wird nur, was man ohnehin erst nach dem
   Scrollen sieht. */
.js .card:nth-child(n+4){opacity:0;transform:translateY(16px)}
.js .card.is-visible{opacity:1;transform:none;transition:opacity .5s var(--ease), transform .5s var(--ease)}
.js .card.is-visible:hover,.js .card.is-visible:active{
  transform:translateY(2px);
  transition:transform .16s var(--ease), box-shadow .16s var(--ease);
}

/* --- 6 Artikel --------------------------------------------------- */
.article{padding:20px 0 96px}  /* 20px oben - darueber stehen die 52px der Seitenmarke -, 96px unten */
.article-head{margin-bottom:40px}
/* Metazeile als kleines Datenblatt: Label oben, Wert darunter.
   Reihenfolge im HTML: Datum, Kategorie, Lesezeit. */
.article-meta{
  display:flex;flex-wrap:wrap;
  gap:16px 34px;      /* 16px Abstand zwischen umgebrochenen Zeilen, 34px zwischen den Bloecken */
  margin-bottom:22px; /* 22px Abstand zur Ueberschrift */
}
.meta-item{display:flex;flex-direction:column;gap:4px}  /* 4px zwischen Label und Wert */
.meta-label{
  font-family:var(--mono);font-size:.75rem;     /* Schriftgroesse Label, 12px - Untergrenze */
  line-height:1;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-faint);
}
.meta-value{
  font-family:var(--mono);font-size:.8125rem;   /* Schriftgroesse Wert, 13px */
  line-height:1.3;color:var(--ink-soft);
}
/* Die Lesezeit steht im HTML, geschrieben von build-vorschau.py. Frueher
   rechnete sie das Skript aus und der Block erschien erst danach - das
   verschob die Ueberschrift nach dem Laden. */
.article h1{
  margin:0;font-size:clamp(1.8rem,4.5vw,2.5rem);line-height:1.15;
  font-weight:600;letter-spacing:-.03em;
}
.article .lead{margin:18px 0 0;font-size:1.125rem;color:var(--ink-soft)}

.prose{font-size:1.0625rem}
.prose h2{margin:44px 0 14px;font-size:1.375rem;font-weight:600;letter-spacing:-.02em;line-height:1.3;scroll-margin-top:32px}
.prose h3{margin:32px 0 10px;font-size:1.125rem;font-weight:600;letter-spacing:-.01em}
.prose p{margin:0 0 18px}
.prose ul,.prose ol{margin:0 0 18px;padding-left:22px}   /* 22px Einzug */
.prose li{margin-bottom:8px}

/* Aufzaehlungspunkt als offener Kreis in der Akzentfarbe, beim Ueberfahren
   gefuellt. Der native Punkt entfaellt dafuer - deshalb braucht jede ul im
   Artikel ein role="list", sonst meldet VoiceOver in Safari die Aufzaehlung
   nicht mehr als Liste. */
.prose ul:not([class]){list-style:none}
.prose ul:not([class]) > li{position:relative}
.prose ul:not([class]) > li::before{
  content:"";position:absolute;
  left:-22px;top:.55em;                 /* 22px nach links, .55em von oben: Mitte der ersten Zeile */
  width:9px;height:9px;                 /* Durchmesser des Punkts, 9px */
  border-radius:50%;border:1.5px solid var(--accent);   /* 1,5px Randstaerke */
  transition:background-color .16s var(--ease);
}
.prose ul:not([class]) > li:hover::before{background:var(--accent)}

/* Nummern einer gewoehnlichen Aufzaehlung ebenfalls in der Akzentfarbe */
.prose ol:not(.schritte) > li::marker{color:var(--accent);font-family:var(--mono);font-size:.875em}
.prose a{color:var(--accent);text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px}
.prose blockquote{
  margin:26px 0;padding:2px 0 2px calc(var(--box-pad) - 2px);   /* 22px Text plus 2px Balken = --box-pad */
  border-left:2px solid var(--accent);color:var(--ink-soft);
}
.prose code{
  font-family:var(--mono);font-size:.875em;
  background:var(--code-bg);padding:2px 6px;border-radius:var(--radius-code);
}
/* Schriftgroesse im Codeblock 15px - vorher 13px und damit deutlich kleiner
   als Code im Fliesstext, der bei 17px Grundschrift auf rund 15px kommt.
   Beides liegt jetzt gleichauf. */
.prose pre{
  background:var(--code-bg);padding:18px var(--box-pad);border-radius:var(--radius-m);overflow-x:auto;
  font-family:var(--mono);font-size:.9375rem;line-height:1.6;margin:0 0 22px;
}
.prose pre code{background:none;padding:0}
.prose figure{margin:28px 0}
/* Steht ein Bild als erster Block im Fliesstext - auf "Ueber mich" das grosse
   Bild unter der Unterzeile -, kommt der Abstand allein vom Kopf darueber. */
.prose > figure:first-child{margin-top:0}
/* Unterschrift unter einem Bild und unter einer Tabelle. Beide sind dasselbe:
   eine leise Zeile, die den Block darueber einordnet, deshalb dieselbe Regel.
   Was nur fuer die Tabelle gilt - Seite und Ausrichtung -, steht bei der
   Tabelle. Der Strich links macht die Zeile als Unterschrift kenntlich, ohne
   sie lauter zu setzen; er waechst von selbst mit, wenn der Text umbricht. */
.prose figcaption,
.prose caption{
  font-family:var(--mono);font-size:.8125rem;color:var(--ink-faint);   /* Schriftgroesse 13px: wird gelesen, nicht ueberflogen */
  margin-top:10px;                                                     /* 10px Abstand zum Block darueber */
  padding-left:12px;border-left:2px solid var(--line);                 /* 12px Einzug, Strich 2px breit - dieselben Masse wie im Inhaltsverzeichnis */
}
/* max-width haelt das Bild in der Textspalte: die Dateien sind 1200px breit,
   die Spalte ist 600px - ohne die Regel schoebe jedes Bild die Seite auf.
   1200px auf 600px angezeigt sind zwei Bildpunkte je Pixel und damit auf
   jedem Retina-Display scharf. Breiter anzeigen bringt deshalb nichts; wo es
   auf Einzelheiten ankommt, steht das Bild in einem Link auf die volle Datei,
   siehe module.md, "Bilder". */
.prose img{display:block;max-width:100%;height:auto;border-radius:var(--radius-m)}

/* Tabelle: nur waagerechte Linien, Kopfzeile in Mono wie die uebrigen
   Metazeilen der Seite. Der Kasten .tabelle scrollt seitlich, sobald die
   Tabelle breiter wird als die Textspalte - ohne ihn schoebe eine breite
   Tabelle die ganze Seite auseinander. Auf dem Handy ist das der Normalfall:
   bei 375px Fensterbreite ist der Kasten 335px breit und eine dreispaltige
   Tabelle 468px.

   Der Kasten traegt im Markup tabindex="0", role="region" und ein
   aria-label - sonst kommt niemand ohne Maus an die rechten Spalten. Dieselbe
   Regel gilt fuer den Dateikasten der Skill-Seite. */
.prose .tabelle{
  overflow-x:auto;margin:0 0 22px;                 /* 22px Abstand nach unten */
  /* Zwei Schatten an den Kanten zeigen, dass es seitwaerts weitergeht, und
     zwei Deckflaechen in Papierfarbe nehmen sie am jeweiligen Ende wieder
     zurueck. Moeglich macht das background-attachment: die Deckflaechen
     stehen auf local und fahren mit dem Inhalt mit, die Schatten auf scroll
     und bleiben am Kasten. */
  background-image:
    linear-gradient(to right, var(--paper) 60%, rgba(255,255,255,0)),
    linear-gradient(to left,  var(--paper) 60%, rgba(255,255,255,0)),
    linear-gradient(to right, rgba(18,18,18,.13), rgba(18,18,18,0)),
    linear-gradient(to left,  rgba(18,18,18,.13), rgba(18,18,18,0));
  background-position:left center, right center, left center, right center;
  background-repeat:no-repeat;
  /* Die Deckflaeche ist 18px breit und davon die ersten 60 Prozent, also rund
     11px, voll deckend - genau die Breite des Schattens. Sie bleibt so schmal
     wie moeglich, weil sie als weisse Flaeche die Papierstruktur des
     Hintergrunds verdeckt. */
  background-size:18px 100%, 18px 100%, 10px 100%, 10px 100%;
  background-attachment:local, local, scroll, scroll;
  /* Die Bildlaufleiste steht dauerhaft da, auch auf macOS, wo der Browser sie
     sonst erst beim Scrollen einblendet - wie beim Dateikasten der Skill-Seite. */
  scrollbar-width:thin;scrollbar-color:var(--line-strong) transparent;
}
.prose .tabelle::-webkit-scrollbar{height:11px}                 /* Hoehe der Bildlaufleiste */
.prose .tabelle::-webkit-scrollbar-track{background:transparent}
.prose .tabelle::-webkit-scrollbar-thumb{
  background:var(--line-strong);border-radius:var(--radius-pill);
  border:3px solid var(--paper);                                /* 3px Luft ringsum, in der Farbe der Seite */
}
.prose .tabelle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}   /* Rahmen 2px breit, 2px Abstand zum Kasten */
.prose table{
  width:100%;border-collapse:collapse;
  font-size:.9375rem;line-height:1.45;             /* Schriftgroesse 15px */
}
.prose th,.prose td{
  text-align:left;vertical-align:top;
  padding:11px 16px 11px 0;                        /* 11px oben und unten, 16px nach rechts */
  border-bottom:1px solid var(--line);
}
.prose th:last-child,.prose td:last-child{padding-right:0}
.prose th{
  font-family:var(--mono);font-size:.8125rem;      /* Schriftgroesse 13px */
  font-weight:400;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-faint);border-bottom-color:var(--ink-faint);
  white-space:nowrap;
}
.prose td{color:var(--ink-soft)}
.prose tbody tr:last-child td{border-bottom:none}
/* Zahlenspalten: rechtsbuendig und mit gleich breiten Ziffern, damit die
   Stellen untereinander stehen. */
.prose th.zahl,.prose td.zahl{text-align:right;font-variant-numeric:tabular-nums}
/* Schrift, Farbe, Abstand und Strich teilt sie sich mit der Bildunterschrift,
   siehe dort. Hier steht nur, was allein fuer die Tabelle gilt. */
.prose caption{caption-side:bottom;text-align:left}

/* Schrittliste: Kasten mit nummerierten Eintraegen, fuer Ablaeufe statt einer
   gewoehnlichen Aufzaehlung. Die Zahlen kommen aus einem CSS-Zaehler, im HTML
   stehen nur die li - eingeschobene Schritte nummerieren sich selbst neu. */
.prose .schritte{
  list-style:none;counter-reset:schritt;
  margin:26px 0;padding:22px 24px;                  /* 22px oben und unten, 24px seitlich - wie jede Karte */
  border:1px solid var(--line);border-radius:var(--radius-m);
  display:grid;gap:20px;                            /* 20px zwischen den Eintraegen */
}

/* Mit Ueberschrift und Introsatz: der Kasten wandert auf .schritte-box, die
   Liste darin verliert Kante und Innenabstand. Ohne Ueberschrift bleibt die
   Liste allein der Kasten - beide Formen funktionieren. */
.prose .schritte-box{
  margin:26px 0;padding:22px 24px;                  /* 22px oben und unten, 24px seitlich - wie jede Karte */
  border:1px solid var(--line);border-radius:var(--radius-m);
}
.prose .schritte-box > .schritte{margin:0;padding:0;border:none}
/* .prose h2 und .prose h3 setzen Groesse, Gewicht, Laufweite und Zeilenhoehe -
   Klasse plus Element ist staerker als die Klasse .bereich-titel allein.
   Deshalb steht hier noch einmal alles, was den Kopf ausmacht. Damit sieht er
   gleich aus, egal welche Ebene die Ueberschrift hat: Die Ebene richtet sich
   nach der Stelle im Dokument (auf "Ueber mich" steht der Kasten vor der ersten
   h2 und braucht deshalb selbst eine h2), das Aussehen nicht. */
.prose .bereich-titel{
  margin:0 0 10px;                                  /* 10px Abstand zum Introsatz */
  font-size:.75rem;font-weight:400;                 /* Schriftgroesse 12px */
  letter-spacing:.04em;
  /* line-height:inherit statt eines Werts: .prose h2 setzt 1.3, .prose h3
     setzt nichts - der Kopf haette also je nach Ebene eine andere Hoehe und
     damit einen anderen sichtbaren Abstand zum Text darunter. Mit inherit
     nimmt er die Zeilenhoehe seiner Umgebung, genau wie der Kopf in der
     Matrixkarte ausserhalb von .prose. */
  line-height:inherit;
}
.prose .schritte-box > .karte-intro{
  margin:0 0 20px;max-width:none;                   /* 20px Abstand zur Liste */
}

/* Aktionskasten: Ueberschrift, ein bis zwei Saetze, Button. Gleiche Kante und
   gleicher Innenabstand wie der Schrittkasten. Die Farbe kommt allein vom
   Button, der Kasten selbst bleibt weiss. */
.prose .cta-box{
  margin:30px 0;padding:22px 24px;                  /* 22px oben und unten, 24px seitlich - wie jede Karte */
  border:1px solid var(--line);border-radius:var(--radius-m);
}
.prose .cta-box > h3{
  margin:0 0 8px;                                   /* 8px Abstand zum Text */
  font-size:1.125rem;font-weight:600;letter-spacing:-.01em;    /* Schriftgroesse 18px wie jede h3 */
}
.prose .cta-box > p{
  margin:0 0 20px;max-width:500px;                  /* 20px Abstand zum Button, 500px Textbreite - vorher 52ch, derselbe Wert in Pixeln */
  font-size:.9375rem;line-height:1.5;color:var(--ink-soft);    /* Schriftgroesse 15px */
}
/* .prose a faerbt jeden Link in der Akzentfarbe und unterstreicht ihn - das
   traefe sonst auch die weisse Schrift auf dem Button. */
.prose .btn{color:var(--accent-ink);text-decoration:none}

/* Infobox: Kasten fuer normalen Fliesstext, gleiche Kante und gleicher
   Innenabstand wie Schritt- und Aktionskasten. Der Kopf ist der leise aus dem
   Schrittkasten und keine Schlagzeile: die Box steht im Lesefluss und
   unterbricht ihn nicht. Hinein gehoert nur Elementares - der wichtigste Punkt
   verdichtet, ein Takeaway oder was jetzt zu tun ist. Nebeninformationen
   bleiben im Fliesstext, sonst wird der Kasten zur Resterampe. */
.prose .infobox{
  margin:26px 0;padding:22px 24px;                  /* 22px oben und unten, 24px seitlich - wie jede Karte */
  border:1px solid var(--line);border-radius:var(--radius-m);
}
.prose .infobox > :last-child{margin-bottom:0}      /* kein Leerraum unter dem letzten Absatz */

/* Fussnote: Text, den lesen kann wer will, den aber niemand lesen muss. Die
   Abgrenzung gegen die Namensgleichen stand vorher in einer .infobox - ein
   Kasten hebt hervor, und genau das soll dieser Hinweis nicht. Jetzt eine
   Haarlinie darueber und Kleinschrift: vollstaendig im Dokument und damit
   sichtbar fuer Suchmaschinen und Antwortmaschinen, im Satzbild aber leise.
   13px ist die Untergrenze fuer Text, der gelesen wird - kleiner darf auch eine
   Fussnote nicht werden. */
.prose .fussnote{
  margin:38px 0 0;padding-top:14px;                             /* 38px Abstand zum Block darueber, 14px von der Linie zum Text */
  border-top:1px solid var(--line);
  font-size:.8125rem;line-height:1.55;color:var(--ink-faint);   /* 13px Schriftgroesse */
}
.prose .fussnote p{margin:0 0 10px}                             /* 10px zwischen zwei Absaetzen der Fussnote */
.prose .fussnote > :last-child{margin-bottom:0}

/* Kernaussagen: erster Block im Text, fasst zusammen, was der Artikel
   beantwortet. Ersetzt den frueheren Einstiegsabsatz - wer ueberfliegt, hat die
   Aussage nach drei Zeilen, und Suchmaschinen wie Sprachmodelle finden sie ohne
   Umweg. Die Ueberschrift liegt im Kasten und taucht deshalb nicht im
   Inhaltsverzeichnis auf: das Verzeichnis liest nur h2 direkt unter #prose. */
.kernaussagen{
  margin:0 0 34px;padding:22px 24px;                /* 34px Abstand zum Block darunter, 22px oben und unten, 24px seitlich */
  border:1px solid var(--line);border-radius:var(--radius-m);
}
.kernaussagen > ul{margin:0}
.kernaussagen li{margin-bottom:11px}                /* 11px zwischen den Punkten */
.kernaussagen li:last-child{margin-bottom:0}

/* Fragenliste aus details/summary, ohne JavaScript. Auch zugeklappt steht jede
   Antwort vollstaendig im Dokument und ist fuer Crawler lesbar. */
.prose .faq{
  margin:26px 0;overflow:hidden;
  border:1px solid var(--line);border-radius:var(--radius-m);
}
.prose .faq > details + details{border-top:1px solid var(--line)}
.prose .faq summary{
  list-style:none;cursor:pointer;
  display:flex;align-items:flex-start;gap:14px;     /* 14px zwischen Zeichen und Frage */
  padding:18px 24px;                                /* 18px oben und unten, 24px seitlich */
  font-size:1.0625rem;font-weight:500;line-height:1.45;   /* Schriftgroesse 17px */
}
.prose .faq summary::-webkit-details-marker{display:none}
/* Zeichen links: offener Kreis wie bei den Schritten, darin ein Plus aus zwei
   Balken. Beim Aufklappen faehrt der senkrechte Balken auf null - aus dem Plus
   wird ein Minus. */
.prose .faq summary::before{
  content:"";flex:none;
  width:22px;height:22px;margin-top:3px;            /* Durchmesser des Kreises 22px, 3px tiefer fuer die erste Zeile */
  border-radius:50%;border:1px solid var(--accent);
  /* Die Balken nehmen die Schriftfarbe des Zeichens an. Ueber currentColor
     statt eines festen Werts, damit sie beim Ausmalen des Kreises mit der
     Flaeche zusammen ueberblenden - background-image allein laesst sich nicht
     animieren und wuerde hart umspringen. */
  color:var(--accent);
  background-color:transparent;
  background-image:
    linear-gradient(currentColor,currentColor),
    linear-gradient(currentColor,currentColor);
  background-size:9px 1.5px, 1.5px 9px;             /* Balken 9px lang, 1,5px stark */
  background-position:center,center;
  background-repeat:no-repeat;
  transition:background-size .16s var(--ease), background-color .16s var(--ease),
             color .16s var(--ease);
}
.prose .faq details[open] > summary::before{background-size:9px 1.5px, 1.5px 0}
/* Beim Ueberfahren und im aufgeklappten Zustand wird der Kreis ausgemalt und
   die Balken wechseln auf die Flaechenfarbe - dieselbe Bewegung wie beim
   Aufzaehlungspunkt im Fliesstext und bei der Zahl im Schrittkasten. Vorher
   wurde er nur grau hinterlegt und fiel damit aus der Reihe. */
.prose .faq summary:hover::before,
.prose .faq details[open] > summary::before{
  background-color:var(--accent);
  color:var(--accent-ink);
}
.prose .faq details > :not(summary){
  margin:0 24px 20px 60px;                          /* 60px links: Text steht unter der Frage (24px Innenabstand, 22px Zeichen, 14px Abstand), 24px rechts, 20px nach unten */
  font-size:.9375rem;line-height:1.55;color:var(--ink-soft);   /* Schriftgroesse 15px */
}
/* Die Nummer steht absolut, der Eintrag selbst bleibt ein normaler Block.
   Nur so darf im li beliebiger Inhalt stehen - mit einem Raster landeten
   Zwischenueberschrift und Absatz in getrennten Feldern. */
.prose .schritte > li{
  counter-increment:schritt;margin:0;
  position:relative;padding-left:40px;min-height:26px;  /* 40px Einzug: 26px Kreis plus 14px Abstand */
}
.prose .schritte > li::before{
  content:counter(schritt);
  position:absolute;left:0;top:1px;                 /* 1px tiefer, dann sitzt der Kreis auf der ersten Textzeile */
  width:26px;height:26px;                           /* Durchmesser des Kreises, 26px */
  border-radius:50%;border:1px solid var(--accent);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-size:.75rem;line-height:1;color:var(--accent);   /* Schriftgroesse 12px */
  transition:background-color .16s var(--ease), color .16s var(--ease);
}
/* Beim Ueberfahren des Eintrags wird die Zahl ausgemalt */
.prose .schritte > li:hover::before{background:var(--accent);color:var(--accent-ink)}
.prose .schritte strong{display:block;font-weight:600;margin-bottom:4px}  /* 4px Abstand zum Text darunter */
.prose .schritte p{margin:0;font-size:.9375rem;line-height:1.5;color:var(--ink-soft)}  /* Schriftgroesse 15px */

@media (max-width:640px){
  .prose .schritte,
  .prose .schritte-box,
  .prose .cta-box,
  .kernaussagen{padding:20px 18px}                  /* 20px oben und unten, 18px seitlich */
  .prose .schritte-box > .schritte{padding:0}
  .prose .faq summary{padding:16px 18px}            /* 16px oben und unten, 18px seitlich */
  /* Auf schmalen Fenstern steht die Antwort am linken Kastenrand statt unter
     der Frage - eingerueckt bliebe zu wenig Zeilenbreite uebrig. */
  .prose .faq details > :not(summary){margin:0 18px 18px}
}

/* Lesefortschritt: dünne Linie am oberen Rand, rein dekorativ */
.progress{
  position:fixed;top:0;left:0;right:0;height:2px;z-index:50;
  background:var(--accent);
  transform:scaleX(0);transform-origin:0 50%;
  will-change:transform;
}

/* Artikel-Layout: Textspalte mit dem Inhaltsverzeichnis im rechten Rand.
   Unter 1280px stapelt alles, das Verzeichnis wird zum Kasten ueber dem Text.

   Links steht eine leere Spalte in derselben Breite wie die Randspalte rechts.
   Ohne sie zentrierte das Raster den Block aus Text und Verzeichnis gemeinsam,
   und die Textspalte landete 133px links von der Seitenmitte - beim
   Ueberschreiten der Schwelle sprang der Text also zur Seite. Mit der
   gespiegelten Spalte steht der Text auf jeder Seite der Website an derselben
   Stelle, und das Verzeichnis haengt daneben im Rand.

   200px Randspalte und 64px Abstand: Die 64px sind der Platz, in den die
   Kaesten im Text um --box-pad nach aussen treten; danach bleiben 40px bis
   zum Verzeichnis. Mit 600px Text ergibt das 1176px Gesamtbreite, die
   Schwelle von 1280px - ein verbreitetes Fenstermass - reicht dafuer. */
.article-layout > .head-wrap,
.article-layout > .kernaussagen,
.article-layout > .body-wrap{max-width:var(--read);margin-inline:auto}

@media (min-width:1280px){
  .article-layout{
    /* Text plus zweimal Randspalte und Abstand, dazu der Innenabstand -
       breiter als --wrap, weil die Raender selbst leer bleiben. */
    max-width:calc(var(--read) + 2 * (200px + 64px) + 2 * var(--gutter));
    display:grid;
    grid-template-columns:200px minmax(0,var(--read)) 200px;
    column-gap:64px;justify-content:center;align-items:start;
  }
  .article-layout > .head-wrap{grid-column:2;grid-row:1;margin-inline:0}
  .article-layout > .kernaussagen{grid-column:2;grid-row:2;margin-inline:calc(-1 * var(--box-pad))}
  .article-layout > .body-wrap{grid-column:2;grid-row:3;margin-inline:0}
  /* Ab Zeile 2 steht der Text. Das Verzeichnis beginnt damit auf gleicher Hoehe
     wie der erste Inhaltsblock, nicht schon neben Metazeile und Ueberschrift.
     Auf schmalen Fenstern gilt die Reihenfolge im Dokument: der Kasten steht
     dort ueber dem Verzeichnis. */
  .article-layout > .toc{
    grid-column:3;grid-row:2 / span 2;
    position:sticky;top:40px;align-self:start;  /* 40px Abstand zum oberen Fensterrand im angehefteten Zustand */
    margin:0;padding:0;border:none;
  }
}

/* --- Inhaltsverzeichnis ------------------------------------------ */
.toc{
  max-width:var(--read);margin:0 auto 34px;
  padding:18px var(--box-pad);border:1px solid var(--line);border-radius:var(--radius-m);
}
.toc-title{font-family:var(--mono);font-size:.75rem;color:var(--ink-faint);margin:0 0 12px}
.toc ol{list-style:none;margin:0;padding:0;display:grid;gap:9px}
.toc a{
  display:block;font-size:.875rem;line-height:1.35;color:var(--ink-soft);
  padding-left:12px;border-left:2px solid var(--line);
  transition:color .16s var(--ease), border-color .16s var(--ease);
}
.toc a:hover{color:var(--ink)}
.toc a[aria-current="true"]{color:var(--ink);border-color:var(--accent)}

/* --- Kaesten treten nach aussen ---------------------------------- */
/* Der Text in einem Kasten steht auf derselben Spalte wie der Fliesstext
   davor und danach, die Kante liegt um --box-pad weiter aussen. Vorher lag
   die Kante auf der Textspalte und der Innenabstand ging vom Text ab: der
   Text im Kasten war damit 48px schmaler als der Text daneben.

   Erst ab 760px Fensterbreite. Darunter ist der Platz neben der Lesespalte
   nicht da - 600px Text plus zweimal 24px Ueberhang plus zweimal 24px
   Innenabstand der Seite sind 696px, dazu etwas Luft -, dort bleibt die
   Kante buendig.

   Wer einen neuen Kasten baut, traegt ihn hier ein und gibt ihm seitlich
   var(--box-pad) als Innenabstand. Ohne beides steht sein Text schmaler als
   der Fliesstext daneben. */
@media (min-width:760px){
  .prose > .schritte,
  .prose > .schritte-box,
  .prose > .cta-box,
  .prose > .infobox,
  .prose > .faq,
  .prose > pre{margin-inline:calc(-1 * var(--box-pad))}
  /* Das Zitat hat nur links eine Kante und tritt deshalb auch nur links heraus. */
  .prose > blockquote{margin-left:calc(-1 * var(--box-pad))}
  /* Diese beiden Kaesten stehen selbst zentriert und vertragen keine negativen
     Raender - sie verloeren dadurch ihre Mitte. Bei ihnen waechst statt dessen
     die Hoechstbreite um zweimal den Innenabstand. Im Artikelraster ab 1280px
     bekommen die Kernaussagen zusaetzlich den negativen Rand, weil sie dort in
     einer Rasterspalte von genau --read stehen. */
  .article-layout > .kernaussagen,
  .toc{max-width:calc(var(--read) + 2 * var(--box-pad))}
  /* Die Tabelle bleibt buendig: sie hat keinen Innenabstand, ihre erste Spalte
     steht direkt auf der Textkante. */
}

/* --- Abschluss des Artikels -------------------------------------- */
.article-end{
  max-width:var(--read);margin:56px auto 0;padding-top:28px;
  border-top:1px solid var(--line);
  display:flex;flex-wrap:wrap;gap:18px 28px;align-items:center;justify-content:space-between;
}
/* Nur im Artikelraster: dort steht der Abschluss in .body-wrap und ist
   bereits von der Rasterspalte positioniert - die automatischen Raender
   wuerden ihn darin ein zweites Mal zentrieren. Ungebunden traf die Regel
   auch den Abschluss auf "Ueber mich", der als direktes Kind von main in
   voller Fensterbreite steht: er rutschte dort an den linken Fensterrand. */
@media (min-width:1280px){ .article-layout .article-end{margin-inline:0} }

.author-line{display:inline-flex;align-items:center;gap:12px;font-size:.9375rem;font-weight:500}
.author-line .avatar{
  width:40px;height:40px;border-radius:50%;overflow:hidden;
  background:var(--placeholder-bg);flex:none;
}
.author-line .avatar svg,.author-line .avatar img{width:100%;height:100%}
.author-line .author-more{
  display:inline-flex;align-items:center;gap:7px;  /* 7px zwischen Linktext und Pfeil */
  font-weight:400;color:var(--ink-soft);
}
.author-line .arrow{width:15px;height:15px;color:var(--accent);transition:transform .18s var(--ease)}
.author-line:hover .arrow{transform:translateX(4px)}

/* --- 7 Zipfel + Flyout ------------------------------------------- */
/* Der Zipfel ist der Griff der Schublade. Beim Oeffnen faehrt die Karte von
   rechts herein, und der Zipfel wandert um genau ihre Breite mit nach links -
   er klebt also an ihrer Kante und schiebt sie beim naechsten Klick zurueck.
   Beide Bewegungen dauern gleich lang und es wird nichts ein- oder
   ausgeblendet: eine Ueberblendung wuerde wie ein Austausch zweier Elemente
   wirken, nicht wie ein Herausziehen. Deshalb liegt der Zipfel auch ueber der
   Karte (z-index 42 zu 41) und verschwindet nicht hinter ihr. */
.tab{
  position:fixed;right:0;top:34%;transform:translateY(-50%);z-index:42;
  display:flex;align-items:center;gap:8px;   /* 8px zwischen Laempchen und Text, genau wie in der Pille */
  writing-mode:vertical-rl;padding:18px 9px; /* 18px oben und unten, 9px an den Seiten */
  font-family:var(--mono);font-size:.75rem;letter-spacing:.04em;color:var(--ink-soft);
  background:var(--surface);border:1px solid var(--line);border-right:none;
  border-radius:var(--radius-m) 0 0 var(--radius-m);box-shadow:var(--lift-drawer);cursor:pointer;
  transition:transform .28s var(--ease), box-shadow .16s var(--ease), color .16s var(--ease);
}
/* Geschlossen laesst er sich 2px nach rechts druecken, wie die Filterpillen */
.tab[aria-expanded="false"]:hover{
  transform:translateY(-50%) translateX(2px);box-shadow:var(--lift-drawer-pressed);
}
/* Offen sitzt er an der linken Kante der Karte und teilt sich deren Schatten */
.tab[aria-expanded="true"]{
  transform:translateY(-50%) translateX(calc(-1 * var(--flyout-w)));
  color:var(--ink);box-shadow:var(--lift-drawer-open);
}
/* Beim Ueberfahren rueckt die ganze Schublade 2px Richtung "zu", Griff und
   Karte gemeinsam - nur den Griff zu verschieben wuerde ihn ueber die
   Kartenkante schieben. Die Karte ist im Markup das direkt folgende Element. */
.tab[aria-expanded="true"]:hover{transform:translateY(-50%) translateX(calc(2px - var(--flyout-w)))}
.tab[aria-expanded="true"]:hover + .flyout{transform:translateY(-50%) translateX(2px)}

/* Dasselbe Laempchen wie in den Filterpillen, nur mitgedreht: grau, solange
   die Karte zu ist, Berry mit Schein, sobald sie offen ist. */
.tab:hover .lamp{background:var(--lamp-hover)}
.tab[aria-expanded="true"] .lamp{background:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}

.flyout{
  position:fixed;right:0;top:34%;transform:translateY(-50%) translateX(100%);
  z-index:41;width:var(--flyout-w);padding:26px;
  background:var(--surface);border:1px solid var(--line);border-right:none;
  border-radius:var(--radius-l) 0 0 var(--radius-l);box-shadow:var(--lift-drawer-open);
  visibility:hidden;
  /* visibility schaltet erst nach der Fahrt um, sonst verschwindet die Karte
     schon waehrend sie noch hinausfaehrt */
  transition:transform .28s var(--ease), visibility 0s linear .28s;
}
.flyout[data-open="true"]{
  transform:translateY(-50%) translateX(0);visibility:visible;
  transition:transform .28s var(--ease), visibility 0s;
}
.flyout p{margin:0 0 20px;font-size:.9375rem;color:var(--ink-soft)}
.flyout-close{
  position:absolute;top:10px;right:12px;background:none;border:none;cursor:pointer;
  font-size:1.25rem;line-height:1;color:var(--ink-faint);padding:6px;
}

/* .btn war fuer Links gebaut. Auf dem UTM-Generator sitzt er einmal auf einem
   <button>, und dort legt der Browser von sich aus einen 2px breiten Rahmen um
   die Pille und zeigt beim Ueberfahren den Pfeil statt der Hand - der Reset
   oben setzt bei button nur Schrift und Farbe zurueck, nicht Rahmen und
   Zeiger. Beides hier abgestellt; an den <a class="btn"> aendert es nichts. */
.btn{
  display:inline-flex;align-items:center;gap:9px;padding:12px 20px;
  border:none;cursor:pointer;
  border-radius:var(--radius-pill);background:var(--accent);color:var(--accent-ink);
  font-size:.9375rem;font-weight:500;box-shadow:var(--lift);
  transition:transform .16s var(--ease), box-shadow .16s var(--ease);
}
/* Regel der Website: ein inaktiver Knopf reagiert auf nichts. Deshalb hängt
   an jeder Hover-Regel ein :not(:disabled) statt hinterher ein zweiter Block,
   der den Effekt wieder zurücknimmt - dort vergisst man leicht eine
   Eigenschaft, und der Knopf zuckt beim Überfahren doch. An <a class="btn">
   ändert das nichts: ein Link kann nicht disabled sein. */
.btn:hover:not(:disabled),.btn:active:not(:disabled){transform:translateY(2px);box-shadow:var(--lift-pressed)}
.btn svg{width:15px;height:15px;transition:transform .18s var(--ease)}
.btn:hover:not(:disabled) svg{transform:translateX(4px)}
.btn:disabled{opacity:.42;cursor:default;transform:none;box-shadow:none}

/* --- 8 Fuß ------------------------------------------------------- */
/* Der Fuss ist die einzige Navigation der Website: Startseite und "Ueber mich"
   neben den Rechtsseiten. Seine Links stehen als absolute Pfade da, waehrend
   sonst alles relativ verlinkt ist - nur so ist der Block auf allen sechs
   Seiten zeichengleich, und genau das soll er sein. Vom Server aus stimmen sie;
   oeffnet man eine Datei direkt von der Festplatte, greifen sie nicht, so wie
   der Favicon-Link auch nicht.
   Die eigene Seite bleibt im Fuss stehen, damit der Block sich nicht von Seite
   zu Seite unterscheidet. Sie traegt aria-current="page" - Vorlesesoftware
   meldet sie damit als aktuelle Seite - und steht im dunkleren Ton. */
.site-footer{
  border-top:1px solid var(--line);padding:32px 0 56px;
  font-family:var(--mono);font-size:.8125rem;color:var(--ink-faint);
}
.site-footer .wrap{display:flex;flex-wrap:wrap;gap:10px 26px;align-items:center}
.site-footer a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:3px}
.site-footer a[aria-current="page"]{color:var(--ink)}

/* --- 9 Responsive ------------------------------------------------ */
@media (max-width:960px){
  .grid{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:640px){
  :root{--gutter:20px}
  /* Aendert keine Schriftgroesse: die Elemente setzen ihre Groesse in rem,
     also am Wurzelelement. Wirksam ist die Zeile allein fuer --read, weil ch
     an der Schriftgroesse des Elements haengt - der Text wird also schmaler
     gemessen, nicht kleiner gesetzt. */
  body{font-size:16px}
  .masthead{padding:12px 0 40px}   /* 12px oben, die Seitenmarke steht darueber */
  .grid{grid-template-columns:1fr;padding-bottom:72px}
  .card{min-height:0}
  .article{padding:0 0 72px}     /* oben nur die Seitenmarke, 72px unten */

  /* Deutsche Komposita sind laenger als die Spalte: "Datenschutzerklaerung"
     als h1 schob die Seite bei 320px Fensterbreite um 44px auf, ohne dass ein
     Element ueber den Rand ragte - das Wort selbst lief aus seinem Kasten.
     hyphens:auto trennt an gueltiger Stelle, weil lang="de" gesetzt ist;
     overflow-wrap greift bei Woertern ohne Trennstelle im Woerterbuch.
     Nur hier, damit Ueberschriften auf breiten Fenstern ungetrennt bleiben. */
  h1,h2,h3,.card h2{hyphens:auto;overflow-wrap:break-word}

  .tab{
    top:auto;bottom:18px;right:18px;transform:none;
    writing-mode:horizontal-tb;padding:12px 18px;
    border:1px solid var(--line);border-radius:var(--radius-pill);
    box-shadow:var(--lift);
    display:flex;align-items:center;gap:8px;
  }
  .tab[aria-expanded="false"]:hover{transform:translateY(2px)}
  /* Unten faehrt die Karte von unten herein und wuerde den Zipfel verdecken -
     er fuehrt die Bewegung deshalb an und schiebt sich nach unten hinaus */
  .tab[aria-expanded="true"],
  .tab[aria-expanded="true"]:hover{transform:translateY(140%);box-shadow:none}

  .flyout{
    top:auto;bottom:0;left:0;right:0;width:auto;transform:translateY(100%);
    /* 16px oben: als Karte, die von unten hereinfaehrt, darf sie runder sein
       als die Karten auf der Flaeche - der bewusst einzige eigene Radius. */
    border-radius:16px 16px 0 0;border-right:1px solid var(--line);padding:26px 20px 34px;
  }
  .flyout[data-open="true"]{transform:translateY(0)}
  /* Die seitliche Hover-Regel des Zipfels gilt hier nicht - unten faehrt die
     Karte von unten herein und darf nicht seitlich verschoben werden */
  .tab[aria-expanded="true"]:hover + .flyout{transform:translateY(0)}
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;transition-duration:.01ms !important}
  .js .card,.js .station{opacity:1;transform:none}
}

/* --- 10 Ueber mich: Faehigkeiten-Matrix --------------------------- */
/* Die Seite hat genau zwei Breiten: Text laeuft in der Lesespalte (.wrap-read),
   Matrix und Liste stehen in einem eigenen, breiteren Container. Beide sind
   mittig gesetzt. Frueher hatte jeder Block seine eigene Breite - --read ist in
   ch angegeben und ergibt je nach Schriftgroesse des Elements andere Pixelwerte,
   deshalb nie direkt als max-width auf einem Block mit abweichender Schrift.

   Die Matrix ist eine Landkarte, keine Bewertung. x = wie weit ich selbst
   umsetze, y = wie weit ich die Richtung vorgebe. Die Position eines Punktes
   steht im HTML als --x, --y, --xk und --yk, jeweils als Zahl von 0 bis 100
   ohne Einheit. --yk ist freiwillig: fehlt es, bleibt der Punkt strategisch
   auf derselben Hoehe. Nur diese Werte pflegst du von Hand. */

.skills{margin-top:64px}                       /* 64px Abstand zum Fliesstext darueber */

.skills-head{margin:0 0 30px}                  /* 30px Abstand zum Umschalter */
.skills-head h2{
  margin:0 0 14px;font-size:1.375rem;font-weight:600;   /* 14px Abstand zum Absatz, Schriftgroesse 22px wie jede h2 */
  letter-spacing:-.02em;line-height:1.25;
}
.skills-head p{margin:0;color:var(--ink-soft);font-size:1.0625rem}

/* Umschalter ohne KI / mit KI. Zwei Radiofelder, bedient werden die
   Beschriftungen; die Felder selbst sind unsichtbar, aber fuer Tastatur und
   Screenreader vorhanden. */
.mode{
  margin:0 0 18px;padding:0;border:none;       /* 18px Abstand zur Grafik darunter */
  display:flex;align-items:center;gap:10px;    /* 10px zwischen den beiden Schaltflaechen */
}
.mode input{position:absolute;opacity:0;width:1px;height:1px}
/* Aussehen und Verhalten kommen von .pill, siehe Abschnitt 4. Hier steht nur,
   was der Umschalter zusaetzlich braucht: der Fokusrahmen muss vom versteckten
   Radiofeld auf die sichtbare Pille wandern. */
.mode input:focus-visible + .pill{outline:2px solid var(--accent);outline-offset:3px;border-radius:var(--radius-pill)}

/* Umschalter, Grafik und Beschreibungen stehen in einer gemeinsamen Karte.
   Vorher hatte jedes der drei Teile seine eigene Kante und die Gruppe zerfiel
   optisch in Bausteine. Der Plot behaelt innerhalb der Karte seinen Rahmen:
   ohne ihn haetten Kreuz und Eckbeschriftungen keine Flaeche mehr, auf die sie
   sich beziehen. Die beiden Rahmen stoeren sich nicht, weil der innere
   deutlich kleiner ist und wie ein Bild in der Karte liegt. */
.matrix-karte{
  border:1px solid var(--line);border-radius:var(--radius-l);
  padding:22px 24px 24px;                        /* 22px oben, 24px seitlich und unten */
  background:var(--surface);
}

/* Raster: Beschriftung der y-Achse links, Plot rechts, Beschriftung der
   x-Achse darunter. Der Plot ist quadratisch und schmaler als die Spalte -
   rechts daneben stehen die Beschriftungen der Punkte. */
.matrix{
  /* Die Breite des Plots steht hier einmal und gilt fuer Grafik und
     x-Achsenbeschriftung. Die Untergrenze von 260px ist noetig, weil
     100vh bei starkem Zoom klein wird: ohne sie rechnete die Formel gegen
     null und die Matrix verschwand - bei 400% Zoom auf 2x2px gemessen. */
  --plot-w:min(470px, max(260px, calc(100vh - 300px)));
  display:grid;
  grid-template-columns:auto minmax(0,1fr);
  grid-template-rows:auto auto;
  gap:8px;                           /* 8px zwischen Achsenbeschriftung und Plot - eng genug, dass die Texte am Rahmen haengen und nicht frei im Weissraum stehen */
}

.axis-y{
  grid-column:1;grid-row:1;margin:0;
  writing-mode:vertical-rl;transform:rotate(180deg);
  display:flex;justify-content:space-between;align-items:center;
  font-family:var(--mono);font-size:.75rem;     /* Schriftgroesse 12px - Untergrenze */
  letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);
  padding:6px 0;                                 /* 6px Innenabstand oben und unten */
}
.axis-x{
  grid-column:2;grid-row:2;margin:0;max-width:var(--plot-w);   /* gleiche Breite wie der Plot */
  display:flex;justify-content:space-between;
  font-family:var(--mono);font-size:.75rem;      /* Schriftgroesse 12px - Untergrenze */
  letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);
}

.plot{
  grid-column:2;grid-row:1;
  position:relative;aspect-ratio:1;
  /* Hoechstens 470px breit, der Rest der Spalte bleibt fuer die
     Beschriftungen der Punkte. Auf flachen Fenstern schrumpft der Plot mit,
     damit Grafik und Eintrag darunter zusammen ins Bild passen - aber nie
     unter 260px, siehe --plot-w. */
  max-width:var(--plot-w);
  border:1px solid var(--line);border-radius:var(--radius-m);
}
/* Das Kreuz teilt die vier Felder. Seit der Plot keinen eigenen Rahmen mehr
   hat, ist es die einzige Linie in der Flaeche - die Ecken markieren die
   Beschriftungen der vier Felder, die Enden der Skalen die Achsentexte. */
/* Das Kreuz gehoert hinter die Punkte. Ohne z-index entscheidet die
   Stapelreihenfolge: ::before wird vor den Kindern gezeichnet, ::after danach -
   die senkrechte Linie liefe also ueber Punkte und Beschriftungen. */
.plot::before,.plot::after{content:"";position:absolute;background:var(--line);z-index:0}
.plot::before{left:0;right:0;top:50%;height:1px}      /* waagerechte Trennlinie, 1px hoch */
.plot::after{top:0;bottom:0;left:50%;width:1px}       /* senkrechte Trennlinie, 1px breit */

.quad{
  position:absolute;width:50%;
  font-family:var(--mono);font-size:.75rem;     /* Schriftgroesse 12px - Untergrenze */
  letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);
  padding:14px 16px;pointer-events:none;        /* 14px Abstand oben und unten, 16px seitlich */
}
.quad-tl{top:0;left:0}
.quad-tr{top:0;right:0;text-align:right}
.quad-bl{bottom:0;left:0}
.quad-br{bottom:0;right:0;text-align:right}

/* Jeder Punkt deckt als Listenelement die ganze Flaeche ab. Erst dadurch
   beziehen sich die Prozentwerte seiner Kinder auf beide Achsen. */
/* Gezeigter Ausschnitt der Skala: 20 bis 100, nicht 0 bis 100. Unter 20 steht
   kein Feld, und mit dem vollen Bereich draengten sich alle Punkte im oberen
   Drittel. Die Umrechnung steckt in den calc-Formeln der Punkte:
   (Wert - 20) * 1.25 ergibt wieder 0 bis 100 Prozent der Flaeche. Aendert sich
   der Ausschnitt, sind vier Stellen anzufassen - Marker, Ring, Spur und der
   KI-Zustand - dazu die Laenge der Spur in script.js und die drei Wortmarken
   an beiden Achsen. Die Achsen tragen deshalb Worte und keine Zahlen: der
   untere Rand ist kein Nullpunkt, sondern die Stufe, die dort gilt. */
.points{list-style:none;margin:0;padding:0;position:absolute;inset:22px;z-index:1}  /* 22px Abstand der Skalenenden zum Rahmen, ueber dem Kreuz */
/* Das Listenelement deckt die ganze Flaeche ab und darf deshalb keine Klicks
   annehmen - sonst laege es wie eine unsichtbare Scheibe ueber allen anderen
   Punkten. Reagieren soll nur die Gruppe aus Punkt und Beschriftung. */
.skill{position:absolute;inset:0;pointer-events:none}

.skill .marker{
  position:absolute;
  left:calc((var(--x) - 20) * 1.25%);bottom:calc((var(--y) - 20) * 1.25%);
  transform:translateY(50%);
  display:flex;align-items:center;gap:9px;      /* 9px zwischen Punkt und Beschriftung */
  pointer-events:auto;
  transition:left .4s var(--ease), bottom .4s var(--ease);   /* 400ms Dauer der Wanderung */
}

/* Der Punkt ist eine Schaltflaeche, damit er auch per Tippen und per Tastatur
   erreichbar ist. Auf schmalen Fenstern erscheint dabei die Beschriftung als
   Kaertchen ueber dem Punkt, weil dort neben den Punkten kein Platz ist. */
.skill .dot{
  flex:none;width:24px;height:24px;             /* Punktdurchmesser 24px */
  border:none;padding:0;cursor:pointer;
  margin-left:-12px;                            /* halber Durchmesser, damit die Mitte auf dem x-Wert liegt */
  border-radius:50%;background:var(--accent);color:var(--accent-ink);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-size:.75rem;line-height:1;     /* Schriftgroesse der Nummer 12px */
  box-shadow:0 0 0 3px var(--surface);          /* 3px breiter Ring in Flaechenfarbe, trennt dicht liegende Punkte */
  transition:transform .3s ease;                /* 300ms Dauer, im Gleichklang mit dem Zuruecktreten der uebrigen Punkte */
}

.skill .name{
  font-size:.8125rem;line-height:1.25;white-space:nowrap;color:var(--ink-soft);   /* Schriftgroesse 13px */
  background:var(--surface);padding:1px 3px;margin-left:-3px;   /* deckend, damit Trennlinien nicht durch die Schrift laufen */
}

/* Beschriftung auf der linken Seite des Punktes. Noetig, wo rechts neben einem
   Punkt ein anderer Punkt steht: die Namen sind 60 bis 160px breit und wuerden
   sich sonst gegenseitig ueberdecken. Der Marker wird um seine eigene Breite
   nach links geschoben, damit seine rechte Kante auf dem x-Wert liegt; der
   negative Aussenabstand rueckt die Mitte des Punktes wieder genau darauf.

   Welche Seite passt, haengt vom Zustand ab, weil die Punkte beim Umschalten
   wandern: .label-links gilt im Ruhezustand, .label-links-ki im umgeschalteten.
   Deshalb wird im KI-Zustand zuerst alles auf rechts zurueckgesetzt und danach
   nur fuer .label-links-ki wieder nach links gestellt - die Reihenfolge der
   beiden Bloecke ist Absicht und darf nicht getauscht werden. */
.skill.label-links .marker{flex-direction:row-reverse;transform:translate(-100%, 50%)}
.skill.label-links .dot{margin-left:0;margin-right:-12px}   /* halber Punktdurchmesser, jetzt nach rechts */
.skill.label-links .name{margin-left:0;margin-right:-3px}

.skills:has(#modeKi:checked) .skill.label-links .marker,
.skills[data-mode="ki"] .skill.label-links .marker{flex-direction:row;transform:translate(0, 50%)}
.skills:has(#modeKi:checked) .skill.label-links .dot,
.skills[data-mode="ki"] .skill.label-links .dot{margin-left:-12px;margin-right:0}
.skills:has(#modeKi:checked) .skill.label-links .name,
.skills[data-mode="ki"] .skill.label-links .name{margin-left:-3px;margin-right:0}

.skills:has(#modeKi:checked) .skill.label-links-ki .marker,
.skills[data-mode="ki"] .skill.label-links-ki .marker{flex-direction:row-reverse;transform:translate(-100%, 50%)}
.skills:has(#modeKi:checked) .skill.label-links-ki .dot,
.skills[data-mode="ki"] .skill.label-links-ki .dot{margin-left:0;margin-right:-12px}
.skills:has(#modeKi:checked) .skill.label-links-ki .name,
.skills[data-mode="ki"] .skill.label-links-ki .name{margin-left:0;margin-right:-3px}

/* Ring an der Position ohne KI und die Spur dahin. Beide erscheinen erst im
   Zustand mit KI. Laenge und Winkel der Spur setzt script.js aus den vier
   Werten des Punktes - ohne Skript bleibt der Ring allein stehen. */
/* Ringe und Spuren liegen eine Ebene tiefer als die Punkte. Ohne das legt
   sich die Spur eines spaeter notierten Feldes ueber die Beschriftung eines
   frueheren - die Punkte sind Geschwister, und ohne z-index entscheidet allein
   die Reihenfolge im Dokument. */
.skill .ghost,.skill .trail{position:absolute;opacity:0;z-index:0;transition:opacity .3s var(--ease)}
.skill .marker{z-index:1}
.skill .ghost{
  left:calc((var(--x) - 20) * 1.25%);bottom:calc((var(--y) - 20) * 1.25%);
  width:24px;height:24px;margin-left:-12px;transform:translateY(50%);   /* gleicher Durchmesser wie der Punkt, 24px */
  border:1px solid var(--accent);border-radius:50%;background:var(--surface);
}
.skill .trail{
  left:calc((var(--x) - 20) * 1.25%);bottom:calc((var(--y) - 20) * 1.25%);
  width:0;height:0;border-top:1px dashed var(--accent);   /* Spurlinie 1px hoch */
  transform-origin:0 0;
}

/* Zustand mit KI: der Punkt wandert auf --xk und, falls gesetzt, auf --yk.
   Der zweite Selektor greift, sobald das Skript laeuft. */
.skills:has(#modeKi:checked) .skill .marker,
.skills[data-mode="ki"] .skill .marker{
  left:calc((var(--xk) - 20) * 1.25%);
  bottom:calc((var(--yk, var(--y)) - 20) * 1.25%);
}
.skills:has(#modeKi:checked) .skill .ghost,
.skills:has(#modeKi:checked) .skill .trail,
.skills[data-mode="ki"] .skill .ghost,
.skills[data-mode="ki"] .skill .trail{opacity:.32}   /* leise genug, dass zuerst die Punkte gelesen werden */

/* Ist ein Punkt angesteuert, treten die uebrigen zurueck. Das zeigt ohne jede
   Verdeckung, welcher Punkt gerade unten beschrieben wird - eine Vergroesserung
   allein reicht dafuer nicht, wenn die Punkte dicht beieinander liegen.

   Bewusst nur bis 45 Prozent Deckkraft und ueber 300ms: Faehrt man mit der Maus
   ueber mehrere Punkte, laeuft der Wechsel sonst als hartes Blinken ab. Die
   Kurve ist hier ease statt der sonst genutzten --ease, weil die am Anfang
   schnell ist und der Uebergang dadurch ruppig wirkt. */
.skill{transition:opacity .3s ease}          /* 300ms Dauer */
.skills.hat-aktiv .skill:not(.is-active){opacity:.45}
.skill.is-active .dot{transform:scale(1.25)}
.skill.is-active .name{color:var(--ink);font-weight:500}
.skill.is-active{z-index:2}

/* Liste unter der Matrix. Sie steht im HTML vollstaendig und offen - ohne
   JavaScript liest man sie am Stueck, auch ein Crawler ohne Skriptausfuehrung.
   Erst script.js setzt data-offen="false" und faltet sie zu einem Feld zusammen,
   in dem jeweils ein Eintrag sichtbar ist. Kein Text wird per Skript erzeugt.

   Der Bereich hat einen eigenen Rahmen und eine Ueberschrift. Ohne beides steht
   im zusammengefalteten Zustand nur ein Satz und ein Schalter auf der Seite,
   und niemand erkennt, dass das die Erklaerungen zur Grafik sind. */
/* Der Beschreibungsbereich gehoert zur selben Karte. Statt eines eigenen
   Rahmens trennt ihn eine Linie ueber die volle Kartenbreite von der Grafik -
   die negativen Raender heben dafuer den Innenabstand der Karte auf. */
.details{
  margin:26px -24px 0;                                  /* 26px Abstand zur Achsenbeschriftung, seitlich bis an die Kartenkante */
  border-top:1px solid var(--line);
  padding:20px 24px 0;                                  /* 20px Abstand unter der Linie, 24px seitlich wie die Karte */
}

.details-kopf{
  display:flex;align-items:center;justify-content:space-between;gap:16px;   /* 16px zwischen Ueberschrift und Schalter */
  margin-bottom:14px;                                   /* 14px Abstand zum Feld darunter */
}
/* Beide Bereiche der Karte tragen denselben Kopf. Gleiche Optik wie die
   Ueberschrift des Inhaltsverzeichnisses im Artikel: das liest sich als
   Bezeichnung des Bereichs, nicht als Textueberschrift. */
.bereich-titel{
  margin:0;font-family:var(--mono);font-size:.75rem;    /* Schriftgroesse 12px */
  font-weight:400;letter-spacing:.04em;color:var(--ink-faint);
}
.karte-kopf{margin-bottom:18px}                         /* 18px Abstand zum Umschalter darunter */
.karte-intro{
  margin:0 0 18px;font-size:.9375rem;color:var(--ink-soft);   /* 18px Abstand zum Umschalter, Schriftgroesse 15px */
  max-width:58ch;
}

.details-schalter{
  flex:none;
  font-family:var(--mono);font-size:.75rem;line-height:1;    /* Schriftgroesse 12px */
  padding:9px 14px;border:1px solid var(--line);border-radius:var(--radius-pill);
  background:var(--surface);cursor:pointer;box-shadow:var(--lift);
  transition:transform .16s var(--ease), box-shadow .16s var(--ease);
}
.details-schalter:hover,.details-schalter:active{transform:translateY(2px);box-shadow:var(--lift-pressed)}

/* Ruhetext und Eintraege liegen im zusammengefalteten Zustand an derselben
   Stelle uebereinander. Wuerde der Ruhetext darueber stehen, erschiene die
   Beschreibung eine Zeile tiefer als der Hinweis - das Auge springt dann bei
   jedem Ueberfahren hin und her. */
/* Die Deckkraft wechselt beim Auf- und Zuklappen, die Hoehe animiert
   script.js. Beides zusammen ergibt den Uebergang zwischen Feld und Liste. */
.details-feld{position:relative;transition:opacity .13s ease}   /* 130ms Dauer */
.details-ruhe{
  margin:0;font-size:.875rem;color:var(--ink-faint);    /* Schriftgroesse 14px */
  transition:opacity .22s ease;                         /* 220ms Dauer des Wechsels */
}
.details[data-offen="false"] .details-ruhe{position:absolute;left:0;right:0;top:0}
.skills.hat-aktiv .details[data-offen="false"] .details-ruhe{opacity:0}
.details[data-offen="true"] .details-ruhe{display:none}

/* Zwei Fassungen desselben Hinweises, je nachdem ob ein Zeiger da ist */
.nur-touch{display:none}
@media (hover:none){
  .nur-maus{display:none}
  .nur-touch{display:inline}
}

.skills-legend{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(2,1fr);gap:24px 30px; /* 24px zwischen Zeilen, 30px zwischen Spalten */
}
.skills-legend li{display:flex;gap:12px;align-items:flex-start}   /* 12px zwischen Nummer und Text */
.skills-legend .lnum{
  flex:none;width:24px;height:24px;             /* gleicher Durchmesser wie der Punkt, 24px */
  border-radius:50%;border:1px solid var(--line);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-size:.75rem;line-height:1;color:var(--ink-faint);     /* Schriftgroesse 12px */
  transition:background-color .3s ease, border-color .3s ease, color .3s ease;   /* 300ms Dauer, im Gleichklang mit der Grafik */
}
.skills-legend li.is-active .lnum{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
/* Eintrag einer Liste, keine Abschnittsueberschrift - deshalb 15px und nicht
   die 18px der Ueberschriftenskala. Im HTML ist es eine h4, weil der
   Bereichskopf "Die Felder im Einzelnen" darueber die h3 ist. */
.skills-legend h4{margin:0 0 5px;font-size:.9375rem;font-weight:600;line-height:1.3}  /* 5px Abstand zum Text, Schriftgroesse 15px */
.skills-legend p{margin:0;font-size:.875rem;line-height:1.5;color:var(--ink-soft)}    /* Schriftgroesse 14px */
/* Einordnung des Feldes als kurze Datenzeile, im selben Ton wie die Metazeile
   im Artikel. Sie steht im HTML, damit die Aussage der Grafik auch ohne Bild
   vorhanden ist - fuer Screenreader und fuer Sprachmodelle.
   Bewusst kurz und einzeilig: die Hoehe des zusammengefalteten Feldes richtet
   sich nach dem hoechsten Eintrag, und zwei Zeilen mehr je Eintrag liessen im
   Ruhezustand viel Leerraum stehen. */
.skills-legend .feld-lage{
  margin:0 0 5px;font-family:var(--mono);font-size:.75rem;line-height:1.4;   /* 5px Abstand zur Beschreibung, Schriftgroesse 12px */
  color:var(--ink-faint);
}

/* Zusammengefalteter Zustand: alle Eintraege liegen uebereinander, sichtbar ist
   der angesteuerte. Die Hoehe des Feldes setzt script.js aus dem hoechsten
   Eintrag - sonst muesste sie bei jeder Textaenderung von Hand nachgezogen
   werden, und bei zu knappem Wert springt die Grafik unter der Maus weg.
   Der Wert hier gilt nur, bis das Skript gemessen hat. */
.details[data-offen="false"] .skills-legend{
  display:block;position:relative;min-height:80px;   /* 80px Starthoehe, bis das Skript gemessen hat */
}
.details[data-offen="false"] .skills-legend li{
  position:absolute;left:0;right:0;top:0;
  opacity:0;pointer-events:none;transition:opacity .22s ease;   /* 220ms Dauer des Wechsels */
}
.details[data-offen="false"] .skills-legend li.is-active{opacity:1}


/* --- 11 Ueber mich: Stationen ------------------------------------- */
/* Jede Station hat drei Teile: die Aufgabe, die Erfolge und die Erfahrung.
   Beides steht bewusst als eigenes Feld und nicht im Fliesstext. */
.cv{margin-top:88px}   /* 88px Abstand zur Matrix darueber */
.cv h2{margin:0 0 30px;font-size:1.375rem;font-weight:600;letter-spacing:-.02em}  /* 30px Abstand zur ersten Station, Schriftgroesse 22px */

.timeline{list-style:none;margin:0;padding:0 0 0 26px;position:relative}  /* 26px Innenabstand links fuer Linie und Punkt */
.timeline::before{
  content:"";position:absolute;left:4px;top:8px;bottom:8px;width:1px;   /* Linie 1px breit, 4px vom linken Rand, 8px Abstand oben und unten */
  background:var(--line);
}

/* Zweite Linie in der Akzentfarbe, die beim Scrollen mitwaechst. Sie liegt
   genau auf der grauen und zeigt, wie weit man im Werdegang ist. Ohne
   JavaScript gibt es sie nicht - dann steht die graue Linie allein, und die
   Punkte sind von Anfang an gefuellt. */
.js .timeline::after{
  content:"";position:absolute;left:4px;top:8px;bottom:8px;width:1px;   /* deckungsgleich mit der grauen Linie */
  background:var(--accent);
  transform:scaleY(var(--fortschritt, 0));transform-origin:50% 0;
  will-change:transform;
}
.station{position:relative;padding-bottom:44px}   /* 44px Abstand zur naechsten Station */
.station:last-child{padding-bottom:0}
/* Der Punkt einer Station ist gefuellt, sobald die Linie ihn erreicht hat.
   Ohne JavaScript sind alle gefuellt - der Grundzustand ist deshalb gefuellt
   und wird erst mit aktivem Skript zurueckgenommen. */
.station::before{
  content:"";position:absolute;left:-26px;top:7px;   /* sitzt auf der Linie, 7px unter der Oberkante des Zeitraums */
  width:9px;height:9px;border-radius:50%;            /* Punktdurchmesser 9px */
  background:var(--accent);border:1px solid var(--accent);
  transition:background-color .3s var(--ease), border-color .3s var(--ease);   /* 300ms Dauer */
}
.js .station::before{background:var(--paper);border-color:var(--ink-faint)}
.js .station.is-erreicht::before{background:var(--accent);border-color:var(--accent)}

.cv-time{
  margin:0 0 6px;font-family:var(--mono);font-size:.75rem;   /* 6px Abstand zur Rolle, Schriftgroesse 12px */
  letter-spacing:.04em;color:var(--ink-faint);
}
.station h3{margin:0 0 4px;font-size:1.125rem;font-weight:600;line-height:1.35}  /* 4px Abstand zur Firmenzeile, Schriftgroesse 18px wie jede h3 */
.cv-org{
  display:block;font-family:var(--mono);font-size:.8125rem;   /* Schriftgroesse 13px */
  font-weight:400;color:var(--ink-soft);letter-spacing:.02em;
}
.station > p{margin:12px 0 0;color:var(--ink-soft)}  /* 12px Abstand zur Ueberschrift; Schriftgroesse 17px aus dem Fliesstext geerbt, damit sich der Lebenslauf so gut liest wie der Rest der Seite */
/* Was das Unternehmen macht, steht in einer eigenen Zeile ueber der Aufgabe.
   Sonst mischt sich die Beschreibung der Firma in die Beschreibung der Rolle,
   und man liest beim Ueberfliegen nicht mehr auseinander, was wovon gilt.
   Leiser gesetzt als die Aufgabe: Sie ist Einordnung, nicht die Aussage. */
/* Zwei Klassen im Selektor, damit die Regel die Schriftgroesse und die Farbe
   aus .station > p ueberschreibt - mit nur .cv-firma gewinnt der andere
   Selektor, weil er Klasse plus Element ist. */
.station > .cv-firma{
  margin:10px 0 0;font-size:.9375rem;line-height:1.5;color:var(--ink-faint);   /* 10px Abstand zur Firmenzeile, Schriftgroesse 15px */
}
.cv-firma + .cv-aufgabe{margin-top:10px}   /* 10px Abstand, wenn die Firmenbeschreibung darueber steht */

.cv-notes{margin-top:16px;display:grid;gap:12px}   /* 16px Abstand zum Absatz, 12px zwischen den beiden Feldern */
.note{padding-left:13px;border-left:2px solid var(--line)}   /* 13px Innenabstand links, Strich 2px breit */
/* Dieselbe Beschriftung steht ueber der Firmenbeschreibung und ueber der
   Aufgabe (.cv-label), damit man beim Lesen nicht selbst herleiten muss, was
   welcher Absatz ist. Vier gleich gesetzte Marken je Station: Unternehmen,
   Aufgabe, Erfolge, Erfahrung. */
.note-label,.cv-label{
  display:block;margin-bottom:3px;font-family:var(--mono);font-size:.75rem;     /* 3px Abstand zum Text, Schriftgroesse 12px */
  letter-spacing:.09em;text-transform:uppercase;color:var(--ink-faint);
}
.note p{margin:0;color:var(--ink-soft)}  /* Schriftgroesse 17px und Zeilenhoehe 1,6 aus dem Fliesstext geerbt - Erfolge und Erfahrung sind die wichtigeren Inhalte und stehen deshalb nicht kleiner da als die Aufgabe */
/* Die Akzentfarbe markiert die Erfolge. Die Erfahrung steht gleichberechtigt
   daneben - sie kann aus einem Erfolg kommen oder aus einem Fehler, und beides
   soll gleich viel wert sein. */
.note-akzent{border-left-color:var(--accent)}

/* Stationen blenden beim Scrollen ein, wie die Karten auf der Startseite.
   Die erste bleibt sichtbar, aus demselben Grund wie dort. */
/* Mehrere Positionen beim selben Unternehmen: Die oberste traegt den
   Firmennamen, jede weitere bekommt .folge - kein zweiter Firmenname, ein
   kleinerer Punkt auf der Zeitleiste und weniger Abstand nach oben. So liest
   sich die Gruppe als ein Unternehmen mit mehreren Rollen, ohne dass eine
   zusaetzliche Ebene oder eine Einrueckung noetig waere. Die Reihenfolge
   innerhalb der Gruppe ist dieselbe wie im ganzen Werdegang: neueste oben. */
.station:has(+ .station.folge){padding-bottom:20px}   /* 20px statt 44px, solange die naechste Position zum selben Unternehmen gehoert */
.station.folge::before{
  left:-24px;top:8px;width:6px;height:6px;           /* Punktdurchmesser 6px statt 9px, sitzt weiter auf derselben Linie */
}

.js .station:nth-child(n+2){opacity:0;transform:translateY(14px)}   /* 14px Versatz vor dem Einblenden */
.js .station.is-visible{
  opacity:1;transform:none;
  transition:opacity .55s var(--ease), transform .55s var(--ease);   /* 550ms Dauer */
}

/* Kamm zum Comb-Shaped Marketer im Fliesstext der Seite "Ueber mich": die
   Leiste oben ist die Breite ueber alle Disziplinen, die Zinken sind die Tiefe
   je Disziplin.

   Vorher stand die Form allein zwischen zwei Absaetzen, ohne Bezug zu etwas
   anderem auf der Seite. Jetzt ist sie bemasst wie in einer
   Konstruktionszeichnung: oben ein Massband ueber die ganze Breite, rechts
   eines ueber die Hoehe, dazu die beiden Woerter, die auch an den Achsen der
   Landkarte stehen. Damit steht keine Schrift frei im Weissraum - dieselbe
   Regel wie bei der Matrix.

   Der Aufbau ist ein Raster aus drei Zeilen und zwei Spalten. Spalte 1 ist die
   Form und gibt die Breite vor, Spalte 2 haelt die Tiefenbemassung. Beide
   Baender sind leere div, die sich in ihrer Rasterzelle aufspannen: ihre
   Laenge haengt damit an der Form und nicht an einer zweiten Zahl. */
.prose .kamm-figur{
  display:grid;
  grid-template-columns:auto auto;      /* Form, Tiefenbemassung */
  grid-template-rows:auto auto auto;    /* Wort, Massband, Form */
  justify-content:start;
  margin:22px 0 30px;                   /* 22px Abstand zum Absatz darueber, 30px zum naechsten */
  color:var(--ink-faint);               /* Farbe von Woertern, Linien und Pfeilspitzen */
}
.prose .kamm{
  display:block;grid-column:1;grid-row:3;
  width:210px;max-width:100%;height:auto;   /* 210px Breite der Form; Hoehe folgt der viewBox, 69px bei 210px */
}
.prose .kamm-label{
  margin:0;
  font-family:var(--mono);font-size:.75rem;line-height:1;   /* 12px Schriftgroesse, wie jede Achsenbeschriftung */
  letter-spacing:.09em;text-transform:uppercase;color:inherit;
}
.prose .kamm-breite{
  grid-column:1;grid-row:1;
  text-align:center;margin-bottom:6px;   /* 6px Abstand zum Massband darunter */
}

/* Die beiden Massbaender. Die Linie ist der Hintergrund des Elements, die
   beiden Pfeilspitzen sind ::before und ::after - je ein Quadrat mit zwei
   Raendern, um 45 Grad gedreht. Alles in currentColor, damit Linie, Spitzen
   und Wort dieselbe Farbe haben und sich nur an einer Stelle aendern. */
.prose .kamm-mass-x,
.prose .kamm-mass-y{position:relative}
.prose .kamm-mass-x::before,.prose .kamm-mass-x::after,
.prose .kamm-mass-y::before,.prose .kamm-mass-y::after{
  content:"";position:absolute;
  width:5px;height:5px;                  /* 5px Kantenlaenge der Pfeilspitze */
  border-left:1px solid currentColor;    /* 1px Strichstaerke */
  border-bottom:1px solid currentColor;
}

/* Waagerecht ueber die Breite der Form */
.prose .kamm-mass-x{
  grid-column:1;grid-row:2;
  height:9px;margin-bottom:7px;          /* 9px Hoehe des Bands, 7px Abstand zur Form */
  background:linear-gradient(currentColor,currentColor) left center/100% 1px no-repeat;
}
.prose .kamm-mass-x::before{left:0;top:50%;transform:translateY(-50%) rotate(45deg)}
.prose .kamm-mass-x::after{right:0;top:50%;transform:translateY(-50%) rotate(-135deg)}

/* Senkrecht ueber die Hoehe der Form. Das Band steht in derselben Rasterzeile
   wie die Form und wird von ihr auf deren Hoehe gezogen. */
.prose .kamm-tiefe{
  grid-column:2;grid-row:3;
  display:flex;align-items:stretch;gap:7px;   /* 7px zwischen Band und Wort */
  margin-left:9px;                            /* 9px Abstand zur Form */
}
.prose .kamm-mass-y{
  width:9px;flex:none;                        /* 9px Breite des Bands */
  background:linear-gradient(currentColor,currentColor) center top/1px 100% no-repeat;
}
.prose .kamm-mass-y::before{top:0;left:50%;transform:translateX(-50%) rotate(135deg)}
.prose .kamm-mass-y::after{bottom:0;left:50%;transform:translateX(-50%) rotate(-45deg)}
/* Das Wort laeuft von unten nach oben, wie die Beschriftung einer senkrechten
   Achse. writing-mode dreht die Zeile, die Drehung um 180 Grad kehrt die
   Leserichtung von oben-nach-unten auf unten-nach-oben. */
.prose .kamm-tiefe .kamm-label{
  writing-mode:vertical-rl;transform:rotate(180deg);
  align-self:center;
}

/* Privater Abschnitt am Seitenende. Eigene Ueberschrift auf derselben Kante
   wie Landkarte und Stationen, damit er nicht als Anhang des Lebenslaufs
   gelesen wird. */
.privat{margin-top:88px}   /* 88px Abstand zu den Stationen darueber, derselbe Wert wie von den Stationen zur Matrix */
.privat h2{
  margin:0 0 14px;                                      /* 14px Abstand zum Absatz */
  font-size:1.375rem;font-weight:600;                   /* Schriftgroesse 22px wie jede h2 */
  letter-spacing:-.02em;line-height:1.25;
}

/* --- 12 Ueber mich: Responsive ------------------------------------ */
@media (max-width:720px){
  .skills-legend{grid-template-columns:1fr;gap:22px}   /* 22px zwischen den Eintraegen, wenn einspaltig */
  .matrix-karte{padding:18px 16px 18px}               /* 18px oben und unten, 16px seitlich */
  .details{margin:20px -16px 0;padding:18px 16px 0}   /* 20px Abstand zur Grafik, seitlich bis an die Kartenkante */
}
@media (max-width:760px){
  /* Neben den Punkten ist hier kein Platz fuer Beschriftungen. Den Namen liest
     man im Feld unter der Grafik; welcher Punkt gemeint ist, zeigt die
     Hervorhebung - die uebrigen Punkte treten dabei zurueck. */
  .skill .name{display:none}
  .skill .dot{width:22px;height:22px;margin-left:-11px}   /* Punktdurchmesser 22px, halber Wert als Versatz */
  .skill .ghost{width:22px;height:22px;margin-left:-11px}
  /* Die Beschriftungen der vier Felder entfallen hier. Mit der Untergrenze von
     12px brechen sie zweizeilig um, und auf einem 221px breiten Plot liegen
     Punkte mit Randwerten dann darauf. Was die Achsen bedeuten, steht weiter
     unten in Worten an jedem Eintrag der Felderliste - die vier Feldnamen
     waren auf dieser Groesse ohnehin nur noch Beiwerk. */
  .quad{display:none}
  /* 22px statt 16px: auf schmalen Fenstern liegen die Punkte sonst zu dicht
     an den Ecken des Rahmens. */
  .points{inset:22px}                          /* 22px Abstand der Skalenenden zum Rahmen */
  .matrix{gap:6px}                             /* 6px zwischen Achsenbeschriftung und Plot */
  /* Keine Schrift unter 12px, auch hier nicht - die Achsentexte duerfen
     stattdessen umbrechen. */
}

/* --- 13 Bewegungsreduzierung, Nachtrag ---------------------------- */
/* Muss hinter Abschnitt 11 stehen: eine Regel gleicher Spezifitaet weiter oben
   in der Datei wuerde von den Stationen-Regeln wieder ueberschrieben.
   Bei reduzierter Bewegung laeuft die Zeitleiste nicht mit - Linie und Punkte
   stehen dann von Anfang an vollstaendig da. */
@media (prefers-reduced-motion:reduce){
  .js .timeline::after{transform:scaleY(1)}
  .js .station::before{background:var(--accent);border-color:var(--accent)}
}

/* --- 14 Fehlerseite ------------------------------------------------ */
/* Die Ueberschrift der 404.html: "Drink not found", das "Dr" wird
   durchgestrichen und ein "L" von Hand darueber geschrieben. Beides sind
   SVG-Pfade mit pathLength="1": dasharray und dashoffset von 1 blenden den
   Strich aus, die Animation zieht ihn bis 0 auf. Erst die Streichung, dann
   das L, einmal nach dem Laden. Einzelheiten in doku/seiten/404.md. */
.fehler-titel{
  font-family:var(--mono);font-weight:700;letter-spacing:-.02em;
  padding-top:.9em;               /* Platz fuer das L ueber der Zeile */
}
.korrektur{position:relative;display:inline-block}
.korrektur svg{
  position:absolute;overflow:visible;fill:none;
  stroke:var(--accent);stroke-linecap:round;stroke-linejoin:round;
}
.korrektur-strich{left:-.06em;top:0;width:calc(100% + .12em);height:100%;stroke-width:3.2}
.korrektur-l{
  left:50%;top:-.95em;width:.7em;height:.9em;stroke-width:3.4;
  transform:translateX(-50%) rotate(-4deg);
}
/* Bis zum Start unsichtbar: Die runden Linienenden zeichnen sonst auch bei
   Strichlaenge 0 einen Punkt, der vor der Animation ueber dem Text stuende. */
.korrektur path{stroke-dasharray:1 2;stroke-dashoffset:1;opacity:0;animation:korrektur-zeichnen .35s var(--ease) 1.1s forwards}
.korrektur-l path{animation-duration:.45s;animation-delay:1.75s}
@keyframes korrektur-zeichnen{from{opacity:1;stroke-dashoffset:1}to{opacity:1;stroke-dashoffset:0}}
/* Bei reduzierter Bewegung steht gleich das Endbild da. Die allgemeine Regel
   in Abschnitt 9 kuerzt nur die Dauer, nicht die Verzoegerung - ohne diese
   Zeile stuende die Ueberschrift anderthalb Sekunden lang ohne Korrektur da. */
@media (prefers-reduced-motion:reduce){
  .korrektur path{animation:none;stroke-dashoffset:0;opacity:1}
}
