/* app.css — Farbleiter, Chrome und Komponenten.
 *
 * Drei Skins, nicht zwei: light, dark, grey. Die Wahl steht in data-skin, ihre
 * Polaritaet in data-theme, damit Grau ein *dunkler* Skin ist und Code, der auf
 * data-theme verzweigt, weiter stimmt. Die Leiter ist die --tool-*-Leiter von
 * alber.me, Werte uebernommen (OKLab, Hue 250, gleichmaessige Helligkeitsstufen),
 * damit die Projektseite unter alber.me/infinite-alphabets nicht aus dem Rahmen
 * faellt. Die Rollen der alten Variablen (--bg, --ink, --accent) sind darauf
 * abgebildet und bleiben erhalten, damit die SVG-Regeln unten unveraendert gelten.
 *
 * Grau unterscheidet Flaechen ueber den Umriss, nicht ueber die Fuellung: keine
 * Schatten, keine Flaechenakzente. Deshalb ist dort --tool-surface gleich der
 * Seitenfarbe und --tool-border weiss.
 */

/* === DUNKEL (Standard) === */
html[data-theme="dark"] {
  --tool-emph:     #e1e8f0;
  --tool-text:     #bcc5cf;
  --tool-label:    #99a4af;
  --tool-note:     #778390;
  --tool-faint:    #566472;
  --tool-border:   #374656;
  --tool-hairline: #1a2a3a;
  --tool-grid:     #45596d;
  --tool-ink:      #0a1928;
  --tool-panel:    #0a1928;
  --tool-surface:  #111c28;
  --tool-subtle:   #17232f;
  --tool-shadow:   rgba(0, 0, 0, .45);
  --tool-panel-shadow: 0 12px 28px var(--tool-shadow);
  --tool-sig-good: #4ade80;
  --tool-sig-warn: #facc15;
  --tool-sig-bad:  #f87171;
  --tool-sig-info: #38bdf8;
  --tool-ok-bg:     #052e16;
  --tool-ok-border: #15803d;
  --tool-ok-text:   #bbf7d0;
  --tool-warn-bg:     #422006;
  --tool-warn-border: #a16207;
  --tool-warn-text:   #fde68a;
  --tool-danger-bg:     #7f1d1d;
  --tool-danger-border: #b91c1c;
  --tool-danger-text:   #fecaca;
  --tool-info-bg:     #172554;
  --tool-info-border: #1d4ed8;
  --tool-info-text:   #bfdbfe;

  --page:   #14181f;
  --accent: #7d9bb8;
  --accent-hover: #9db8d2;
  --accent-soft:  rgba(125, 155, 184, .20);
  --blue:      #38bdf8;
  --blue-soft: rgba(56, 189, 248, .16);
}

/* === HELL === */
html[data-theme="light"] {
  --tool-emph:     #0c1c2b;
  --tool-text:     #293746;
  --tool-label:    #485563;
  --tool-note:     #687481;
  --tool-faint:    #8b96a1;
  --tool-border:   #afb8c1;
  --tool-hairline: #d5dce3;
  --tool-grid:     #d5dce3;
  --tool-ink:      #ecf3fb;
  --tool-panel:    #ecf3fb;
  --tool-surface:  #ffffff;
  --tool-subtle:   #f1f5f9;
  --tool-shadow:   rgba(15, 30, 50, .10);
  --tool-panel-shadow: 0 1px 3px rgba(15, 30, 50, .08);
  --tool-sig-good: #15803d;
  --tool-sig-warn: #a16207;
  --tool-sig-bad:  #b91c1c;
  --tool-sig-info: #0369a1;
  --tool-ok-bg:     #f0fdf4;
  --tool-ok-border: #bbf7d0;
  --tool-ok-text:   #166534;
  --tool-warn-bg:     #fffbeb;
  --tool-warn-border: #f59e0b;
  --tool-warn-text:   #92400e;
  --tool-danger-bg:     #fee2e2;
  --tool-danger-border: #dc2626;
  --tool-danger-text:   #7f1d1d;
  --tool-info-bg:     #eff6ff;
  --tool-info-border: #93c5fd;
  --tool-info-text:   #1d4ed8;

  --page:   #f8fafc;
  --accent: #1f4a7a;
  --accent-hover: #2c6099;
  --accent-soft:  rgba(31, 74, 122, .10);
  --blue:      #0369a1;
  --blue-soft: rgba(3, 105, 161, .10);
}

/* === GRAU ===
 * Dreifach wiederholtes Attribut wie in der Vorlage: der Skin muss die
 * data-theme-Bloecke oben schlagen, egal wo er in der Datei steht. */
html[data-skin="grey"][data-skin="grey"][data-skin="grey"] {
  --tool-emph:     #ffffff;
  --tool-text:     #f2f4f5;
  --tool-label:    #dfe3e5;
  --tool-note:     #c9cfd2;
  --tool-faint:    #b0b7bb;
  --tool-border:   #ffffff;
  --tool-hairline: #9aa1a6;
  --tool-grid:     #b0b7bb;
  --tool-ink:      #22262b;
  --tool-panel:    #55595f;
  --tool-surface:  #55595f;
  --tool-subtle:   #5c6167;
  --tool-shadow:   transparent;
  --tool-panel-shadow: none;
  /* In einem achromatischen Skin ist die einzige verbleibende Achse die
     Helligkeit, also bekommt sie das, was der Leser nicht uebersehen darf:
     gut tritt zurueck, die Warnung liegt darueber. Info behaelt das Tuerkis.
     Rot ist die ausdrueckliche Ausnahme vom achromatischen Skin: ein Konflikt
     oder ein Parserfehler ist die eine Aussage, die auch hier ins Auge springen
     soll. Der Ton ist so gewaehlt, dass er auf #55595f noch traegt. */
  --tool-sig-good: #b3babe;
  --tool-sig-warn: #d9dde0;
  --tool-sig-bad:  #ff8a80;
  --tool-sig-info: #6fe3d2;
  /* Callouts verlieren ihre Farbe und behalten ihren Kasten. Der Schweregrad
     ist der Rand: durchgehend weiss beim Fehler, eine Stufe runter bei der
     Warnung, Haarlinie beim Rest. */
  --tool-ok-bg:     transparent;
  --tool-ok-border: #9aa1a6;
  --tool-ok-text:   #f2f4f5;
  --tool-warn-bg:     transparent;
  --tool-warn-border: #dfe3e5;
  --tool-warn-text:   #f2f4f5;
  --tool-danger-bg:     transparent;
  --tool-danger-border: #ff8a80;
  --tool-danger-text:   #ff8a80;
  --tool-info-bg:     transparent;
  --tool-info-border: #6fe3d2;
  --tool-info-text:   #f2f4f5;

  --page:   #55595f;
  --accent: #6fe3d2;
  --accent-hover: #93ece0;
  /* Tuerkis ist die einzige Farbe, die dieser Skin hat, und war vorher nur am
     Rand zu sehen. Die weichen Toene sind jetzt eine sehr schwache Waesche
     statt transparent: das gibt akzeptierenden Zustaenden im Graphen und der
     aktiven Zeile wieder eine sichtbare Flaeche, ohne die Regel zu brechen,
     denn das unterscheidende Merkmal bleibt die Form (Doppelkreis, Strichlung).
     --blue-soft muss sich davon absetzen, weil beide im selben Graphen liegen:
     komplement-akzeptierend bekommt deshalb die kraeftigere Waesche. */
  --accent-soft:  rgba(111, 227, 210, .14);
  --blue:      #6fe3d2;
  --blue-soft: rgba(111, 227, 210, .30);
}

/* Rollen, die der alte Code unter seinen Namen liest. Eine Ebene Indirektion,
   damit die SVG- und Tabellenregeln unten nicht dreimal existieren muessen. */
:root {
  --bg:    var(--page);
  --panel: var(--tool-surface);
  --ink:   var(--tool-text);
  --muted: var(--tool-note);
  --line:  var(--tool-hairline);
  --edge:  var(--tool-grid);
  --hot:   var(--tool-sig-bad);
  --ok:    var(--tool-sig-good);
  --warn:  var(--tool-sig-warn);
  --err:   var(--tool-sig-bad);
  --mono: "DejaVu Sans Mono", "Consolas", "Liberation Mono", monospace;
}

/* Scrollbalken. Ein Balken in Systemfarbe ist im grauen Skin ein blauer Streifen
   neben lauter Achromatischem, und im hellen Skin ein dunkler Rahmen um eine
   weisse Flaeche. Zwei Mechanismen, weil sie sich noch nicht ueberall decken:
   das standardisierte scrollbar-color (Firefox, neuere Chromium) und die
   ::-webkit-scrollbar-Pseudoelemente. Wer beide kann, bekommt das erste, weil
   scrollbar-color die Pseudoelemente ausschaltet. */
html {
  scrollbar-color: var(--tool-border) var(--tool-panel);
  scrollbar-width: thin;
  /* Breite der zentrierten Spalte, an der sich Header und Footer ausrichten.
     Sie entspricht der max-width von main, das auf den Prosaseiten schmaler
     ist als auf den Werkzeugseiten. */
  --site-col: 1700px;
}
html:has(main.prose) { --site-col: 780px; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--tool-panel); }
::-webkit-scrollbar-thumb {
  background: var(--tool-border);
  border-radius: 6px;
  border: 2px solid var(--tool-panel);
}
::-webkit-scrollbar-thumb:hover { background: var(--accent); }
::-webkit-scrollbar-corner { background: var(--tool-panel); }

/* Die Textauswahl gehoert genauso in die Leiter wie der Scrollbalken, und aus
   demselben Grund: der Balken des Systems ist in einem nicht fokussierten
   Fenster ein helles Grau. Im dunklen Skin steht dann heller Text auf heller
   Flaeche, also nichts Lesbares mehr, und zwar ausgerechnet dort, wo jemand
   gerade gezogen hat. Akzent als Flaeche, die Gegenfarbe als Schrift. */
::selection { background: var(--accent); color: var(--tool-ink); }

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Charter", "Georgia", "Times New Roman", serif;
  font-size: 16px;
  line-height: 1.55;
}

a { color: var(--accent); }
a:hover { color: var(--accent-hover); }

/* ===================================================================== */
/* AFFORDANZ: drei Klassen, drei Formen.                                 */
/*                                                                       */
/* Die Seite hatte Elemente, denen man nicht ansah, ob sie anklickbar    */
/* sind: Badges sahen aus wie Schaltflaechen, Tabellenzeilen und Zeugen- */
/* Links waren klickbar, ohne es zu zeigen. Ab hier gilt durchgehend:    */
/*                                                                       */
/*   AKTION   .btn        erhaben, Rahmen + Fuellung, cursor: pointer,   */
/*                        reagiert auf hover/active. Tut etwas.          */
/*   AUSWAHL  .seg        flach, geteilte Kanten, aktives Segment traegt */
/*                        den Akzent. Waehlt zwischen Zustaenden.        */
/*   ANZEIGE  .badge      abgerundete Kapsel, kein Rahmen in Buttonfarbe,*/
/*             .verdict   cursor bleibt default. Reagiert auf nichts.    */
/*                                                                       */
/* Klickbares, das kein Kasten sein kann (Tabellenzeile, Zeuge im Fliess-*/
/* text), bekommt .is-clickable bzw. .witness-link: unterstrichen und mit*/
/* Hover-Fuellung, damit die Klickbarkeit sichtbar ist, ohne dass daraus */
/* eine vierte Buttonform wird.                                          */
/* ===================================================================== */

/* --- AKTION ---
   Rechteck, umlaufender Rahmen, leichte Erhebung: eine Flaeche, die man
   druecken kann. Der Schatten ist absichtlich klein; im grauen Skin ist er
   none (--tool-panel-shadow), dort tragen Rahmen und Hover die Aussage. */
button,
.btn {
  font-family: inherit;
  font-size: 0.9rem;
  color: var(--tool-emph);
  border: 1px solid var(--tool-border);
  background: var(--tool-subtle);
  border-radius: 4px;
  padding: 0.32rem 0.7rem;
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease, color .12s ease,
    transform .06s ease;
}
/* Hausregel von alber.me: jede Schaltflaeche beginnt mit einem Icon. Das ist
   das schnellste Merkmal, um Schaltflaechen von Auswahlsegmenten (.seg) und
   Anzeigen (.badge) zu unterscheiden, noch vor Rahmen und Hover, und es traegt
   auch im grauen Skin, wo die Farbe als Unterscheidung ausfaellt.

   Das Icon kommt als CSS-Maske herein und wird in currentColor gefaerbt, wie
   schon beim Theme-Umschalter: so folgt es Hover, :active und .primary ohne
   eigene Regel und ohne eine zweite Farbe ins Bild zu bringen. Welches Icon,
   sagt data-icon am Knopf; ohne Angabe steht ein neutraler Punkt da, damit ein
   spaeter ergaenzter Knopf nicht als einziger ohne Icon dasteht.

   .seg ist ausgenommen: dort waere ein Icon genau das Merkmal, das die beiden
   Rollen wieder ununterscheidbar macht. */
/* Kein Flex-Container: als Flex-Item wuerde jedes <sub> im Beschriftungstext zu
   einer eigenen Box und verloere seine Grundlinie, aus "A<sub>n</sub>" wuerde
   "A n". Der Text bleibt deshalb ein normaler Inline-Fluss, und das Icon ist
   ein inline-block davor, das ueber vertical-align auf der Zeile sitzt. */
button:not(.seg),
.btn:not(.seg) {
  white-space: nowrap;
}
button:not(.seg)::before,
.btn:not(.seg)::before {
  content: "";
  display: inline-block;
  vertical-align: -0.11em;
  margin-right: 0.42em;
  width: 0.92em;
  height: 0.92em;
  background-color: currentColor;
  -webkit-mask: var(--icon, url('../img/icons/ui-dot.svg')) no-repeat center / contain;
  mask: var(--icon, url('../img/icons/ui-dot.svg')) no-repeat center / contain;
}
button[data-icon="play"],       .btn[data-icon="play"]       { --icon: url('../img/icons/ui-play.svg'); }
button[data-icon="check"],      .btn[data-icon="check"]      { --icon: url('../img/icons/ui-check.svg'); }
button[data-icon="merge"],      .btn[data-icon="merge"]      { --icon: url('../img/icons/ui-merge.svg'); }
button[data-icon="replace"],    .btn[data-icon="replace"]    { --icon: url('../img/icons/ui-replace.svg'); }
button[data-icon="add"],        .btn[data-icon="add"]        { --icon: url('../img/icons/ui-square-plus.svg'); }
button[data-icon="filter"],     .btn[data-icon="filter"]     { --icon: url('../img/icons/ui-filter.svg'); }
button[data-icon="minimize"],   .btn[data-icon="minimize"]   { --icon: url('../img/icons/ui-minimize.svg'); }
button[data-icon="scissors"],   .btn[data-icon="scissors"]   { --icon: url('../img/icons/ui-scissors.svg'); }
button[data-icon="link"],       .btn[data-icon="link"]       { --icon: url('../img/icons/ui-link.svg'); }
button[data-icon="code"],       .btn[data-icon="code"]       { --icon: url('../img/icons/ui-code.svg'); }
button[data-icon="eraser"],     .btn[data-icon="eraser"]     { --icon: url('../img/icons/ui-eraser.svg'); }
button[data-icon="skip-back"],  .btn[data-icon="skip-back"]  { --icon: url('../img/icons/ui-skip-back.svg'); }
button[data-icon="skip-fwd"],   .btn[data-icon="skip-fwd"]   { --icon: url('../img/icons/ui-skip-forward.svg'); }
button[data-icon="prev"],       .btn[data-icon="prev"]       { --icon: url('../img/icons/ui-chevron-left.svg'); }
button[data-icon="next"],       .btn[data-icon="next"]       { --icon: url('../img/icons/ui-chevron-right.svg'); }
button[data-icon="undo"],       .btn[data-icon="undo"]       { --icon: url('../img/icons/ui-undo.svg'); }
button[data-icon="rotate"],     .btn[data-icon="rotate"]     { --icon: url('../img/icons/ui-rotate.svg'); }
button[data-icon="layers"],     .btn[data-icon="layers"]     { --icon: url('../img/icons/ui-layers.svg'); }
button[data-icon="plus"],       .btn[data-icon="plus"]       { --icon: url('../img/icons/ui-plus.svg'); }

/* Knoepfe, die nur aus einem Icon bestehen (die Schrittleiste): der Text ist
   dort ohnehin im title und im aria-label, das Icon traegt allein. */
button.icon-only { padding-left: 0.55rem; padding-right: 0.55rem; }
button.icon-only::before { width: 1.05em; height: 1.05em; margin-right: 0; }

/* Das i am Panel ist ein Knopf, aber ein runder mit dem Buchstaben als Zeichen:
   es traegt kein zweites Icon davor. */
button.info-btn::before { content: none; }

/* Der Druck-Effekt: einen halben Pixel tiefer beim Klicken. Kein Schatten, der
   sich bewegt, weil der graue Skin gar keinen hat und die Bewegung dort dann
   fehlen wuerde. */
button:active:not(:disabled),
.btn:active:not(:disabled) { transform: translateY(1px); }
button:hover:not(:disabled),
.btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent) 16%, var(--tool-subtle));
  border-color: var(--accent);
  color: var(--tool-emph);
}
button:active:not(:disabled),
.btn:active:not(:disabled) {
  background: color-mix(in srgb, var(--accent) 26%, var(--tool-subtle));
}
button:disabled,
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
button:focus-visible,
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Rolle: die gefuellte Akzentflaeche (Hauptknopf, geoeffnetes i). Im hellen
   Skin ist --accent ein sehr dunkles Blau, entworfen als Schriftfarbe fuer
   Links. Als Flaeche wird daraus ein fast schwarzer Block: Kontrast zur Seite
   etwa 12:1, waehrend derselbe Knopf in Dunkel und Grau in einem Mittelton bei
   etwa 6:1 steht. Hell nimmt deshalb die Hover-Stufe als Ruhezustand und geht
   beim Hover nach unten statt nach oben, weil mehr Betonung auf hellem Grund
   dunkler heisst und nicht heller. Kein neuer Farbwert, nur die beiden
   vorhandenen Stufen getauscht. */
:root { --accent-fill: var(--accent); --accent-fill-hover: var(--accent-hover); }
html[data-theme="light"] { --accent-fill: var(--accent-hover); --accent-fill-hover: var(--accent); }

button.primary,
.btn.primary {
  background: var(--accent-fill);
  color: var(--tool-ink);
  border-color: var(--accent-fill);
  font-weight: 600;
}
button.primary:hover:not(:disabled),
.btn.primary:hover:not(:disabled) {
  background: var(--accent-fill-hover);
  border-color: var(--accent-fill-hover);
  color: var(--tool-ink);
}

/* --- AUSWAHL ---
   Ecken folgen der DOM-Nachbarschaft, ein einzelnes Segment braucht also
   keinen Sonderfall. Jedes Segment behaelt alle vier eigenen Raender. */
.seg {
  font-family: inherit;
  font-size: 0.85rem;
  color: var(--tool-label);
  background: transparent;
  border: 1px solid var(--tool-border);
  border-radius: 4px;
  padding: 0.22rem 0.6rem;
  cursor: pointer;
  transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}
.seg:has(+ .seg) { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.seg + .seg { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -1px; }
.seg:hover:not(.active) { color: var(--tool-emph); background: var(--tool-subtle); }
.seg.active {
  /* Weiche Akzentwaesche statt gefuellter Flaeche: ein gesaettigter Block
     Akzent ist viel Farbe fuer ein Bedienelement, das nur sagt, welche von
     zwei Ansichten an ist. Gemischt gegen transparent, nicht gegen die
     Seitenfarbe. */
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  color: var(--accent);
  border-color: var(--accent);
  z-index: 2;
}
.seg:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; z-index: 2; }

/* --- ANZEIGE ---
   Der Unterschied zur Schaltflaeche muss ohne Mauszeiger lesbar sein, sonst
   sieht eine Reihe Badges aus wie eine Reihe Knoepfe. Drei Merkmale zusammen,
   nicht eines allein, weil im grauen Skin die Farbe als Unterscheidung ausfaellt:
     - linker Farbbalken statt umlaufendem Rahmen: eine Marke am Text, kein Kasten,
     - keine Erhebung und keine Hover-Reaktion, cursor: default, Text markierbar,
     - kleinere Schrift im Monospace-Satz, waehrend Schaltflaechen die Grundschrift
       der Seite tragen.
   Der Balken traegt die Schwere (ok/warn/err), die Flaeche bleibt ruhig.

   Der Radius ist klein und ausdruecklich kein 999px: border-radius rundet die
   Ecken der Randbox, also wird ein einseitiger 3px-Rand um die volle Kapsel
   gebogen und steht als dicke Klammer vor dem Text statt als gerader Balken.
   Kapselform und einseitiger Rand schliessen sich damit gegenseitig aus. */
.badges { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.55rem; }
.badge {
  display: inline-block;
  font-size: 0.78rem;
  padding: 0.12rem 0.55rem 0.12rem 0.45rem;
  border-radius: 3px;
  border: none;
  border-left: 3px solid var(--tool-hairline);
  background: var(--tool-subtle);
  color: var(--tool-label);
  font-family: var(--mono);
  cursor: default;
  user-select: text;
}
.badge.ok   { border-left-color: var(--tool-sig-good); color: var(--tool-ok-text); }
.badge.warn { border-left-color: var(--tool-sig-warn); color: var(--tool-warn-text); }
.badge.err  { border-left-color: var(--tool-sig-bad);  color: var(--tool-danger-text); }

/* --- KLICKBARES OHNE BUTTONFORM --- */
.witness-link {
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  color: var(--accent);
  border-radius: 3px;
}
.witness-link:hover { background: color-mix(in srgb, var(--accent) 18%, transparent); }

/* Zeilen, die bei Klick den Schritt setzen. Der Zeiger allein reicht nicht,
   weil man ihn erst sieht, wenn man schon dort ist: deshalb zusaetzlich eine
   Hover-Fuellung ueber die ganze Zeile. */
tr.is-clickable { cursor: pointer; }
tr.is-clickable:hover > td { background: var(--tool-subtle); }
tr.is-clickable.active > td { background: color-mix(in srgb, var(--accent) 16%, transparent); }

/* ===================================================================== */

header.site {
  border-bottom: 1px solid var(--tool-hairline);
  background: var(--tool-surface);
  padding: 0.6rem 1.2rem;
  display: flex;
  align-items: center;
  gap: 1.4rem;
  flex-wrap: wrap;
}
header.site .brand { font-weight: 600; font-size: 1.05rem; color: var(--tool-emph); }
header.site nav { display: flex; gap: 1rem; font-size: 0.94rem; align-items: center; }
header.site nav a { text-decoration: none; }
header.site nav a.active { font-weight: 600; border-bottom: 2px solid var(--accent); }
/* Die Umschalter rutschen ans rechte Ende, getrennt vom Seitenwechsel. */
header.site .chrome {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
header.site .chrome .group { display: flex; align-items: center; }
header.site .chrome .group + .group {
  border-left: 1px solid var(--tool-hairline);
  padding-left: 0.75rem;
}

/* Theme-Umschalter: die Icons kommen als CSS-Maske herein und werden in
   currentColor gefaerbt, nicht als ☀/☾-Zeichen. Die rendern in den meisten
   Fonts als Emoji und bringen ihre eigene Farbe mit. */
.theme-seg {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.22rem 0.5rem;
}
.theme-seg::before {
  content: "";
  width: 14px;
  height: 14px;
  background-color: currentColor;
  -webkit-mask: var(--icon) no-repeat center / contain;
  mask: var(--icon) no-repeat center / contain;
}
.theme-seg[data-theme-set="light"] { --icon: url('../img/icons/theme-sun.svg'); }
.theme-seg[data-theme-set="grey"]  { --icon: url('../img/icons/theme-contrast.svg'); }
.theme-seg[data-theme-set="dark"]  { --icon: url('../img/icons/theme-moon.svg'); }

main { padding: 1.2rem; max-width: 1700px; margin: 0 auto; }
main.prose { max-width: 780px; }
main.prose h1 { font-size: 1.7rem; margin-top: 0.4rem; color: var(--tool-emph); }
main.prose h2 { font-size: 1.2rem; margin-top: 2rem; color: var(--tool-emph); }
main.prose code { font-family: var(--mono); font-size: 0.9em; background: var(--tool-subtle); padding: 0.1em 0.3em; border-radius: 3px; }
main.prose pre { background: var(--tool-subtle); padding: 0.8rem; overflow-x: auto; border-radius: 4px; }
main.prose pre code { background: none; padding: 0; }

.cols { display: grid; gap: 1rem; grid-template-columns: minmax(320px, 30fr) minmax(360px, 42fr) minmax(300px, 28fr); align-items: start; }
.cols-2 { display: grid; gap: 1rem; grid-template-columns: minmax(300px, 32fr) minmax(400px, 68fr); align-items: start; }
/* Ein Grid-Kind hat als Mindestbreite die seines Inhalts, nicht die aus
   minmax(). Ohne min-width:0 verbreitert deshalb alles, was beim Aufklappen
   erscheint (die Matrizen, ein langer Zeuge), seine Spalte und schiebt die
   beiden anderen zusammen: das Layout springt beim Klick auf den Aufklapper. */
.cols > *, .cols-2 > * { min-width: 0; }
@media (max-width: 1100px) { .cols, .cols-2 { grid-template-columns: 1fr; } }

.panel {
  background: var(--tool-surface);
  border: 1px solid var(--tool-border);
  border-radius: 5px;
  padding: 0.8rem 0.9rem;
  margin-bottom: 1rem;
  box-shadow: var(--tool-panel-shadow);
}
.panel > h2 { font-size: 0.95rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--tool-label); margin: 0 0 0.6rem; font-weight: 600; }
.panel > h3 { font-size: 0.9rem; margin: 0.9rem 0 0.4rem; color: var(--tool-label); }

textarea, input, select {
  font-family: inherit;
  font-size: 0.95rem;
  color: var(--tool-text);
}
textarea {
  width: 100%;
  min-height: 260px;
  font-family: var(--mono);
  font-size: 0.86rem;
  line-height: 1.45;
  border: 1px solid var(--tool-border);
  border-radius: 4px;
  padding: 0.5rem;
  background: var(--tool-panel);
  resize: vertical;
  tab-size: 2;
}
textarea.small { min-height: 150px; }
input[type="text"] {
  width: 100%;
  border: 1px solid var(--tool-border);
  border-radius: 4px;
  padding: 0.35rem 0.5rem;
  font-family: var(--mono);
  font-size: 0.9rem;
  background: var(--tool-panel);
}
/* Der Aufklapppfeil zeichnet der Browser in die rechte Polsterung hinein, ohne
   selbst Platz dafuer zu reservieren. Bei langen Beispieltiteln laeuft der Text
   sonst unter den Pfeil, deshalb rechts eine eigene Spur. */
select {
  border: 1px solid var(--tool-border);
  border-radius: 4px;
  padding: 0.3rem 1.6rem 0.3rem 0.4rem;
  background: var(--tool-panel);
  max-width: 100%;
  cursor: pointer;
  text-overflow: ellipsis;
}
textarea:focus-visible, input:focus-visible, select:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

.btn-row { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.5rem; }
.seg-row { display: flex; flex-wrap: wrap; align-items: center; margin-top: 0.5rem; }
/* Ein Segmentpaar ausserhalb des Headers: .group haelt die Segmente zusammen,
   damit .seg + .seg die geteilte Kante bekommt, auch wenn ein Label davorsteht. */
.seg-row .group { display: inline-flex; }
.seg-label { font-size: 0.88rem; color: var(--tool-label); margin-right: 0.5rem; }
/* Die Auswahl haengt sonst am Rahmen der Zeichenflaeche darunter. */
.seg-row + .graph-wrap { margin-top: 0.6rem; }
.field-row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; margin-bottom: 0.5rem; }
.field-row label { font-size: 0.88rem; color: var(--tool-label); }

/* Der Rahmen scrollt, statt abzuschneiden: draw.js klemmt den Zoom, damit die
   Zustandskreise in beide Richtungen lesbar bleiben, und was bei kleinstem Zoom
   immer noch breiter ist als die Spalte, faehrt hier heraus. max-height deckelt
   hohe Graphen, damit das Panel nicht die Seite auseinanderzieht. */
.graph-wrap {
  border: 1px solid var(--tool-border);
  border-radius: 4px;
  background: var(--tool-surface);
  overflow: auto;
  max-height: 70vh;
}
/* Ohne Breitenattribut faellt das SVG auf die Spaltenbreite zurueck (Node-Test,
   verstecktes Panel); mit Attribut gewinnt die von draw.js gesetzte Pixelgroesse. */
svg.graph { display: block; max-width: none; min-height: 220px; font-family: "Charter", "Georgia", serif; margin: 0 auto; }
svg.graph:not([width]) { width: 100%; height: auto; }

svg.graph .state-body { fill: var(--tool-surface); stroke: var(--edge); stroke-width: 1.6; }
svg.graph .state-inner { fill: none; stroke: var(--edge); stroke-width: 1.1; }
svg.graph .state-accepting .state-body { fill: var(--accent-soft); }
svg.graph .state-complement .state-body { fill: var(--blue-soft); }
svg.graph .state-weak .state-body { fill: var(--tool-surface); stroke-dasharray: 4 3; }
svg.graph .state.hot .state-body { stroke: var(--hot); stroke-width: 2.6; }
svg.graph .state-label { font-size: 15px; fill: var(--tool-emph); }
svg.graph .state-set { font-size: 11px; fill: var(--hot); font-family: var(--mono); }
svg.graph .edge { fill: none; stroke: var(--edge); stroke-width: 1.5; }
svg.graph .init-arrow { stroke: var(--edge); stroke-width: 1.5; fill: none; }
svg.graph .edge-label text { font-size: 12.5px; fill: var(--tool-text); }
svg.graph .label-bg { fill: var(--tool-surface); fill-opacity: 0.88; }
svg.graph .token-round circle { fill: var(--accent); }
svg.graph .token-square rect { fill: none; stroke: var(--tool-emph); stroke-width: 1.8; }
svg.graph .token-letter { font-size: 9px; fill: var(--tool-ink); font-family: var(--mono); }
svg.graph .token { transition: transform 400ms ease; }

/* === Positionsgraph ===
 * Eine nicht akzeptierende Position darf sich nicht allein ueber die Fuellung
 * zeigen: im grauen Skin ist --tool-danger-bg durchsichtig. Zweites Merkmal ist
 * deshalb der doppelte Umriss, wie beim akzeptierenden Zustand im Automaten. */
svg.graph .pg-box { fill: var(--tool-surface); stroke: var(--edge); stroke-width: 1.4; }
svg.graph .pg-node.rej .pg-box { fill: var(--tool-danger-bg); stroke: var(--tool-danger-border); }
svg.graph .pg-box-inner { fill: none; stroke: var(--tool-danger-border); stroke-width: 1; }
/* Die Hervorhebung der Selbstueberdeckung darf nicht --hot sein: das ist
   dieselbe Farbfamilie wie --tool-danger-border, im grauen Skin sogar derselbe
   Wert. Nicht akzeptierend und auf der Pumpschleife sind aber unabhaengige
   Aussagen, und ein Knoten kann beides sein. Also Akzent aussen, Gefahr innen,
   dann bleiben sie auch zusammen lesbar. */
svg.graph .pg-node.hl .pg-box { stroke: var(--accent); stroke-width: 2.4; }
svg.graph .pg-label { font-size: 12px; fill: var(--tool-emph); font-family: var(--mono); }
/* Die Tokenzeichen tragen eine Zahl, keine Betonung. In --tool-emph stehen sie
   in Hell als volles Schwarz und in Dunkel als volles Weiss im Bild und
   erschlagen die Kaesten und die Tabelle; im grauen Skin faellt das nicht auf,
   weil dort die Seitenfarbe in der Mitte liegt. Ein Mittelton stellt denselben
   Eindruck in allen drei Skins her, ohne die Zeichen zaehlbar zu machen. */
svg.graph .pg-tok { fill: var(--token); }
.tok { color: var(--token); }
/* Eine eigene Rolle statt einer Stufe der Leiter, weil dieselbe Stufe hier
   nicht denselben Eindruck macht: dunkel auf hell wirkt schwerer als hell auf
   dunkel, --tool-label steht in Hell noch als Schwarz im Bild. Hell geht
   deshalb eine Stufe weiter herunter, Dunkel und Grau bleiben. */
:root { --token: var(--tool-label); }
html[data-theme="light"] { --token: var(--tool-note); }
svg.graph .pg-node.is-clickable { cursor: pointer; }
svg.graph .pg-node.is-clickable:hover .pg-box { stroke: var(--accent); stroke-width: 2.2; }
svg.graph .pg-edge { fill: none; stroke: var(--edge); stroke-width: 1.2; }
svg.graph .pg-edge.hot { stroke: var(--accent); stroke-width: 2; }
svg.graph .pg-edge-label { font-size: 11px; fill: var(--tool-note); font-family: var(--mono); }

/* Der Bell-Vergleich steht neben, nicht ueber der Positionstabelle: er hat nur
   so viele Zeilen wie die Erkundung tief ist. */
table.bell { width: auto; margin: 0.4rem 0 0.6rem; }
table.bell td, table.bell th { padding: 0.12rem 0.7rem 0.12rem 0; text-align: right; }
table.bell td:first-child, table.bell th:first-child { text-align: left; }

/* === Zahlenstrahl ===
 * Die beiden Baender duerfen sich nicht allein ueber die Farbe unterscheiden,
 * sonst sind sie im grauen Skin dasselbe Rechteck: das akzeptierende hat einen
 * durchgezogenen Umriss, das komplement-akzeptierende einen gestrichelten, und
 * beide tragen links ihren Namen. Die Punkte an den Enden sind das zweite
 * Merkmal, an dem [1,2] und [1,2) auseinandergehen: gefuellt heisst
 * geschlossen, hohl heisst offen. */
svg.nl { display: block; width: 100%; height: auto; font-family: "Charter", "Georgia", serif; }
svg.nl .nl-axis, svg.nl .nl-tick { stroke: var(--edge); stroke-width: 1.2; }
svg.nl .nl-tick-label { font-size: 10.5px; fill: var(--tool-note); font-family: var(--mono); }
svg.nl .nl-stem { stroke: var(--edge); stroke-width: 1; stroke-dasharray: 2 2; }
/* Der Buchstabe ist der Gegenstand des Bildes und bekommt deshalb die
   staerkste Farbe der Leiter, nicht den Akzent: der ist in zwei der drei
   Skins nur eine Nuance vom Untergrund entfernt und liess die Punkte
   verschwinden. Der helle Rand hebt sie vom Stiel ab. */
svg.nl .nl-letter { fill: var(--tool-emph); stroke: var(--tool-surface); stroke-width: 1.2; }
svg.nl .nl-letter-label { font-size: 12px; fill: var(--tool-text); font-family: var(--mono); }
svg.nl .nl-band-bed { fill: var(--tool-subtle); stroke: var(--tool-hairline); stroke-width: 1; }
svg.nl .nl-band-label { font-size: 10px; fill: var(--tool-label); }
svg.nl .nl-band-empty { font-size: 11px; fill: var(--tool-note); font-family: var(--mono); }
svg.nl .nl-band-acc .nl-band-fill { fill: var(--accent-soft); stroke: var(--accent); stroke-width: 1.2; }
svg.nl .nl-band-comp .nl-band-fill { fill: var(--blue-soft); stroke: var(--blue); stroke-width: 1.2; stroke-dasharray: 4 3; }
svg.nl .nl-end { stroke: var(--tool-emph); stroke-width: 1.2; }
svg.nl .nl-end.closed { fill: var(--tool-emph); }
svg.nl .nl-end.open { fill: var(--tool-surface); }
svg.nl .nl-mu-line { stroke: var(--hot); stroke-width: 1.6; }
svg.nl .nl-mu-head { fill: var(--hot); }
svg.nl .nl-mu-label { font-size: 11px; fill: var(--hot); font-family: var(--mono); }
svg.nl .nl-hit { fill: transparent; cursor: ew-resize; }
/* Gezogen wird hier, also darf das Ziehen keinen Text markieren: eine
   angefangene Auswahl legt sonst den Markierungsbalken des Systems hinter
   die Beschriftungen, in jedem Skin dieselbe helle Flaeche. Der Graph
   nebenan haengt am selben Zug und bekommt es deshalb auch. */
svg.nl, svg.graph { -webkit-user-select: none; user-select: none; }
#nlVerdict { margin-top: 0.5rem; }

/* === Sprachkarte ===
 * Eine Kachel je Wort. Die Klasse steht als Zeichen in der Kachel und nicht nur
 * in ihrer Farbe, damit die vier Faelle auch im grauen Skin auseinandergehen.
 * Die Kachel ist klickbar, traegt aber keinen Buttonrahmen: sie ist zu klein
 * fuer die Affordanzregel und bekommt den Hover-Rand in Akzentfarbe. */
#mapOut { margin-top: 0.6rem; }
.lm-summary { font-size: 0.88rem; margin: 0.2rem 0 0.5rem; }
.lm-summary .tag { font-family: var(--mono); font-size: 0.78rem; color: var(--tool-note); }
.lm-body { max-height: 58vh; overflow: auto; }
.lm-row { margin-bottom: 0.5rem; }
.lm-head { font-size: 0.8rem; color: var(--tool-label); margin-bottom: 0.2rem; }
.lm-grid { display: flex; flex-wrap: wrap; gap: 2px; }
.lm-cell {
  width: 19px; height: 19px;
  border: 1px solid var(--tool-hairline);
  border-radius: 2px;
  background: var(--tool-subtle);
  color: var(--tool-text);
  font-family: var(--mono);
  font-size: 9.5px;
  line-height: 17px;
  text-align: center;
  letter-spacing: -0.5px;
  cursor: pointer;
  user-select: none;
}
.lm-cell:hover { border-color: var(--accent); color: var(--tool-emph); }
.lm-cell.both { background: var(--accent-soft); border-color: var(--accent); color: var(--tool-emph); }
.lm-cell.a    { background: var(--tool-surface); border-color: var(--accent); color: var(--tool-emph); }
.lm-cell.b    { background: var(--tool-surface); border-color: var(--blue); color: var(--tool-emph); }
.lm-cell.none { background: var(--tool-subtle); border-color: var(--tool-hairline); }
.lm-cell.acc  { background: var(--accent-soft); border-color: var(--accent); color: var(--tool-emph); }
.lm-cell.rej  { background: var(--tool-subtle); border-color: var(--tool-hairline); }
.lm-legend { display: flex; flex-wrap: wrap; gap: 0.8rem; font-size: 0.82rem; color: var(--tool-note); margin: 0.5rem 0; }
.lm-legend span { display: flex; align-items: center; gap: 0.3rem; }
.lm-legend .lm-cell { cursor: default; }

table { border-collapse: collapse; width: 100%; font-size: 0.86rem; }
th, td { text-align: left; padding: 0.22rem 0.45rem; border-bottom: 1px solid var(--tool-hairline); vertical-align: top; }
th { color: var(--tool-label); font-weight: 600; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; }
td.mono, th.mono, .mono { font-family: var(--mono); font-size: 0.85rem; }

/* Oberer Abstand, weil das Verdikt im Wort-Panel unmittelbar auf die
   Schrittleiste folgt und ohne ihn an den Schaltflaechen klebt. */
.verdict {
  padding: 0.5rem 0.7rem;
  border-radius: 4px;
  border: 1px solid var(--tool-hairline);
  background: var(--tool-subtle);
  margin: 0.7rem 0 0.6rem;
  font-size: 0.95rem;
  cursor: default;
}
.verdict.acc      { background: var(--tool-ok-bg);     border-color: var(--tool-ok-border);     color: var(--tool-ok-text); }
.verdict.comp     { background: var(--tool-info-bg);   border-color: var(--tool-info-border);   color: var(--tool-info-text); }
.verdict.none     { background: var(--tool-subtle);    border-color: var(--tool-hairline); }
.verdict.conflict { background: var(--tool-danger-bg); border-color: var(--tool-danger-border); color: var(--tool-danger-text); font-weight: 600; }

/* --- INFO-OVERLAY ---
   Ein kleines i oben rechts im Panel, das knapp erklaert, wofuer der Block da
   ist. Es ist eine Aktion, also ein Knopf, aber ein runder: der Kreis ist keine
   vierte Kastenform, sondern die kleinste Ausfuehrung derselben Rolle, und er
   sitzt ausserhalb des Textflusses, damit er nichts verschiebt.
   Die Box ist bewusst nicht modal: ein Klick daneben oder Escape schliesst sie. */
/* Spezifitaet absichtlich ueber die von button:not(.seg): jene Regel setzt
   display, gap und Innenabstand fuer alle Schaltflaechen, und der runde
   i-Knopf braucht seine eigenen Werte, sonst rutscht das i aus dem Kreis. */
button.info-btn {
  position: absolute;
  top: 0.5rem;
  right: 0.55rem;
  width: 1.2rem;
  height: 1.2rem;
  padding: 0;
  border-radius: 50%;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1;
  color: var(--tool-note);
  background: transparent;
  border: 1px solid var(--tool-hairline);
  display: flex;
  align-items: center;
  justify-content: center;
}
button.info-btn:hover:not(:disabled) {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
button.info-btn[aria-expanded="true"] {
  color: var(--tool-ink);
  background: var(--accent-fill);
  border-color: var(--accent-fill);
}
.info-box {
  position: absolute;
  top: 2rem;
  right: 0.55rem;
  z-index: 5;
  width: min(24rem, calc(100% - 1.1rem));
  padding: 0.6rem 0.75rem;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--tool-text);
  background: var(--tool-panel);
  border: 1px solid var(--accent);
  border-radius: 4px;
  box-shadow: var(--tool-panel-shadow);
}
/* Im grauen Skin ist --tool-panel gleich der Seitenfarbe, das Overlay haette
   also keinen Grund, sich vom Panel darunter abzuheben. Der Rahmen im Akzent
   traegt es, und eine Spur Aufhellung gibt ihm Koerper. */
html[data-skin="grey"] .info-box { background: var(--tool-subtle); }
.info-box p { margin: 0 0 0.4rem; }
.info-box p:last-child { margin-bottom: 0; }
.info-box code { font-family: var(--mono); font-size: 0.9em; }

.hint { font-size: 0.85rem; color: var(--tool-note); margin: 0.3rem 0 0; }

/* Kopfzeile eines Papers auf der Startseite: Autoren, Konferenz, Jahr.
   Steht direkt unter der Ueberschrift und ist bewusst leiser als der Fliesstext,
   damit sie als Angabe und nicht als erster Satz gelesen wird. */
.paper-meta { font-size: 0.88rem; color: var(--tool-note); margin: -0.4rem 0 0.6rem; }
.error { color: var(--tool-sig-bad); font-family: var(--mono); font-size: 0.85rem; white-space: pre-wrap; }
.result-line { font-size: 0.88rem; margin: 0.25rem 0; }
.result-line .tag { font-family: var(--mono); font-size: 0.78rem; color: var(--tool-note); }

/* Schieberegler nehmen ohne accent-color das Blau des Betriebssystems und
   stehen dann in jedem Skin daneben. Die Regel gilt global, nicht nur im
   .stepper: der Regler fuer den Familienparameter steht in einer .field-row. */
input[type="range"] { accent-color: var(--accent); }

.stepper { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.stepper input[type="range"] { flex: 1; min-width: 120px; }

.legend { display: flex; flex-wrap: wrap; gap: 0.8rem; font-size: 0.82rem; color: var(--tool-note); margin-top: 0.5rem; }
.legend span { display: flex; align-items: center; gap: 0.3rem; }
.legend i { width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid var(--edge); display: inline-block; }
.legend i.acc { background: var(--accent-soft); box-shadow: inset 0 0 0 2px var(--tool-surface), inset 0 0 0 3px var(--edge); }
.legend i.comp { background: var(--blue-soft); }
.legend i.weak { background: var(--tool-surface); border-style: dashed; }
/* Die Kaesten des Positionsgraphen sind keine Kreise, ihre Marken auch nicht.
   Der doppelte Rahmen wird als Ring nachgebaut, nicht als zweiter Rand: ein
   border-radius bezieht sich auf die Randbox, ein zweiter Rand liesse sich
   dort gar nicht unterbringen. */
.legend i.box { border-radius: 3px; }
.legend i.rej { background: var(--tool-danger-bg); border-color: var(--tool-danger-border);
  box-shadow: inset 0 0 0 1px var(--tool-surface), inset 0 0 0 2px var(--tool-danger-border); }
.legend i.pump { background: none; border: 2px solid var(--accent); }

details { margin-top: 0.5rem; }
summary { cursor: pointer; font-size: 0.88rem; color: var(--tool-label); }
summary:hover { color: var(--tool-emph); }
details[open] summary { margin-bottom: 0.4rem; }

/* Abstract auf der Startseite: der zugeklappte Text soll sich vom Fliesstext
   absetzen, ohne wie ein Kasten mit Funktion auszusehen. Deshalb linker Balken
   plus eine Spur Flaeche, nicht Rahmen: im grey-Skin traegt die Flaeche nichts,
   dort bleibt der Balken das Merkmal. */
.abstract { border-left: 3px solid var(--tool-border); background: var(--tool-subtle); padding: 0.5rem 0.8rem; margin: 0 0 0.8rem; }
.abstract p { margin: 0; color: var(--tool-note); }

/* Die Matrizen sitzen im aufklappbaren Teil der schmalsten Spalte. Ohne
   width:auto zieht die Tabelle die Regel table{width:100%} von oben mit und
   spreizt ihre Spalten ueber die ganze Panelbreite, was das Grid beim Aufklappen
   sichtbar umbrechen laesst. Mit auto ist sie so breit wie ihr Inhalt, und
   #matrices faengt den Rest ab, falls doch etwas Breiteres kommt. */
.matrix { font-family: var(--mono); font-size: 0.8rem; border-collapse: collapse; width: auto; table-layout: auto; }
.matrix td { border: none; padding: 0.02rem 0.22rem; text-align: right; white-space: nowrap; }
.matrix .zero { color: var(--tool-faint); }
#matrices { overflow-x: auto; }

.vec { font-family: var(--mono); font-size: 0.9rem; }
.vec .upper { color: var(--accent); }
.vec .lower { color: var(--blue); }

.word-display { font-family: var(--mono); font-size: 1.05rem; letter-spacing: 0.06em; padding: 0.3rem 0; word-break: break-all; }
.word-display .cursor { color: var(--tool-note); }
/* Das Label gehoert zum Fliesstext, nicht zum Wort. inherit hilft hier nicht:
   .word-display setzt selbst Monospace, das Label muss die Body-Schrift also
   ausdruecklich zurueckholen, ebenso die Sperrung, die nur die Buchstaben
   brauchen. */
.word-display .seg-label { font-family: "Charter", "Georgia", "Times New Roman", serif; letter-spacing: normal; }

.pos-table { max-height: 320px; overflow-y: auto; }

footer.site { border-top: 1px solid var(--tool-hairline); margin-top: 2rem; padding: 1rem 1.2rem; font-size: 0.85rem; color: var(--tool-note); }
footer.site p { margin: 0 0 0.35rem; }
footer.site p:last-child { margin-bottom: 0; }
/* Quellenangaben: Liste ohne Punkte, damit die Fusszeile ruhig bleibt. */
footer.site .footer-credit { font-size: 0.95em; }
footer.site .src { color: var(--tool-text); }

/* Querlink auf das andere Projekt: eine leise Zeile am rechten Rand, damit sie
   nicht wie eine weitere Quellenangabe gelesen wird. */
footer.site .footer-cross { margin: 0.7rem 0 0; text-align: right; }
footer.site .footer-cross a { color: inherit; text-decoration: none; }
footer.site .footer-cross a:hover,
footer.site .footer-cross a:focus { color: var(--tool-text); text-decoration: underline; }

/* Header und Footer laufen ueber die volle Breite, weil Hintergrund und
   Trennlinien das sollen; ihr Inhalt sitzt aber in derselben zentrierten
   Spalte wie main. Statt eines zusaetzlichen Wrappers waechst dafuer das
   Innenmass: 1.2rem Grundabstand plus die Haelfte dessen, was das Fenster
   breiter ist als die Spalte. Der Wert passt zu main, weil dessen max-width
   dank border-box die eigenen 1.2rem schon enthaelt. Die Regel steht hinter
   footer.site, weil sie dieselbe Spezifitaet hat und sonst verloere. */
header.site,
footer.site {
  padding-inline: calc(1.2rem + max(0px, (100% - var(--site-col)) / 2));
}

/* Teilen-Panel: lange URLs duerfen das Layout nicht sprengen. */
#linkOut { display: flex; flex-direction: column; gap: 0.3rem; margin-top: 0.5rem; }
#linkOut input[type="text"] { font-size: 0.78rem; }
#linkOut .hint { margin: 0; }
.hint, .result-line, .error { overflow-wrap: anywhere; }
#tikzOut { margin-top: 0.5rem; font-size: 0.78rem; }

/* ===================================================================== */
/* UMSCHALTANIMATIONEN                                                    */
/* ===================================================================== */

/* Themewechsel. Browser mit View Transitions blenden zwei Seitentexturen auf
   der GPU um, was unabhaengig von der DOM-Groesse fluessig bleibt. Die alte
   Textur haelt still, die neue wischt aus der oberen rechten Ecke herein, wo
   der Umschalter sitzt: eine ueberdimensionierte Verlaufsmaske faehrt darueber,
   ihre Stops nähern eine Smoothstep an, damit die Kante eine weiche Rampe ist
   und keine harte Schnittkante. */
::view-transition-old(root) { animation: none; }
::view-transition-new(root) {
  mask-image: linear-gradient(to bottom left,
    rgb(0 0 0) 38%, rgb(0 0 0 / 0.92) 43%, rgb(0 0 0 / 0.5) 50%,
    rgb(0 0 0 / 0.08) 57%, transparent 62%);
  mask-size: 300% 300%;
  mask-repeat: no-repeat;
  animation: theme-sweep 1.2s ease-in-out both;
}
@keyframes theme-sweep {
  from { mask-position: 0% 100%; }
  to   { mask-position: 100% 0%; }
}

/* Fallback ohne die API: .theme-morph animiert eine halbe Sekunde lang jedes
   Element, damit die Farben wandern statt zu springen. Bewusst unskopiert: die
   Regel ist 600 ms lang an und nie beim normalen Blaettern. */
html.theme-morph, html.theme-morph * {
  transition: background-color 0.5s ease, color 0.5s ease, border-color 0.5s ease,
    fill 0.5s ease, stroke 0.5s ease, box-shadow 0.5s ease;
}

/* Sprachwechsel als Post-Effekt auf der ganzen Seite. site-chrome.js setzt
   .lang-vt auf <html>, solange die Transition laeuft, damit die Regeln des
   Theme-Sweeps diese Texturen nicht anfassen. Die alte Seite faellt in ihre
   Kanaele auseinander, waehrend die neue darueber herunterwischt.
   @keyframes lang-vt-wave steht nicht hier, sondern wird in site-chrome.js
   zusammen mit den Filtern erzeugt, durch die es schaltet. */
html.lang-vt::view-transition-old(root) {
  animation: lang-vt-wave 1.1s linear both;
}
/* Gleiche Konstruktion wie der Theme-Sweep, aber gerade nach unten. Die Kachel
   ist drei Viewports hoch und der 50%-Stop sitzt bei 37,3% davon, was die Kante
   bei Position 0 genau 1,12 Schirme tief setzt; der Lauf startet bei 62%, also
   0,12 Schirme ueber der Oberkante. TRAVEL in site-chrome.js ist dasselbe
   -0.12 bis 1.12 und muss dazu passen, sonst laufen Band und Kante auseinander.
   Lineares Timing, weil eine Welle, die in der Mitte beschleunigt, nicht als
   Welle gelesen wird, sondern als Seite, die auf einmal umspringt. */
html.lang-vt::view-transition-new(root) {
  mask-image: linear-gradient(to bottom,
    rgb(0 0 0) 34%, rgb(0 0 0 / 0.5) 37.3%, transparent 40.7%);
  mask-size: 100% 300%;
  mask-repeat: no-repeat;
  animation: lang-vt-wipe 1.1s linear both,
             lang-vt-wave 1.1s linear both;
}
@keyframes lang-vt-wipe {
  from { mask-position: 50% 62%; }
  to   { mask-position: 50% 0%; }
}

@media (prefers-reduced-motion: reduce) {
  html.theme-morph, html.theme-morph * { transition: none; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
  ::view-transition-new(root) { mask-image: none; }
  svg.graph .token { transition: none; }
}
