@charset "utf-8";
/* ===========================================================================
   NETZWARE // DIGITAL INFRASTRUCTURE
   Designsystem des oeffentlichen Bereichs.

   Aufbau
     01  Schriften
     02  Tokens
     03  Grundlagen und Typografie
     04  Raster und Abschnitte
     05  Buttons und Links
     06  Karten und Flaechen
     07  Kopfbereich und Navigation
     08  Bloecke
     09  Formulare
     10  Fusszeile
     11  Bewegung
     12  Ruecksichtnahme (reduzierte Bewegung, Kontrast, Druck)

   Grundhaltung: Der dunkle Grund traegt, Cyan ist Signal -- nie Flaeche.
   Bewegung dient dem Verstaendnis, nicht der Schau: Sie zeigt, woher etwas
   kommt und wohin es geht, und laesst sich jederzeit unterbrechen.
   =========================================================================== */

/* ---------------------------------------------------------------- 01 Schriften
   Selbst gehostet. Eine Einbindung ueber Google Fonts wuerde die IP-Adresse
   jedes Besuchers an einen Drittanbieter uebertragen -- in Oesterreich ohne
   Einwilligung nicht zulaessig -- und waere ausserdem durch die CSP blockiert. */

@font-face {
  font-family: 'InterVar';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('../fonts/inter-latin-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'InterVar';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('../fonts/inter-latin-ext-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrainsMonoVar';
  font-style: normal;
  font-display: swap;
  font-weight: 100 800;
  src: url('../fonts/jetbrains-mono-latin-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+20AC, U+2122;
}

/* ------------------------------------------------------------------ 02 Tokens */

:root {
  /* Farbe. Die Marke ist dunkel -- das dunkle Thema ist deshalb die Vorgabe
     und steht hier ohne Bedingung. Das helle Thema weiter unten aendert nur
     diese Variablen; jede Regel im Rest der Datei bleibt unberuehrt.

     Die -rgb-Token tragen nur die Kanaele, damit dieselbe Regel im hellen
     Thema aufhellen statt abdunkeln kann: rgba(var(--tint-rgb),.05) ist
     dunkel ein heller Schleier und hell ein dunkler. */
  --tint-rgb:      255,255,255;
  --shade-rgb:     0,0,0;
  --accent-rgb:    0,217,255;
  --accent2-rgb:   71,119,255;
  --success-rgb:   56,232,165;
  --warn-rgb:      255,194,75;
  --danger-rgb:    255,107,107;
  --bg-rgb:        7,10,15;
  --bg2-rgb:       11,17,24;
  --surface-rgb:   17,25,35;
  --mega-rgb:      13,19,28;
  --muted-rgb:     152,167,179;

  --bg:            #070A0F;
  --bg-2:          #0B1118;
  --bg-deep:       #04070B;
  --surface:       #111923;
  --surface-2:     #16212C;
  --surface-3:     #1C2836;

  --accent:        #00D9FF;
  --accent-ink:    #04222B;   /* Text auf Cyan: 12.9:1 */
  --accent-dim:    #0FA6C4;
  --accent-hover:  #6FEBFF;
  --accent-2:      #4777FF;
  --success:       #38E8A5;
  --warn:          #FFC24B;
  --warn-ink:      #20160A;
  --danger:        #FF6B6B;

  /* --white ist die staerkste Textfarbe, nicht die Farbe Weiss. Im hellen
     Thema ist sie folgerichtig fast schwarz. */
  --white:         #F4F8FA;
  --text:          #D9E3E8;
  --muted:         #98A7B3;   /* 6.4:1 auf --bg */
  --muted-strong:  #B4C1CB;
  --muted-contrast:        #C2CFD8;
  --muted-strong-contrast: #E2EAEF;
  --surface-contrast:      #0E151E;

  --border:        rgba(var(--tint-rgb),.08);
  --border-strong: rgba(var(--tint-rgb),.16);
  --border-accent: rgba(var(--accent-rgb),.42);

  /* Flaechen, die eine eigene Mischung brauchen */
  --header-opaque: #070A0F;
  --mega-opaque:   #0B1118;
  --rack-1:        #0C131B;
  --rack-2:        #070B10;
  --shadow-header: 0 12px 40px -30px rgba(var(--shade-rgb),.9);

  --glow-accent:   0 0 0 1px rgba(var(--accent-rgb),.30), 0 18px 60px -24px rgba(var(--accent-rgb),.42);
  --shadow-card:   0 1px 2px rgba(var(--shade-rgb),.4), 0 18px 48px -28px rgba(var(--shade-rgb),.9);
  --shadow-float:  0 24px 70px -30px rgba(var(--shade-rgb),.95), 0 2px 6px rgba(var(--shade-rgb),.4);

  /* Schrift */
  --font: 'InterVar', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --mono: 'JetBrainsMonoVar', ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

  /* Abstaende. In rem, damit eine groessere Schriftgroesse das Layout
     mitwachsen laesst statt es zu sprengen. */
  --s-1:  .25rem;  --s-2:  .5rem;   --s-3:  .75rem;  --s-4:  1rem;
  --s-5:  1.25rem; --s-6:  1.5rem;  --s-7:  2rem;    --s-8:  2.5rem;
  --s-9:  3.5rem;  --s-10: 4.5rem;  --s-11: 6rem;    --s-12: 8rem;

  --r-sm: 10px;  --r-md: 14px;  --r-lg: 20px;  --r-xl: 28px;  --r-pill: 999px;

  --container:        1240px;
  --container-narrow: 760px;
  --container-wide:   1440px;
  --gutter: clamp(1.25rem, 4vw, 2.5rem);

  /* Bewegung.
     --spring ist die Weberfassung einer kritisch gedaempften Feder
     (Daempfung 1.0, Antwortzeit ~0.4 s): schnell heran, kein Nachschwingen.
     --spring-soft darf minimal ueberschwingen und ist nur fuer Bewegungen
     gedacht, die aus einem Schwung heraus entstehen. */
  --spring:       cubic-bezier(.32,.72,0,1);
  --spring-soft:  cubic-bezier(.34,1.12,.42,1);
  --ease-out:     cubic-bezier(.22,.61,.36,1);
  --dur-press:    110ms;
  --dur-fast:     180ms;
  --dur:          340ms;
  --dur-slow:     620ms;

  --header-h: 76px;
  /* Hoehe der Leiste, sobald gescrollt wird. Steht als Token, weil die
     Differenz zu --header-h an zwei Stellen gebraucht wird. */
  --header-h-klein: 64px;
  color-scheme: dark;
}

/* ------------------------------------------------------- 02b Helles Thema

   Zwei Wege fuehren hierher, und beide setzen dieselben Werte:

   1. Der Browser meldet eine helle Systemeinstellung -- dann greift das
      helle Thema von selbst, ohne JavaScript und ohne Flackern.
   2. Jemand hat den Umschalter in der Kopfleiste benutzt -- dann steht
      data-theme am <html> und schlaegt die Systemeinstellung.

   Der Block steht deshalb doppelt. Das ist Absicht: CSS kann eine
   Media-Bedingung und einen Attributselektor nicht in einer Regel
   zusammenfassen, und ein Umweg ueber ein zweites Variablenpaar waere
   schwerer zu lesen als diese Wiederholung. Beide Bloecke sind zeichengleich
   -- wer einen aendert, aendert den anderen mit.
   ------------------------------------------------------------------------ */

@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) {
    /* Helles Thema.
       Nur Variablen -- kein Selektor weiter unten weiss davon.
       --tint-rgb kippt von Weiss auf Tinte: derselbe Schleier, der dunkel
       aufhellt, dunkelt hier ab. Deshalb bleiben Karten, Leisten und
       Trennlinien ohne eigene helle Regel richtig.
       --accent-rgb ist bewusst heller als --accent: die Kanaele tragen nur
       Flaechen und Schimmer, --accent traegt Text und muss den Kontrast
       halten (5.3:1 auf dem Grund, 5.7:1 auf Weiss). */
    color-scheme: light;

    --tint-rgb:      10,17,23;
    --shade-rgb:     15,25,38;
    --accent-rgb:    5,140,167;
    --accent2-rgb:   39,67,196;
    --success-rgb:   10,110,72;
    --warn-rgb:      138,90,0;
    --danger-rgb:    179,38,30;
    --bg-rgb:        245,247,250;
    --bg2-rgb:       234,239,244;
    --surface-rgb:   255,255,255;
    --mega-rgb:      255,255,255;
    --muted-rgb:     90,104,117;

    --bg:            #F5F7FA;
    --bg-2:          #EAEFF4;
    --bg-deep:       #E6ECF2;
    --surface:       #FFFFFF;
    --surface-2:     #F7F9FC;
    --surface-3:     #EEF2F7;

    --accent:        #0A7186;
    --accent-ink:    #FFFFFF;
    --accent-dim:    #075A6B;
    --accent-hover:  #064E5D;
    --accent-2:      #2743C4;
    --success:       #0A6E48;
    --warn:          #8A5A00;
    --warn-ink:      #FFFFFF;
    --danger:        #B3261E;

    --white:         #0A1117;
    --text:          #17212B;
    --muted:         #5A6875;
    --muted-strong:  #3B4854;
    --muted-contrast:        #414E5B;
    --muted-strong-contrast: #26313C;
    --surface-contrast:      #FFFFFF;

    --border:        rgba(var(--tint-rgb),.13);
    --border-strong: rgba(var(--tint-rgb),.22);
    --border-accent: rgba(var(--accent-rgb),.50);

    --header-opaque: #F5F7FA;
    --mega-opaque:   #FFFFFF;
    --rack-1:        #EDF1F6;
    --rack-2:        #DEE5EC;

    /* Schatten tragen im Hellen viel weniger Alpha -- sonst wirkt jede Karte
       wie ausgeschnitten. */
    --shadow-header: 0 10px 30px -26px rgba(var(--shade-rgb),.55);
    --glow-accent:   0 0 0 1px rgba(var(--accent-rgb),.35), 0 18px 50px -26px rgba(var(--accent-rgb),.40);
    --shadow-card:   0 1px 2px rgba(var(--shade-rgb),.06), 0 14px 34px -24px rgba(var(--shade-rgb),.30);
    --shadow-float:  0 20px 54px -28px rgba(var(--shade-rgb),.34), 0 2px 6px rgba(var(--shade-rgb),.06);
  }
}

:root[data-theme='light'] {
  /* Helles Thema.
     Nur Variablen -- kein Selektor weiter unten weiss davon.
     --tint-rgb kippt von Weiss auf Tinte: derselbe Schleier, der dunkel
     aufhellt, dunkelt hier ab. Deshalb bleiben Karten, Leisten und
     Trennlinien ohne eigene helle Regel richtig.
     --accent-rgb ist bewusst heller als --accent: die Kanaele tragen nur
     Flaechen und Schimmer, --accent traegt Text und muss den Kontrast
     halten (5.3:1 auf dem Grund, 5.7:1 auf Weiss). */
  color-scheme: light;

  --tint-rgb:      10,17,23;
  --shade-rgb:     15,25,38;
  --accent-rgb:    5,140,167;
  --accent2-rgb:   39,67,196;
  --success-rgb:   10,110,72;
  --warn-rgb:      138,90,0;
  --danger-rgb:    179,38,30;
  --bg-rgb:        245,247,250;
  --bg2-rgb:       234,239,244;
  --surface-rgb:   255,255,255;
  --mega-rgb:      255,255,255;
  --muted-rgb:     90,104,117;

  --bg:            #F5F7FA;
  --bg-2:          #EAEFF4;
  --bg-deep:       #E6ECF2;
  --surface:       #FFFFFF;
  --surface-2:     #F7F9FC;
  --surface-3:     #EEF2F7;

  --accent:        #0A7186;
  --accent-ink:    #FFFFFF;
  --accent-dim:    #075A6B;
  --accent-hover:  #064E5D;
  --accent-2:      #2743C4;
  --success:       #0A6E48;
  --warn:          #8A5A00;
  --warn-ink:      #FFFFFF;
  --danger:        #B3261E;

  --white:         #0A1117;
  --text:          #17212B;
  --muted:         #5A6875;
  --muted-strong:  #3B4854;
  --muted-contrast:        #414E5B;
  --muted-strong-contrast: #26313C;
  --surface-contrast:      #FFFFFF;

  --border:        rgba(var(--tint-rgb),.13);
  --border-strong: rgba(var(--tint-rgb),.22);
  --border-accent: rgba(var(--accent-rgb),.50);

  --header-opaque: #F5F7FA;
  --mega-opaque:   #FFFFFF;
  --rack-1:        #EDF1F6;
  --rack-2:        #DEE5EC;

  /* Schatten tragen im Hellen viel weniger Alpha -- sonst wirkt jede Karte
     wie ausgeschnitten. */
  --shadow-header: 0 10px 30px -26px rgba(var(--shade-rgb),.55);
  --glow-accent:   0 0 0 1px rgba(var(--accent-rgb),.35), 0 18px 50px -26px rgba(var(--accent-rgb),.40);
  --shadow-card:   0 1px 2px rgba(var(--shade-rgb),.06), 0 14px 34px -24px rgba(var(--shade-rgb),.30);
  --shadow-float:  0 20px 54px -28px rgba(var(--shade-rgb),.34), 0 2px 6px rgba(var(--shade-rgb),.06);
}

/* ------------------------------------------------- 03 Grundlagen, Typografie */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1.5rem);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: clamp(1rem, .955rem + .22vw, 1.1875rem);
  line-height: 1.65;
  font-weight: 400;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;   /* die Seite selbst scrollt nie waagerecht */
}

/* Grundrauschen: ein feines technisches Raster und zwei sehr flache
   Lichtflaechen. Beides mit CSS gezeichnet -- kein Bild, kein Byte. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    linear-gradient(rgba(var(--tint-rgb),.022) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--tint-rgb),.022) 1px, transparent 1px);
  background-size: 72px 72px;
  mask-image: radial-gradient(120% 90% at 50% 0%, #000 0%, transparent 72%);
  -webkit-mask-image: radial-gradient(120% 90% at 50% 0%, #000 0%, transparent 72%);
}

h1, h2, h3, h4 {
  color: var(--white);
  font-weight: 640;
  margin: 0 0 var(--s-4);
  text-wrap: balance;
}

/* Laufweite ist groessenabhaengig: grosse Schrift wirkt zu weit gesetzt und
   wird enger, Kleingedrucktes bekommt etwas mehr Luft. Ein fester Wert waere
   irgendwo immer falsch. */
h1, .h1 {
  font-size: clamp(2.55rem, 1.55rem + 3.5vw, 5.25rem);
  /**
   * 1.02 war zu eng, sobald eine h1 umbricht.
   *
   * Gemeldet: auf /datenschutz/ sei das "g" von "Datenschutzerklaerung"
   * abgeschnitten. Gemessen bei 1440 px: Schriftgroesse 75,2 px,
   * Zeilenhoehe 76,7 px -- die TINTE einer Zeile ist aber 84 px hoch.
   * Zwei Zeilen brauchten 168 px und bekamen 153. Die Unterlaenge der
   * ersten Zeile lief damit in die Oberlaenge der zweiten.
   *
   * 1.08 ist weiterhin eng genug fuer die grosse Hero-Zeile und laesst
   * Unterlaengen stehen. Einzeilige Ueberschriften aendern sich dadurch
   * nicht sichtbar -- gemessen ueber alle 41 Seiten.
   */
  line-height: 1.08;
  letter-spacing: -.032em;
  font-weight: 660;
}
/* Sicherheitsnetz gegen zu lange Woerter. "Netzwerkbetreuung" ist bei 75 px
   breiter als eine halbe Spalte und lief vorher aus dem Kasten heraus, quer
   ueber die Grafik daneben. Umbrechen ist haesslicher als nicht umbrechen --
   Ueberlappen ist beides. */
/**
 * Lange deutsche Woerter in grosser Schrift.
 *
 * `overflow-wrap: break-word` allein bricht IRGENDWO -- aus
 * "Datenschutzerklaerung" wurde bei 1440 px "Datenschutzerklaeru" und in
 * der naechsten Zeile "ng". Das ist kein Satzfehler des Browsers, sondern
 * die einzige Anweisung, die er hatte.
 *
 * BIS 1.8.1 STAND HIER `hyphens: auto`, und das war der falsche Weg.
 *
 * Gemeldet wurde mit Bildschirmfotos: "IT-Betreuung für Un-/ternehmen",
 * "Private Cloud im Ver-/gleich", "Wie der Umzug eines be-/stehenden
 * Servers", "Was Betreuung bei einem Ma-/naged Server", "Organisation
 * ent-/scheidet". Alles Woerter von sieben bis elf Zeichen, die in die
 * naechste Zeile gepasst haetten. Der Browser hat trotzdem getrennt, weil
 * `text-wrap: balance` gleich lange Zeilen sucht und eine Trennstelle das
 * Ergebnis "ausgeglichener" macht. Zusammen ergeben die beiden Regeln eine
 * Schlagzeile, die aussieht, als waere sie zerbrochen.
 *
 * `hyphens: manual` schaltet nur die AUTOMATISCHE Trennung ab. Weiche
 * Trennzeichen im Text wirken weiter -- und genau darauf stuetzt sich diese
 * Website seit 1.7.3: trennhilfe() in app/helpers.php setzt sie an den
 * Fugen der zehn Woerter, die wirklich zu lang sind, und der Selbsttest
 * meldet jede Ueberschrift mit einem langen Wort, das dort noch fehlt. Das
 * ist die verlaessliche Loesung: Sie braucht kein Trennwoerterbuch im
 * Browser, sie trennt in jedem Browser gleich, und sie trennt nur dort, wo
 * jemand nachgesehen hat.
 *
 * `break-word` bleibt als letzte Reissleine fuer Woerter, die auch so nicht
 * passen -- eine URL etwa hat keine Fuge.
 */
h1, h2, h3, .h1, .h2 {
  overflow-wrap: break-word;
  hyphens: manual;
}
/* DIE H2 MUSS UNTER DER H1 BLEIBEN -- an jeder Fensterbreite.

   Vorher: h1 im Seitenkopf `clamp(2rem, 1.3rem + 2.2vw, 3.25rem)`,
           h2          `clamp(2rem, 1.42rem + 2.1vw, 3.5rem)`.
   Beide hatten dieselbe Untergrenze und die h2 die hoehere Obergrenze.
   Nachgemessen auf /it-wartung/:

     320 px   h1 32,0 px   h2 32,0 px   gleich gross
     390 px   h1 32,0 px   h2 32,0 px   gleich gross
    1440 px   h1 52,0 px   h2 53,0 px   die h2 war GROESSER

   Auf dem Telefon war der Seitentitel damit von jeder Abschnittsueberschrift
   nicht zu unterscheiden, auf dem Schreibtisch stand die Rangfolge auf dem
   Kopf. Gesenkt wird die h2, nicht die h1 angehoben: Eine groessere h1 macht
   den Seitenkopf auf schmalen Geraeten noch hoeher, und 32 px Abschnitts-
   ueberschrift auf einer 280 px breiten Spalte sind ohnehin zu viel -- die
   Zeile bricht dreimal, bevor sie etwas gesagt hat.

   Jetzt:  320 px  h1 32,0  h2 27,2   Verhaeltnis 1,18
          1440 px  h1 52,0  h2 45,9   Verhaeltnis 1,13 */
h2, .h2 {
  font-size: clamp(1.7rem, 1.25rem + 1.8vw, 3rem);
  line-height: 1.12;
  letter-spacing: -.024em;
}
h3, .h3 {
  font-size: clamp(1.3rem, 1.14rem + .62vw, 1.7rem);
  line-height: 1.24;
  letter-spacing: -.014em;
}
h4, .h4 {
  font-size: clamp(1.06rem, 1.01rem + .24vw, 1.22rem);
  line-height: 1.34;
  letter-spacing: -.008em;
  font-weight: 620;
}

/* `text-wrap: pretty` verhindert die einzelne Silbe auf der letzten Zeile.
   Der Vorspann hat es seit jeher (.lead weiter unten), der Fliesstext nicht
   -- dabei faellt ein Schusterjunge dort genauso auf. Es aendert
   ausschliesslich die Stelle, an der eine Zeile bricht: kein anderer Text,
   keine andere Schrift, keine andere Groesse. Browser, die es nicht kennen,
   setzen wie bisher. */
p { margin: 0 0 var(--s-5); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

.lead {
  font-size: clamp(1.08rem, 1rem + .5vw, 1.375rem);
  line-height: 1.55;
  color: var(--muted-strong);
  letter-spacing: -.006em;
  max-width: 62ch;
  text-wrap: pretty;
}

.prose { max-width: 72ch; }
.prose > * + * { margin-top: var(--s-5); }
.prose ul, .prose ol { margin: 0; padding-left: 1.35em; }
.prose li { margin-bottom: .5em; }
.prose li::marker { color: var(--accent); }
.prose strong { color: var(--white); font-weight: 620; }
.prose code {
  font-family: var(--mono);
  font-size: .88em;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: .12em .42em;
}

small, .small { font-size: .875rem; letter-spacing: .006em; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: .6em;
  font-family: var(--mono);
  font-size: .74rem;
  font-weight: 550;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--s-4);
}
.eyebrow::before {
  content: '';
  width: 1.6em;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent));
  flex: none;
}

.muted { color: var(--muted); }
.accent { color: var(--accent); }

::selection { background: rgba(var(--accent-rgb),.28); color: var(--white); }

hr {
  border: 0;
  height: 1px;
  background: var(--border);
  margin: var(--s-8) 0;
}

img, svg, video { max-width: 100%; height: auto; display: block; }

/* Sichtbarer Fokus, immer. Er wird nie entfernt, nur gestaltet -- ohne ihn
   ist die Seite mit der Tastatur unbedienbar. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}
:focus:not(:focus-visible) { outline: none; }

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

.skip-link {
  position: absolute;
  top: -100px; left: var(--s-4);
  z-index: 200;
  background: var(--accent);
  color: var(--accent-ink);
  padding: .75rem 1.25rem;
  border-radius: var(--r-sm);
  font-weight: 600;
  transition: top var(--dur-fast) var(--ease-out);
}
.skip-link:focus { top: var(--s-4); }

/* ------------------------------------------------- 04 Raster und Abschnitte */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
  position: relative;
  z-index: 1;
}
.container--narrow { max-width: var(--container-narrow); }
.container--wide   { max-width: var(--container-wide); }

.section { position: relative; }
.section--sp-sm { padding-block: clamp(2.5rem, 5vw, 3.5rem); }
.section--sp-md { padding-block: clamp(3.5rem, 7vw, 5.5rem); }
.section--sp-lg { padding-block: clamp(4.5rem, 9vw, 8rem); }
.section--sp-xl { padding-block: clamp(6rem, 12vw, 11rem); }

.section--surface { background: var(--bg-2); }
.section--surface::before,
.section--surface::after {
  content: ''; position: absolute; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--border-strong) 22%, var(--border-strong) 78%, transparent);
}
.section--surface::before { top: 0; }
.section--surface::after  { bottom: 0; }

.section--deep {
  background:
    radial-gradient(90% 70% at 78% 12%, rgba(var(--accent2-rgb),.13), transparent 62%),
    radial-gradient(70% 60% at 12% 88%, rgba(var(--accent-rgb),.10), transparent 60%),
    var(--bg-deep);
}

/* Auf der "deep"-Flaeche brauchen Breadcrumb und Eyebrow eine Stufe mehr.
   Gefunden beim Messen der Kopfbilder, und zwar an einer Stelle, an der gar
   kein Kopfbild liegt: Breadcrumb und Eyebrow kamen dort auf 4,39 bis 4,46:1
   -- knapp unter den geforderten 4,5. axe hatte es nicht gemeldet, weil die
   Flaeche zwei Verlaeufe traegt; Verlaeufe meldet axe als "nicht pruefbar",
   nicht als Verstoss. Der Fehler stand schon vor dieser Lieferung da.

   Betroffen sind /it-infrastruktur/lwl-glasfaser/ und
   /server-hosting/managed-server/ -- die beiden Seitenkoepfe mit dieser
   Flaeche. Beide Farben kommen aus dem Satz, der ohnehin da ist. */
.section--deep .breadcrumb a,
.section--deep .breadcrumb span { color: var(--muted-strong); }
.section--deep .eyebrow          { color: var(--accent-dim); }

.section--quiet { background: linear-gradient(180deg, var(--bg), var(--bg-2)); }

.section--accent {
  background:
    radial-gradient(70% 130% at 50% 0%, rgba(var(--accent-rgb),.14), transparent 66%),
    linear-gradient(180deg, var(--surface), var(--bg-2));
}
.section--accent::before {
  content: ''; position: absolute; inset-inline: 0; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  opacity: .55;
}

.section--center { text-align: center; }
.section--center .eyebrow { justify-content: center; }
.section--center .eyebrow::before { display: none; }
.section--center .lead { margin-inline: auto; }
/* text-align mittet nur Text. Knopfreihen sind Flex-Behaelter und bleiben
   davon unberuehrt -- sie klebten deshalb links, waehrend darueber alles
   mittig stand. Dasselbe gilt fuer Statuszeilen und Merkmalslisten. */
.section--center .cta-band__actions,
.section--center .cluster,
.section--center .btn-row,
.section--center .state-list,
.section--center .trust { justify-content: center; }
/* Und ein Element mit eigener Maximalbreite muss sich selbst mitten stellen,
   sonst haengt der Kasten links und nur seine Zeilen sind mittig. */
.section--center .cta-band h2,
.section--center h2,
.section--center .prose { margin-inline: auto; }

/* Abschnittskopf */
.sec-head { max-width: 78ch; margin-bottom: clamp(2rem, 4vw, 3.5rem); }
.section--center .sec-head { margin-inline: auto; }
.sec-head > :last-child { margin-bottom: 0; }

/* Freies Spaltenraster.
   --cols setzt die Anzahl, --tracks optional die Verhaeltnisse. Beides kommt
   aus den Layout-Einstellungen des Blocks und wird serverseitig geprueft --
   im CSS steht nur, wie daraus ein Raster wird. */
.grid {
  display: grid;
  gap: clamp(1rem, 2.2vw, 1.75rem);
  grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
}
/* Spaltenzahl und Verhaeltnisse kommen als Klasse, nicht als style-Attribut:
   Die CSP verbietet Inline-Styles, und ein Attribut waere hier ohnehin die
   schlechtere Loesung -- so lassen sich die Umbrueche unten sauber ueberschreiben. */
.cols-1 { --cols: 1; }
.cols-2 { --cols: 2; }
.cols-3 { --cols: 3; }
.cols-4 { --cols: 4; }
.grid.has-tracks { grid-template-columns: var(--tracks); }

@media (max-width: 1080px) {
  .grid { grid-template-columns: repeat(min(var(--cols, 3), 2), minmax(0, 1fr)); }
  .grid.has-tracks { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 680px) {
  .grid,
  .grid.has-tracks { grid-template-columns: minmax(0, 1fr); }
}

/* Zwei Spalten mit frei einstellbarem Verhaeltnis (Block "split", "rack"). */
.split {
  display: grid;
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
  grid-template-columns: var(--tracks, 1fr 1fr);
}
.split--reverse > :first-child { order: 2; }
@media (max-width: 900px) {
  .split { grid-template-columns: minmax(0, 1fr); }
  .split--reverse > :first-child { order: 0; }
}

.stack > * + * { margin-top: var(--s-5); }
.cluster { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; }

/* --------------------------------------------------- 05 Buttons und Links */

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: .22em; }
a:hover { color: var(--accent-hover); }

.btn {
  --btn-bg: var(--accent);
  --btn-fg: var(--accent-ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6em;
  padding: .82rem 1.5rem;
  min-height: 48px;          /* Trefferflaeche deutlich ueber 24 px */
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: inherit;
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -.006em;
  text-decoration: none;
  cursor: pointer;
  position: relative;
  isolation: isolate;
  transition:
    transform var(--dur-press) var(--ease-out),
    box-shadow var(--dur) var(--spring),
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
  box-shadow: 0 0 0 0 rgba(var(--accent-rgb),0);
}
.btn:hover { box-shadow: 0 12px 34px -14px rgba(var(--accent-rgb),.7); color: var(--btn-fg); }
/* Rueckmeldung beim Druecken, nicht erst beim Loslassen. */
.btn:active { transform: scale(.972); transition-duration: var(--dur-press); }

.btn .icon { transition: transform var(--dur) var(--spring); }
.btn:hover .icon { transform: translateX(3px); }

.btn--secondary {
  --btn-bg: transparent;
  --btn-fg: var(--white);
  border-color: var(--border-strong);
  backdrop-filter: blur(8px);
}
.btn--secondary:hover {
  border-color: var(--border-accent);
  background: rgba(var(--accent-rgb),.07);
  box-shadow: none;
}

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--muted-strong);
  padding-inline: .5rem;
}
.btn--ghost:hover { --btn-fg: var(--white); box-shadow: none; }

.btn--lg { padding: 1rem 1.9rem; font-size: 1.06rem; min-height: 56px; }
.btn--block { width: 100%; }

/* Textlink mit Pfeil, der beim Zeigen vorruckt. */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: .45em;
  font-weight: 570;
  text-decoration: none;
  color: var(--accent);
}
.link-arrow .icon { transition: transform var(--dur) var(--spring); }
.link-arrow:hover .icon { transform: translateX(4px); }
.link-arrow::after {
  content: '';
  position: absolute;
  inset: 0;
}
.link-arrow--plain::after { content: none; position: static; }

/* ---------------------------------------------------- 06 Karten und Flaechen */

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: clamp(1.4rem, 2.4vw, 2rem);
  background: linear-gradient(180deg, rgba(var(--tint-rgb),.035), rgba(var(--tint-rgb),.012));
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  backdrop-filter: blur(14px) saturate(130%);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  transition:
    border-color var(--dur) var(--spring),
    transform var(--dur) var(--spring),
    box-shadow var(--dur) var(--spring),
    background-color var(--dur) var(--spring);
}
/* Eine Lichtkante oben -- so, wie Licht auf einer Glasflaeche auftrifft. */
.card::before {
  content: '';
  position: absolute;
  inset-inline: 12%;
  top: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(var(--tint-rgb),.28), transparent);
  opacity: .7;
}
.card--link:hover,
.card--link:focus-within {
  border-color: var(--border-accent);
  transform: translateY(-3px);
  box-shadow: var(--shadow-float), 0 0 40px -22px rgba(var(--accent-rgb),.7);
}
.card--outline { background: transparent; backdrop-filter: none; box-shadow: none; }
.card--plain {
  background: none; border: 0; border-radius: 0; box-shadow: none;
  backdrop-filter: none; padding: 0;
}
.card--plain::before { content: none; }

.card__icon {
  display: grid;
  place-items: center;
  width: 46px; height: 46px;
  border-radius: 13px;
  color: var(--accent);
  background: rgba(var(--accent-rgb),.09);
  border: 1px solid rgba(var(--accent-rgb),.20);
  flex: none;
  margin-bottom: var(--s-2);
}
.card__title { margin: 0; }
.card__text { margin: 0; color: var(--muted); }
.card__foot { margin-top: auto; padding-top: var(--s-4); }

/* Abzeichen und Ampel -- Farbe traegt die Aussage nie allein, der Text steht
   immer daneben. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  font-family: var(--mono);
  font-size: .7rem;
  font-weight: 550;
  letter-spacing: .11em;
  text-transform: uppercase;
  padding: .34em .8em;
  border-radius: var(--r-pill);
  border: 1px solid var(--border);
  background: rgba(var(--tint-rgb),.04);
  color: var(--muted-strong);
  white-space: nowrap;
}
.badge__dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: currentColor;
  flex: none;
}
.badge--ok      { color: var(--success); border-color: rgba(var(--success-rgb),.30); background: rgba(var(--success-rgb),.07); }
.badge--info    { color: var(--accent);  border-color: rgba(var(--accent-rgb),.30);  background: rgba(var(--accent-rgb),.07); }
.badge--warn    { color: var(--warn);    border-color: rgba(var(--warn-rgb),.30); background: rgba(var(--warn-rgb),.07); }
.badge--neutral { color: var(--muted);   }
.badge--ok .badge__dot { box-shadow: 0 0 0 3px rgba(var(--success-rgb),.16); }

.tag {
  display: inline-block;
  font-size: .8rem;
  padding: .3em .7em;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  color: var(--muted-strong);
  background: rgba(var(--tint-rgb),.03);
}

/* --------------------------------------------------------- 06b Kopfleiste

   Ueber der Navigation steht die Zeile fuer den Fall, dass etwas nicht
   funktioniert: Servicenummer, Zentrale, Bürozeiten, Fernwartung. Sie ist
   bewusst nicht klebrig -- wer sie braucht, ist am Seitenanfang und sucht
   nicht, sondern will anrufen. Beim Scrollen macht sie der schlanken,
   klebenden Navigation Platz.
   ------------------------------------------------------------------------ */

.topbar {
  position: relative;
  z-index: 101;
  background: var(--bg-deep);
  border-bottom: 1px solid var(--border);
  font-size: .82rem;
  line-height: 1.2;
}
.topbar__inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .35rem 1.15rem;
  min-height: 38px;
  padding-block: .3rem;
}
/* Die Servicenummer steht links und ist der einzige Eintrag, der auch auf
   dem Telefon bleibt. Alles Weitere steht rechts.

   Bis 1.7.3 erledigte das ein `margin-inline-end: auto` an der Servicenummer.
   Seit 1.7.4 steht ein Zitat in der Mitte der Leiste, und ein automatischer
   Aussenabstand kann nichts mittig setzen -- er schiebt nur, was danach
   kommt. Jetzt sind es drei Flex-Elemente: zwei gleich breite Seiten und der
   Satz dazwischen. `flex: 1 1 0` heisst "beide gleich breit, unabhaengig vom
   Inhalt" -- genau daraus folgt, dass die Mitte die Mitte ist. */
.topbar__seite {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .35rem 1.15rem;
  flex: 1 1 0;
  min-width: 0;
}
.topbar__seite--rechts { justify-content: flex-end; }

/* Das Zitat. Es traegt keinen Weg zur Hilfe, also tritt es zurueck, sobald
   der Platz knapp wird -- die Nummer und die Fernwartung haben Vorrang.
   Seit Zentrale und Buerozeiten aus der Leiste heraus sind, passt es
   deutlich frueher: gemessen bis hinunter zu 700 Punkten, gesetzt auf 760
   als Reserve fuer eine laengere Servicenummer oder einen laengeren Satz. */
.topbar__motto {
  flex: 0 0 auto;
  margin: 0;
  /* --muted-strong statt --muted: nachgemessen 10,99:1 im dunklen und
     7,87:1 im hellen Thema gegen --bg-deep. --muted kaeme im hellen Thema
     auf 4,81 -- das haelt die Grenze, aber bei kursiver Schrift in 13
     Punkten will man dort nicht stehen. */
  color: var(--muted-strong);
  font-style: italic;
  letter-spacing: .01em;
  white-space: nowrap;
}
@media (max-width: 759px) { .topbar__motto { display: none; } }

.topbar__phone,
.topbar__remote {
  display: inline-flex;
  align-items: center;
  gap: .45em;
  white-space: nowrap;
}
.topbar__phone {
  color: var(--muted-strong);
  text-decoration: none;
  border-radius: var(--r-sm);
  padding: .3rem .1rem;
  transition: color var(--dur-fast) var(--ease-out);
}
.topbar__phone strong {
  color: var(--white);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
}
.topbar__phone:hover { color: var(--muted-strong); }
.topbar__phone:hover strong { color: var(--accent); }
.topbar__phone .icon { color: var(--accent); flex: none; }
.topbar__phone--service .icon { color: var(--success); }

.topbar__label {
  color: var(--muted);
  font-size: .78rem;
}
.topbar__label::after { content: ':'; }


/* Fernwartung ist ein Knopf, kein Link: sie startet etwas, statt irgendwo
   hinzufuehren. Deshalb traegt sie einen Rahmen und das Symbol fuer ein
   fremdes Ziel. */
.topbar__remote {
  padding: .34rem .8rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  background: rgba(var(--tint-rgb),.04);
  color: var(--white);
  text-decoration: none;
  font-weight: 550;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.topbar__remote:hover {
  border-color: var(--border-accent);
  background: rgba(var(--accent-rgb),.10);
  color: var(--accent);
}
.topbar__remote .icon { flex: none; }
.topbar__ext { opacity: .55; }

/* Umschalter fuer das Farbschema. Ohne JavaScript hat er keine Aufgabe und
   ist deshalb bis dahin ausgeblendet -- ein toter Knopf ist schlimmer als
   keiner. Die Website folgt dann weiter der Browsereinstellung. */
.theme-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 34px; height: 34px;
  flex: none;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: rgba(var(--tint-rgb),.04);
  color: var(--muted-strong);
  cursor: pointer;
  transition:
    color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out);
}
.js .theme-toggle { display: inline-flex; }
.theme-toggle:hover { color: var(--accent); border-color: var(--border-accent); }
.theme-toggle .icon { transition: transform var(--dur) var(--spring), opacity var(--dur-fast) linear; }
/* Genau ein Symbol ist sichtbar: das des Themas, in das der Knopf fuehrt. */
.theme-toggle__moon { display: none; }
:root[data-theme='light'] .theme-toggle__moon { display: block; }
:root[data-theme='light'] .theme-toggle__sun  { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) .theme-toggle__moon { display: block; }
  :root:not([data-theme='dark']) .theme-toggle__sun  { display: none; }
}
.theme-toggle:hover .icon { transform: rotate(18deg); }

/* Enger: erst die Bürozeiten, dann die Zentrale, dann die ganze Zeile. Was
   bleibt, bleibt in dieser Reihenfolge, weil die Servicenummer im Ernstfall
   die einzige ist, die zaehlt. */
/* Buerozeiten und Zentrale standen bis 1.7.3 in dieser Leiste und wurden
   hier je nach Breite ausgeblendet. Seit 1.7.4 stehen sie gar nicht mehr
   darin -- die Regeln sind damit gegenstandslos. Die Angaben selbst sind
   nicht weg: Sie stehen in der Fusszeile, auf der Kontaktseite und im
   ausgeklappten Menue auf dem Telefon. */
@media (max-width: 640px)  {
  /* Nur noch das Symbol: die Beschriftung und der Hinweis auf das fremde
     Ziel wuerden hier die Servicenummer aus der Zeile draengen.

     Die Beschriftung verschwindet dabei optisch, nicht aus dem Dokument --
     mit display:none haette der Knopf fuer einen Screenreader gar keinen
     Namen mehr und waere nur noch "Link". */
  .topbar__remote-label {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .topbar__ext { display: none; }
  .topbar__remote { padding-inline: .55rem; }
  .topbar__label { display: none; }
}

/* --------------------------------------- 07 Kopfbereich und Navigation */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  /* Durchscheinende Leiste: der Inhalt laeuft darunter durch, statt dass ein
     undurchsichtiger Streifen dauerhaft Platz verbraucht. */
  background: rgba(var(--bg-rgb),.62);
  backdrop-filter: blur(18px) saturate(180%);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  border-bottom: 1px solid transparent;
  transition:
    background-color var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out),
    margin-bottom var(--dur) var(--spring);
}
.site-header.is-scrolled {
  background: rgba(var(--bg-rgb),.86);
  border-bottom-color: var(--border);
  box-shadow: var(--shadow-header);
  /* DIE LEISTE SCHRUMPFT, DER PLATZ BLEIBT.
     `position: sticky` laesst das Element im Fluss: Wird es niedriger,
     rutscht die ganze Seite darunter nach oben -- und weil dadurch der
     Inhalt oberhalb des Sichtfensters kuerzer wird, zieht der Browser die
     Scrollposition nach. Diese zieht wiederum an der Schwelle, ab der die
     Leiste schrumpft. Gemeldet wurde das als "der Banner huepft ganz leicht,
     sieht aus wie ein Zittern" -- nachgemessen sprang die Scrollposition
     beim langsamen Scrollen zwischen 4 und 12 Punkten hin und her.
     Der Aussenabstand ersetzt genau die Hoehe, die die Leiste abgibt. Die
     Summe aus Hoehe und Abstand bleibt damit in jedem Moment der
     Bewegung --header-h, und unter der Leiste bewegt sich nichts.
     Sichtbar ist der Abstand nicht: Dort laeuft der Inhalt ohnehin unter
     der durchscheinenden Leiste durch. */
  margin-bottom: calc(var(--header-h) - var(--header-h-klein));
}
/* Weiche Kante statt harter Trennlinie, und nur dort, wo Inhalt darunter
   durchlaeuft. */
.site-header::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 100%;
  height: 22px;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(var(--bg-rgb),.55), transparent);
  opacity: 0;
  transition: opacity var(--dur) var(--ease-out);
}
.site-header.is-scrolled::after { opacity: 1; }

.header-inner {
  display: flex;
  align-items: center;
  gap: var(--s-5);
  max-width: var(--container-wide);
  min-height: var(--header-h);
  transition: min-height var(--dur) var(--spring);
}
.site-header.is-scrolled .header-inner { min-height: var(--header-h-klein); }

.brand {
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  text-decoration: none;
  color: var(--white);
  font-weight: 660;
  font-size: 1.22rem;
  letter-spacing: -.028em;
  flex: none;
}
.brand__mark { width: 30px; height: 30px; flex: none; color: var(--accent); }

/* Eigenes Logo statt der eingebauten Marke.
   Beide Fassungen stehen im Markup und die CSS entscheidet, welche gilt --
   so ist beim Themawechsel kein Nachladen noetig und es blitzt nichts auf.
   Die Hoehe steuert eine Klasse, weil die CSP keine Style-Attribute zulaesst. */
.brand__logo {
  display: block;
  width: auto;
  height: var(--logo-h, 34px);
  max-width: min(240px, 46vw);
  object-fit: contain;
}
.brand--custom { gap: 0; }
.brand__logo--light { display: none; }
:root[data-theme='light'] .brand__logo--light { display: block; }
:root[data-theme='light'] .brand__logo--dark  { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) .brand__logo--light { display: block; }
  :root:not([data-theme='dark']) .brand__logo--dark  { display: none; }
}
.logo-h-24 { --logo-h: 24px; }
.logo-h-28 { --logo-h: 28px; }
.logo-h-32 { --logo-h: 32px; }
.logo-h-36 { --logo-h: 36px; }
.logo-h-40 { --logo-h: 40px; }
.logo-h-48 { --logo-h: 48px; }
.logo-h-56 { --logo-h: 56px; }
.logo-h-64 { --logo-h: 64px; }

.brand__sub {
  font-family: var(--mono);
  font-size: .58rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
  display: block;
  margin-top: -2px;
  font-weight: 500;
}

.nav { margin-left: auto; }
.nav__list {
  display: flex;
  align-items: center;
  gap: .15rem;
  list-style: none;
  margin: 0; padding: 0;
}
.nav__link {
  display: inline-flex;
  align-items: center;
  gap: .35em;
  /* Ein Menuepunkt mit Untermenue ist ein <button>. Ohne diese Zeilen traegt er
     die Standardflaeche des Browsers und faellt aus der Reihe. */
  background: none;
  border: 0;
  font: inherit;
  cursor: pointer;
  padding: .5rem .66rem;
  border-radius: var(--r-sm);
  color: var(--muted-strong);
  text-decoration: none;
  font-size: .92rem;
  font-weight: 500;
  letter-spacing: -.004em;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.nav__link:hover { color: var(--white); background: rgba(var(--tint-rgb),.05); }

/* Beschriftung und Pfeil sind seit 1.8.4 zwei Bedienelemente -- sie sollen
   trotzdem wie eines aussehen. Die Flaeche beim Zeigen liegt deshalb auf der
   Huelle, nicht auf dem Link: Sonst leuchtete nur die Haelfte auf, ueber der
   der Zeiger gerade steht.

   Die Innenabstaende sind so aufgeteilt, dass die Gruppe zusammen so breit
   ist wie der eine Knopf vorher -- die Navigation ist auf Laptopbreiten
   knapp, jeder zusaetzliche Punkt kostet dort Platz. */
.nav__head { display: inline-flex; align-items: center; border-radius: var(--r-sm); }
.nav__head:hover { background: rgba(var(--tint-rgb),.05); }
.nav__head:hover .nav__link { color: var(--white); background: none; }
.nav__link--gruppe { padding-right: .18rem; }
.nav__pfeil {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer; color: inherit;
  /* 24 Punkte breit: die kleinste Flaeche, die WCAG 2.5.8 zulaesst. */
  min-width: 24px; min-height: 24px;
  padding: .5rem .5rem .5rem .1rem;
  border-radius: var(--r-sm);
  color: var(--muted-strong);
  transition: color var(--dur-fast) var(--ease-out);
}
.nav__head:hover .nav__pfeil, .nav__pfeil:hover { color: var(--white); }
.nav__link[aria-current='page'],
.nav__link.is-active { color: var(--white); }
.nav__link[aria-current='page']::after,
.nav__link.is-active::after {
  content: '';
  position: absolute;
  left: .66rem; right: .66rem;
  bottom: .1rem;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
}
.nav__item { position: relative; }

/* ---------------------------------------------------------------------------
   Der Seitenkopf auf Laptopbreiten

   Gemeldet: "Header bei kleineren Bildschirm abgeschnitten (Laptop)".
   Gemessen auf der Startseite: Die waagerechte Navigation schaltet ab 1241 px
   ein, der Kopf braucht ausgefahren aber 1427 px. Dazwischen stand der
   CTA ueber dem rechten Rand hinaus:

       1280 px  ->  147 px abgeschnitten
       1320 px  ->  107 px
       1366 px  ->   61 px   (die haeufigste Laptopaufloesung)
       1400 px  ->   27 px
       1440 px  ->    0

   Bei 1366 war von "IT-Beratung anfragen" das halbe Wort weg. Aufgefallen
   ist es nie, weil die Seite dabei KEINEN waagerechten Rollbalken bekommt --
   der Kopf ist fest positioniert und schneidet ab, statt zu schieben. Der
   Durchlauf, der auf document.scrollWidth prueft, sah deshalb nichts.

   Verworfen: die Navigation erst ab 1440 px einblenden. Dann traegt genau
   der Bereich, in dem die meisten Geschaefts-Laptops stehen, das
   Hamburger-Menue -- und die acht Bereiche, um die es geht, waeren dort
   hinter zwei Klicks.

   Stattdessen wird der Kopf in diesem Bereich enger gesetzt: weniger
   Innenabstand je Menuepunkt, etwas kleinere Schrift, schmalerer CTA. Die
   Trefferflaechen bleiben dabei ueber 24 px (gemessen).

   SEIT 1.8.5 REICHT DIESE STUFE BIS 1839 PX statt bis 1440. Grund ist der
   neunte Menuepunkt: Mit den urspruenglichen Abstaenden braucht der Kopf
   1810 px, und zwischen 1441 und 1810 stand der CTA wieder ueber dem Rand --
   bei 1600 px um 109 Punkte. Mit den engeren Werten passt er ab 1542 px;
   darunter uebernehmen die kurzen Beschriftungen (weiter unten). Ab 1840 px
   gilt wieder die urspruengliche Fassung. */
@media (min-width: 1241px) and (max-width: 1839px) {
  .header-inner { gap: var(--s-3); }
  .nav__link    { padding-inline: .36rem; font-size: .858rem; }
  .nav__link[aria-current='page']::after,
  .nav__link.is-active::after { left: .36rem; right: .36rem; }
  .header-cta .btn { padding-inline: 1.05rem; font-size: .93rem; }
  .brand        { font-size: 1.1rem; }
}

/* ---------------------------------------------------------------------------
   Kurze Beschriftungen, wo die lange Zeile nicht mehr hinpasst (1.8.5)

   Mit "IT-Infrastruktur" traegt das Hauptmenue neun Punkte. Gemessen am
   fertigen Kopf der Startseite, Ueberstand des CTA ueber den rechten
   Fensterrand bei voller Beschriftung:

       1241 px -> 181 px abgeschnitten      1440 px ->  49 px
       1280 px -> 142 px                    1600 px -> 109 px
       1366 px -> 123 px                    1800 px ->   9 px

   Die volle Zeile braucht also rund 1810 px, mit den engeren Werten oben
   immer noch 1542. Engere Abstaende allein reichen nicht: Bei 12,8 px
   Schrift und fast ohne Innenabstand fehlten bei 1241 px immer noch
   200 Punkte -- die Beschriftungen selbst sind der Platzbedarf.

   Deshalb zeigen die drei Doppelnamen unterhalb von 1580 px nur ihren
   ersten Teil ("Server & Hosting" -> "Server"). Damit passt die Zeile
   lueckenlos ab 1241 px, also bis hinunter zu der Breite, ab der ohnehin
   das Hamburger-Menue uebernimmt.

   Verworfen: die Navigation erst ab 1580 px einblenden. Dann traegt genau
   der Bereich, in dem die meisten Geschaefts-Laptops stehen (1280 und
   1366 px), das Hamburger-Menue -- und die Bereiche lagen dort hinter zwei
   Klicks. Ebenfalls verworfen: "IT-Infrastruktur" statt "Leistungen" ins
   Hauptmenue. Gemessen passt auch das erst ab 1600 px.

   Beide Fassungen stehen im Markup; die verdeckte steht auf display:none
   und zaehlt damit weder fuer die Vorlesefunktion noch fuer die
   Suchmaschine zum Namen des Links. */
.nav__wort--kurz { display: none; }

@media (min-width: 1241px) and (max-width: 1579px) {
  .nav__list .nav__wort--lang { display: none; }
  .nav__list .nav__wort--kurz { display: inline; }
}

/* Unter 1340 px wird es noch einmal enger -- dort fehlen die meisten Punkte. */
@media (min-width: 1241px) and (max-width: 1339px) {
  .nav__link { padding-inline: .26rem; font-size: .83rem; }
  .nav__link[aria-current='page']::after,
  .nav__link.is-active::after { left: .26rem; right: .26rem; }
  .header-cta .btn { padding-inline: .85rem; font-size: .9rem; }
}

/* DAS BREITE PANEL HAENGT AM SEITENCONTAINER, NICHT AM AUSLOESER.

   Vorher hing es an seinem Menuepunkt: `left: 50%` plus Verschiebung um die
   halbe eigene Breite. Bei "Leistungen" -- dem zweiten Punkt von links --
   lag die Mitte des 920 Punkte breiten Panels damit bei etwa 268 Punkten,
   die linke Kante also bei minus 96. Die erste Menuespalte stand ausserhalb
   des Fensters und war nicht erreichbar; sichtbar waren drei von fuenf.

   `position: static` nimmt dem Menuepunkt die Rolle als Bezugsrahmen. Der
   naechste Vorfahr mit `position: relative` ist dann der Seitencontainer --
   und darin zentriert sich das Panel sauber, unabhaengig davon, an welcher
   Stelle der Auslaeser sitzt.

   Die schmalen Klapplisten bleiben an ihrem Auslaeser: Sie sind schmal genug,
   dass sie nicht anstossen, und direkt darunter gehoeren sie auch hin. */
.header-inner { position: relative; }
.nav__item--breit { position: static; }

.nav__link .icon, .nav__pfeil .icon { transition: transform var(--dur) var(--spring); opacity: .7; }
.nav__item[data-open='true'] .nav__pfeil .icon { transform: rotate(180deg); }

/* Mega-Menu. Es klappt aus seinem Ausloeser heraus auf.

   ES IST UNDURCHSICHTIG, und das ist eine Korrektur aus 1.6.0.

   Vorher stand hier `rgba(var(--mega-rgb),.92)` mit
   `backdrop-filter: blur(26px)`. Gedacht war: die Seite darunter verschwimmt,
   das Panel wirkt wie Material. Tatsaechlich blieb der Inhalt gestochen
   scharf lesbar -- auf der Leistungsseite las man die Ueberschrift des
   Seitenkopfs mitten durch die Menuespalten hindurch.

   Der Grund: `.site-header` traegt selbst einen `backdrop-filter`. Damit
   wird die Kopfleiste zur "Backdrop Root", und fuer das Panel darin ist
   hinter ihr nichts mehr zu verwischen. Die Unschaerfe lief ins Leere, uebrig
   blieben 92 % Deckkraft -- und durch die restlichen 8 % liest man mit.

   NACHGEMESSEN (Kantenenergie hinter dem Panel, hoher Wert = scharf):
     mit backdrop-filter an der Kopfleiste     3,43
     ohne backdrop-filter an der Kopfleiste    2,24   (Unschaerfe greift)
   Auch 2,24 ist noch sichtbarer Inhalt. Ein Menue muss lesbar sein, immer,
   ueber jedem Hintergrund -- deshalb deckend statt durchscheinend. Das
   erspart nebenbei die zweite Unschaerfeebene beim Oeffnen.

   HOEHE. Auf einem Laptop war das Panel hoeher als das Fenster: 853 Punkte
   bei 800 Punkten Fensterhoehe, 161 Punkte ragten unten hinaus. Die letzten
   Eintraege einer Spalte waren damit nicht erreichbar. Jetzt begrenzt es
   sich auf die Fensterhoehe und rollt notfalls innen. */
.mega {
  position: absolute;
  top: calc(100% + 10px);
  left: 50%;
  transform-origin: top center;
  width: min(920px, calc(100vw - 3rem));
  max-height: calc(100vh - 8rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: clamp(1.25rem, 2vw, 1.75rem);
  background: var(--mega-opaque);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-float);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -8px) scale(.975);
  transition:
    opacity var(--dur-fast) var(--ease-out),
    transform var(--dur) var(--spring),
    visibility 0s linear var(--dur);
}
.nav__item[data-open='true'] > .mega {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0) scale(1);
  transition-delay: 0s;
}
/* Die schmale Klappliste der vier Bereiche. Dieselbe Flaeche, dieselben
   Zeilen wie im grossen Menue -- nur eine Spalte und am Auslaeser statt in
   der Mitte. Links ausgerichtet, damit sie unter ihrem Menuepunkt beginnt
   und nicht darueber hinaus nach links wandert.

   KEIN EIGENER INNENABSTAND MEHR (1.8.3). Bis dahin stand hier
   `padding: .6rem` -- 9,6 statt 28 Punkte. Nebeneinander gesehen wirkten die
   vier Bereichsmenues dadurch gedraengt, waehrend "Leistungen" Luft hatte;
   gemeldet wurde genau das. Jetzt gilt der Wert des grossen Menues fuer
   alle, und die Klappliste wird breiter, damit der Text dieselbe Zeilenbreite
   behaelt wie vorher: 320 + zweimal die Differenz der Innenabstaende. */
.mega--schmal {
  left: 0;
  width: min(376px, calc(100vw - 3rem));
  transform: translate(0, -8px) scale(.975);
}
.nav__item[data-open='true'] > .mega--schmal { transform: translate(0, 0) scale(1); }

.mega__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: clamp(1rem, 2vw, 1.75rem);
}
.mega__col-title {
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 .8rem;
  font-weight: 550;
}
.mega__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.mega__link {
  display: block;
  padding: .5rem .6rem;
  margin-inline: -.6rem;
  border-radius: var(--r-sm);
  color: var(--text);
  text-decoration: none;
  font-size: .93rem;
  line-height: 1.35;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.mega__link:hover, .mega__link:focus-visible { background: rgba(var(--accent-rgb),.09); color: var(--white); }
.mega__link small { display: block; color: var(--muted); font-size: .78rem; margin-top: 1px; }
.mega__foot {
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  align-items: center;
  justify-content: space-between;
}

.header-cta { flex: none; }
.nav-toggle { display: none; }

@media (max-width: 1240px) {
  .nav, .header-cta .btn span { display: none; }
  .nav-toggle {
    display: inline-flex;
    align-items: center; justify-content: center;
    width: 46px; height: 46px;
    margin-left: auto;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-sm);
    background: rgba(var(--tint-rgb),.04);
    color: var(--white);
    cursor: pointer;
  }
  .header-cta { display: none; }
}

/* Mobile Navigation. Sie erscheint als Flaeche von oben und verschwindet
   denselben Weg -- gleiche Richtung hin wie zurueck. */
.mobile-nav {
  position: fixed;
  inset: var(--header-h) 0 0;
  z-index: 99;
  background: rgba(var(--bg-rgb),.97);
  backdrop-filter: blur(24px);
  padding: var(--s-6) var(--gutter) var(--s-10);
  overflow-y: auto;
  overscroll-behavior: contain;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-12px);
  transition:
    opacity var(--dur-fast) var(--ease-out),
    transform var(--dur) var(--spring),
    visibility 0s linear var(--dur);
}
.mobile-nav.is-open {
  opacity: 1; visibility: visible; transform: none; transition-delay: 0s;
}
.mobile-nav__group { border-bottom: 1px solid var(--border); }
.mobile-nav__group > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: 1rem 0;
  font-size: 1.12rem;
  font-weight: 570;
  color: var(--white);
  cursor: pointer;
  list-style: none;
}
.mobile-nav__group > summary::-webkit-details-marker { display: none; }
.mobile-nav__group > summary .icon { transition: transform var(--dur) var(--spring); color: var(--muted); }
.mobile-nav__group[open] > summary .icon { transform: rotate(180deg); }
.mobile-nav__sub { list-style: none; margin: 0 0 var(--s-4); padding: 0; display: grid; gap: 1px; }
.mobile-nav__sub a {
  display: block; padding: .6rem 0; color: var(--muted-strong);
  text-decoration: none; font-size: 1rem;
}
.mobile-nav__sub a:hover { color: var(--accent); }
.mobile-nav__link {
  display: block; padding: 1rem 0; color: var(--white);
  text-decoration: none; font-size: 1.12rem; font-weight: 570;
  border-bottom: 1px solid var(--border);
}
.mobile-nav__cta { margin-top: var(--s-7); }
/* Im mobilen Menue stehen dieselben Wege wie in der Kopfleiste, nur gross
   genug fuer einen Daumen. */
.mobile-nav__contact {
  display: flex;
  align-items: center;
  gap: .85rem;
  margin-top: var(--s-3);
  padding: .8rem 1rem;
  min-height: 56px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: rgba(var(--tint-rgb),.03);
  color: var(--white);
  text-decoration: none;
}
.mobile-nav__contact .icon { color: var(--accent); flex: none; }
.mobile-nav__contact strong {
  display: block;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.mobile-nav__contact-label {
  display: block;
  font-size: .78rem;
  color: var(--muted);
}
.mobile-nav__theme {
  display: none;
  align-items: center;
  gap: .85rem;
  width: 100%;
  margin-top: var(--s-3);
  padding: .8rem 1rem;
  min-height: 56px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: rgba(var(--tint-rgb),.03);
  color: var(--white);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.js .mobile-nav__theme { display: flex; }
.mobile-nav__theme .icon { color: var(--accent); flex: none; }


body.nav-open { overflow: hidden; }

/* Brotkrumen */
.breadcrumb { margin-bottom: var(--s-4); }
.breadcrumb ol {
  display: flex; flex-wrap: wrap; align-items: center; gap: .4rem;
  list-style: none; margin: 0; padding: 0;
  font-size: .84rem; color: var(--muted);
}
.breadcrumb a { color: var(--muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--accent); }
.breadcrumb li + li::before { content: '/'; margin-right: .4rem; color: rgba(var(--tint-rgb),.2); }
.breadcrumb [aria-current='page'] { color: var(--muted-strong); }

/* ------------------------------------------------------------- 08 Bloecke */

/* --- Hero ---------------------------------------------------------------- */
.hero {
  position: relative;
  padding-block: clamp(3.5rem, 9vw, 8rem) clamp(3rem, 7vw, 6rem);
  overflow: hidden;
}
/* Mit Breadcrumb faengt der Kopf oben deutlich schmaeler an -- der Weg
   gehoert dicht an die Navigation, aus der er kommt. Der Abstand, den die
   Ueberschrift zur Kopfleiste braucht, entsteht dann durch den Breadcrumb
   selbst und nicht durch Leerraum darueber. */
.hero--crumbs { padding-top: clamp(1.5rem, 3vw, 2.5rem); }
.hero::before {
  content: '';
  position: absolute;
  inset: -30% -10% auto -10%;
  height: 120%;
  pointer-events: none;
  background:
    radial-gradient(46% 46% at 72% 30%, rgba(var(--accent-rgb),.16), transparent 68%),
    radial-gradient(42% 42% at 22% 12%, rgba(var(--accent2-rgb),.14), transparent 66%);
  filter: blur(8px);
}
.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr);
  gap: clamp(2.5rem, 5vw, 5rem);
  align-items: center;
}
/* Die zweispaltige Fassung braucht eine kleinere Ueberschrift.

   Der Grund ist die deutsche Sprache: "Standortvernetzung" ist ein Wort und
   laesst sich nicht umbrechen. In der halben Spalte war es bei 75 px breiter
   als der Platz und lief quer ueber die Grafik daneben. Silbentrennung waere
   die elegante Loesung, aber `hyphens: auto` braucht ein Woerterbuch der
   Dokumentsprache, und darauf ist kein Verlass -- fehlt es, bricht der
   Browser mitten im Wort um, ohne Trennstrich ("Netzwerkbetre / uung").

   Also die verlaessliche Loesung: In zwei Spalten wird die Schrift so weit
   gedeckelt, dass auch das laengste dieser Woerter in eine Zeile passt.
   Die breite Fassung behaelt die volle Groesse; dort ist Platz.

   Die Ausnahme `hyphens: auto`, die hier von 1.7.0 bis 1.8.1 stand, ist mit
   1.8.2 entfallen -- der Grund steht oben bei der allgemeinen Regel. */
.hero:not(.hero--wide) .hero__grid h1 {
  font-size: clamp(2rem, 1.3rem + 2.2vw, 3.25rem);
  line-height: 1.06;
}
/**
 * Die Geschichte dieser Stelle in drei Saetzen, damit sie nicht ein viertes
 * Mal hin und her wandert:
 *
 * Bis 1.7.0 war die Silbentrennung hier abgeschaltet; auf /datenschutz/ gab
 * das "Datenschutzerklaeru" und darunter "ng". 1.7.0 schaltete `hyphens:
 * auto` ein -- was den Fall nur dort behob, wo ein Trennwoerterbuch im
 * Browser liegt, und ueberall sonst gar nichts tat. 1.7.3 brachte die
 * eigentliche Loesung: weiche Trennzeichen im Text (trennhilfe()), die in
 * jedem Browser wirken. Seit 1.8.2 ist `auto` wieder weg, weil es Woerter
 * trennte, die bequem in die naechste Zeile gepasst haetten.
 */
.hero--wide .hero__grid { grid-template-columns: minmax(0, 1fr); }
/* Die Breitenbegrenzung gehoert an die Textelemente, nicht an die Spalte.

   Vorher stand hier `max-width: 34ch` auf .hero__copy. Das "ch" bezieht sich
   auf die Schriftgroesse des Kastens -- also auf die Grundschrift, nicht auf
   die viel groessere Ueberschrift darin. Die Spalte war damit rund 395 px
   breit, und die H1 brach Wort fuer Wort in eigene Zeilen. Betroffen war
   jede Unterseite ohne Visual, also die meisten.

   Jetzt bekommt jedes Element sein eigenes Mass, jeweils in seiner eigenen
   Schriftgroesse gerechnet: die Ueberschrift knapp drei Zeilen, der
   Einleitungssatz die uebliche Lesebreite. */
.hero--wide .hero__copy { max-width: none; }
/* "ch" ist die Breite der Null in der jeweiligen Schriftgroesse -- bei einer
   75-px-Ueberschrift also rund 50 px. 17ch sind damit etwa 850 px und ergeben
   zwei bis drei Zeilen; ein groesserer Wert greift nie, weil der Container
   vorher endet. */
.hero--wide .hero__copy h1 { max-width: 17ch; }
.hero--wide .lead { max-width: 62ch; }
@media (max-width: 980px) {
  .hero__grid { grid-template-columns: minmax(0, 1fr); }
}
.hero__copy > :last-child { margin-bottom: 0; }
.hero h1 { margin-bottom: var(--s-5); }
.hero .lead { margin-bottom: var(--s-7); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.hero__facts {
  display: flex; flex-wrap: wrap; gap: var(--s-5) var(--s-7);
  margin-top: var(--s-8);
  padding-top: var(--s-6);
  border-top: 1px solid var(--border);
}
.hero__fact {
  display: flex; align-items: center; gap: .6rem;
  font-size: .92rem; color: var(--muted-strong);
}
.hero__fact .icon { color: var(--accent); flex: none; }

/* --- Infrastruktur-Diagramm --------------------------------------------- */
/* Die Stationen sind Knoepfe, keine reinen Grafiken: sie sind mit der
   Tabulatortaste erreichbar und geben denselben Text aus wie beim Zeigen.
   Eine Grafik, die nur auf den Mauszeiger reagiert, waere fuer einen Teil
   der Besucher schlicht nicht vorhanden. */
.infra {
  position: relative;
  display: grid;
  gap: .5rem;
  padding: clamp(1.1rem, 2vw, 1.6rem);
  background: linear-gradient(180deg, rgba(var(--surface-rgb),.72), rgba(var(--bg2-rgb),.55));
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  backdrop-filter: blur(16px) saturate(140%);
  box-shadow: var(--shadow-float);
  overflow: hidden;
}
.infra::before {
  content: '';
  position: absolute; inset: 0;
  background-image:
    linear-gradient(rgba(var(--tint-rgb),.03) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--tint-rgb),.03) 1px, transparent 1px);
  background-size: 34px 34px;
  pointer-events: none;
}
.infra__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3); flex-wrap: wrap;
  padding: .3rem .3rem .9rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: .4rem;
}
.infra__title {
  font-family: var(--mono); font-size: .68rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--muted); font-weight: 550;
}
.infra__states { display: flex; flex-wrap: wrap; gap: .4rem; }

.infra__node {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s-4);
  width: 100%;
  padding: .82rem 1rem;
  background: rgba(var(--tint-rgb),.028);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition:
    border-color var(--dur) var(--spring),
    background-color var(--dur) var(--spring),
    transform var(--dur) var(--spring),
    box-shadow var(--dur) var(--spring);
}
.infra__node:hover,
.infra__node:focus-visible {
  border-color: var(--border-accent);
  background: rgba(var(--accent-rgb),.07);
  transform: translateX(4px);
  box-shadow: 0 0 34px -18px rgba(var(--accent-rgb),.9);
}
.infra__node:active { transform: translateX(4px) scale(.99); transition-duration: var(--dur-press); }
.infra__icon {
  display: grid; place-items: center;
  width: 38px; height: 38px; flex: none;
  border-radius: 11px;
  color: var(--accent);
  background: rgba(var(--accent-rgb),.08);
  border: 1px solid rgba(var(--accent-rgb),.18);
}
.infra__label { display: block; font-weight: 590; color: var(--white); font-size: .98rem; letter-spacing: -.008em; }
.infra__sub { display: block; font-size: .8rem; color: var(--muted); line-height: 1.35; margin-top: 1px; }
.infra__arrow { margin-left: auto; color: var(--muted); flex: none; opacity: .5; }
.infra__node:hover .infra__arrow { color: var(--accent); opacity: 1; }

/* Verbindungslinie zwischen zwei Stationen, mit wanderndem Datenpunkt. */
.infra__link {
  position: relative;
  height: 20px;
  margin-inline: 1.55rem;
  width: 1px;
  background: linear-gradient(180deg, rgba(var(--tint-rgb),.16), rgba(var(--tint-rgb),.06));
}
.infra__link::after {
  content: '';
  position: absolute;
  left: 50%; top: 0;
  width: 4px; height: 4px;
  margin-left: -2px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px rgba(var(--accent-rgb),.9);
  animation: flow 3.4s var(--ease-out) infinite;
  animation-delay: var(--delay, 0s);
  opacity: 0;
}
@keyframes flow {
  0%   { transform: translateY(-2px); opacity: 0; }
  18%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { transform: translateY(20px); opacity: 0; }
}

/* --- Server-Rack --------------------------------------------------------- */
/* --- Serverschrank ------------------------------------------------------

   Vorher war das ein Stapel aus acht gleichen Balken mit Punkt und
   Fortschrittsanzeige -- das sah aus wie eine Ladeanzeige. Jetzt eine
   Zeichnung mit drei Flaechen (Front, Seitenwand, Deckel) und
   unterscheidbaren Geraeten. Parallelprojektion statt Fluchtpunkt: Bei
   technischen Zeichnungen ist das die ehrlichere Wahl, weil nichts verzerrt.

   Die Farben kommen aus denselben Tokens wie der Rest, damit der Schrank in
   beiden Themen sitzt. --rack-1/--rack-2 tragen den Korpus.
   ------------------------------------------------------------------------ */

.minirk__floor path { stroke: var(--border); stroke-width: 1; }
.minirk__side path {
  fill: var(--rack-2);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.minirk__top path {
  fill: rgba(var(--tint-rgb),.05);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.minirk__body rect {
  fill: var(--rack-1);
  stroke: var(--border-strong);
  stroke-width: 1.4;
}
.minirk__rail rect { fill: rgba(var(--tint-rgb),.06); stroke: none; }
/* Die Lochung der Schiene: eine gestrichelte Linie ist billiger als 40
   einzelne Loecher und sieht aus derselben Entfernung gleich aus. */
.minirk__rail path {
  stroke: rgba(var(--tint-rgb),.22);
  stroke-width: 2;
  stroke-dasharray: 2 6;
  stroke-linecap: round;
}
.minirk__plinth path { stroke: var(--border-strong); stroke-width: 1.2; }
.minirk__plinth rect { fill: var(--rack-2); stroke: var(--border-strong); stroke-width: 1; }

.minirk__tray path { stroke: var(--border-strong); stroke-width: 1.2; }
.minirk__feed path { stroke: var(--muted); stroke-width: 1.6; opacity: .7; }

/* Geraetefronten. Der Farbverlauf faellt nach unten ab -- Licht kommt von
   oben, und ohne diesen Verlauf sehen die Blenden wie Aufkleber aus. */
.minirk__dev rect:first-child {
  fill: var(--rack-2);
  stroke: rgba(var(--tint-rgb),.14);
  stroke-width: 1;
}
.minirk__dev--patch rect:first-child  { fill: rgba(var(--tint-rgb),.07); }
.minirk__dev--switch rect:first-child { fill: rgba(var(--tint-rgb),.055); }
.minirk__dev--ups rect:first-child    { fill: rgba(var(--tint-rgb),.045); }

.minirk__ports rect {
  fill: var(--bg-deep);
  stroke: rgba(var(--tint-rgb),.16);
  stroke-width: .8;
}
.minirk__bays rect {
  fill: var(--bg-deep);
  stroke: rgba(var(--tint-rgb),.13);
  stroke-width: .8;
}
.minirk__vent path { stroke: rgba(var(--tint-rgb),.13); stroke-width: 2; }
.minirk__blank rect {
  fill: rgba(var(--tint-rgb),.025);
  stroke: rgba(var(--tint-rgb),.08);
  stroke-width: 1;
}
.minirk__blank path { stroke: rgba(var(--tint-rgb),.18); stroke-width: 1.4; }

.minirk__screen {
  fill: rgba(var(--accent-rgb),.10);
  stroke: rgba(var(--accent-rgb),.35);
  stroke-width: 1;
}
.minirk__screen-line {
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* Kabel: drei Straenge, unterschiedlich stark, damit es ein Bund ist und
   nicht drei parallele Linien. */
.minirk__cable path {
  stroke: var(--muted-strong);
  stroke-linecap: round;
  opacity: .55;
}
.minirk__cable path:nth-child(1) { stroke-width: 2.6; }
.minirk__cable path:nth-child(2) { stroke-width: 2;   opacity: .45; }
.minirk__cable path:nth-child(3) { stroke-width: 1.6; opacity: .35; }

/* Lampen. Versetzt und unterschiedlich lang -- Gleichtakt wirkt wie eine
   Animation, Versatz wie Betrieb. */
.minirk__led      { fill: var(--success); }
.minirk__led--net { fill: var(--accent); }
.minirk__link circle { fill: var(--accent); }
.minirk__led, .minirk__link circle {
  animation: minirk-blink 4s ease-in-out infinite;
}
.bl-1 { animation-delay: 0s;    animation-duration: 4s;   }
.bl-2 { animation-delay: -.7s;  animation-duration: 5.2s; }
.bl-3 { animation-delay: -1.4s; animation-duration: 3.4s; }
.bl-4 { animation-delay: -2.1s; animation-duration: 4.6s; }
.bl-5 { animation-delay: -.4s;  animation-duration: 2.2s; }
.bl-6 { animation-delay: -1.8s; animation-duration: 2.8s; }
.bl-7 { animation-delay: -2.6s; animation-duration: 3.1s; }
@keyframes minirk-blink {
  0%, 84%, 100% { opacity: 1; }
  90%           { opacity: .25; }
}

.minirk__tray-top {
  fill: rgba(var(--tint-rgb),.035);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.minirk__note text { fill: var(--accent); font-size: 10px; letter-spacing: .06em; }


.state-list { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: var(--s-5); }

/* --- Leistungskarten ----------------------------------------------------- */
.feature {
  position: relative;
  display: flex; flex-direction: column;
  gap: var(--s-4);
  padding: clamp(1.5rem, 2.6vw, 2.25rem);
  background: linear-gradient(165deg, rgba(var(--tint-rgb),.045), rgba(var(--tint-rgb),.008));
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  backdrop-filter: blur(14px);
  overflow: hidden;
  transition: border-color var(--dur) var(--spring), transform var(--dur) var(--spring), box-shadow var(--dur) var(--spring);
}
.feature::after {
  content: '';
  position: absolute;
  inset: auto -40% -60% auto;
  width: 240px; height: 240px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--accent-rgb),.16), transparent 68%);
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out);
  pointer-events: none;
}
.feature:hover, .feature:focus-within {
  border-color: var(--border-accent);
  transform: translateY(-4px);
  box-shadow: var(--shadow-float);
}
.feature:hover::after, .feature:focus-within::after { opacity: 1; }
.feature__head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.feature__num {
  font-family: var(--mono);
  font-size: .72rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted); font-weight: 550;
}
.feature__num b { color: var(--accent); font-weight: 600; }
.feature__title { margin: 0; }
.feature__text { color: var(--muted); margin: 0; }
.feature__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; }
.feature__list li {
  display: flex; align-items: flex-start; gap: .6rem;
  font-size: .93rem; color: var(--muted-strong);
}
.feature__list .icon { color: var(--accent); flex: none; margin-top: .18em; }

/* --- Ebenen -------------------------------------------------------------- */
.layers { display: grid; gap: .55rem; counter-reset: layer; }
.layer {
  position: relative;
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr);
  gap: var(--s-4);
  align-items: center;
  padding: 1rem 1.25rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: linear-gradient(90deg, rgba(var(--tint-rgb),.045), rgba(var(--tint-rgb),.012));
  /* Jede Ebene ist etwas schmaler als die darueber -- das zeigt "von aussen
     nach innen", ohne es beschriften zu muessen. */
  width: calc(100% - (var(--i, 0) * 1.6%));
  margin-inline: auto;
  transition: border-color var(--dur) var(--spring), background-color var(--dur) var(--spring);
}
.layer:hover { border-color: var(--border-accent); background: rgba(var(--accent-rgb),.05); }
.layer__num {
  counter-increment: layer;
  font-family: var(--mono); font-size: .78rem; letter-spacing: .1em;
  color: var(--accent); font-weight: 550;
}
.layer__num::before { content: counter(layer, decimal-leading-zero); }
.layer__title { font-weight: 590; color: var(--white); margin: 0 0 .15rem; font-size: 1rem; }
.layer__text { margin: 0; font-size: .9rem; color: var(--muted); }
@media (max-width: 620px) {
  .layer { width: 100%; grid-template-columns: 40px minmax(0, 1fr); padding: .9rem 1rem; }
}

/* --- Ablauf -------------------------------------------------------------- */
.timeline { display: grid; gap: 0; counter-reset: step; position: relative; }
.tl-step {
  position: relative;
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr);
  gap: var(--s-5);
  padding-bottom: var(--s-7);
}
.tl-step:last-child { padding-bottom: 0; }
.tl-step__marker { position: relative; display: flex; justify-content: center; }
.tl-step__dot {
  position: relative; z-index: 1;
  display: grid; place-items: center;
  width: 46px; height: 46px;
  border-radius: 50%;
  background: var(--bg-2);
  border: 1px solid var(--border-accent);
  color: var(--accent);
  font-family: var(--mono); font-size: .82rem; font-weight: 600;
  counter-increment: step;
}
.tl-step__dot::before { content: counter(step, decimal-leading-zero); }
/* Die Linie wird beim Scrollen aufgebaut: sie zeigt den Weg, den der Text
   gerade beschreibt. */
.tl-step:not(:last-child) .tl-step__marker::after {
  content: '';
  position: absolute;
  top: 46px; bottom: calc(-1 * var(--s-7));
  width: 1px;
  background: linear-gradient(180deg, var(--border-accent), var(--border));
  transform-origin: top;
  transform: scaleY(var(--progress, 1));
}
.tl-step__title { margin: .5rem 0 .4rem; }
.tl-step__text { color: var(--muted); margin: 0; }
@media (max-width: 620px) {
  .tl-step { grid-template-columns: 54px minmax(0, 1fr); gap: var(--s-4); }
}

/* --- Kurzantwort --------------------------------------------------------- */
.definition {
  position: relative;
  padding: clamp(1.5rem, 3vw, 2.5rem);
  padding-left: clamp(1.75rem, 3.4vw, 3rem);
  border: 1px solid var(--border);
  border-left: 2px solid var(--accent);
  border-radius: 0 var(--r-lg) var(--r-lg) 0;
  background: linear-gradient(90deg, rgba(var(--accent-rgb),.06), rgba(var(--tint-rgb),.014) 40%);
  max-width: 80ch;
}
/* Die Kurzantwort traegt eine h2, war aber kleiner gesetzt als die h3 der
   Karten daneben: 18,4 px gegen 20,8 px auf dem Telefon. Eine Ueberschrift
   der zweiten Ebene, die kleiner aussieht als die der dritten, dreht die
   Rangfolge um. Jetzt liegt sie auf der Hoehe der h3 -- bewusst nicht
   darueber, der Block ist eine Antwort und kein Abschnittstitel. */
.definition__term { margin: 0 0 var(--s-3); font-size: clamp(1.3rem, 1.14rem + .62vw, 1.7rem); }
.definition__answer { margin: 0; color: var(--muted-strong); font-size: 1.05em; line-height: 1.62; }
.definition__note { margin: var(--s-4) 0 0; font-size: .86rem; color: var(--muted); }

/* --- Checkliste ---------------------------------------------------------- */
.checklist { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-4); }
.check-item { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: var(--s-3); align-items: start; }
.check-item__mark {
  display: grid; place-items: center;
  width: 24px; height: 24px; border-radius: 50%;
  background: rgba(var(--success-rgb),.10);
  border: 1px solid rgba(var(--success-rgb),.28);
  color: var(--success);
  margin-top: .12em;
}
.check-item__title { font-weight: 570; color: var(--white); display: block; }
.check-item__text { color: var(--muted); font-size: .93rem; margin: .2rem 0 0; }

/* --- Gegenueberstellung -------------------------------------------------- */
.compare { display: grid; gap: clamp(1rem, 2vw, 1.5rem); }
.compare__col {
  display: flex; flex-direction: column; gap: var(--s-4);
  padding: clamp(1.4rem, 2.4vw, 2rem);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, rgba(var(--tint-rgb),.035), rgba(var(--tint-rgb),.01));
}
.compare__title { margin: 0; }
.compare__summary { color: var(--muted); margin: 0; }
.compare__points { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.compare__points li { display: flex; gap: .6rem; font-size: .93rem; color: var(--muted-strong); }
.compare__points .icon { color: var(--accent); flex: none; margin-top: .18em; }
.compare__note {
  margin-top: auto; padding-top: var(--s-4);
  border-top: 1px solid var(--border);
  font-size: .88rem; color: var(--muted);
}

/* --- Kennzahlen ---------------------------------------------------------- */
.stat { text-align: left; }
.stat__value {
  font-size: clamp(2.2rem, 1.6rem + 2.2vw, 3.4rem);
  line-height: 1; font-weight: 660; letter-spacing: -.035em;
  color: var(--white);
  font-variant-numeric: tabular-nums;
}
.stat__label { display: block; margin-top: .5rem; color: var(--white); font-weight: 560; }
.stat__note { display: block; margin-top: .25rem; font-size: .86rem; color: var(--muted); }

/* Auf dem Telefon zwei Spalten statt vier Bloecke untereinander.
   Vorher standen die vier Kennzahlen linksbuendig in einer Spalte -- 561
   Punkte hoch, rechts daneben nichts. Das liest sich als lose Liste, nicht
   als Zahlenreihe, und man muss dreimal scrollen, um vier Zahlen zu sehen.
   Zwei Spalten mit Trennlinien ergeben ein Feld: halb so hoch, und die
   Zahlen stehen wieder nebeneinander, wo man sie vergleicht.

   Die generelle Regel bricht Raster unter 680 Punkten auf eine Spalte um.
   Hier steht bewusst dagegen -- Kennzahlen sind kurz, sie brauchen die
   Breite nicht. `.grid.stats-grid` ist spezifischer als `.grid` und gewinnt
   unabhaengig von der Reihenfolge im Stylesheet.

   Die Zahl skaliert mit dem Fenster statt fest: "Ø 15 Min." ist der
   breiteste Wert, und bei 320 Punkten Fensterbreite bleiben je Zelle nur
   140 -- bei fester Groesse bricht genau dieser eine Wert um, und dann
   stehen die vier Zellen unterschiedlich hoch. */
@media (max-width: 680px) {
  .grid.stats-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0;
  }
  .stats-grid .stat {
    text-align: center;
    padding: var(--s-4) .5rem;
  }
  .stats-grid .stat:nth-child(2n)   { border-inline-start: 1px solid var(--border); }
  .stats-grid .stat:nth-child(n+3)  { border-top: 1px solid var(--border); }
  .stats-grid .stat__value { font-size: clamp(1.5rem, 7.2vw, 2rem); }
  .stats-grid .stat__label { margin-top: .35rem; font-size: .95rem; }
  .stats-grid .stat__note  { margin-top: .15rem; font-size: .8rem; }
}

/* --- Die versteckte Seite /robi/ ------------------------------------------

   Ein Scherz, aber kein Sonderweg: Platte, Rahmen, Abstaende und Schrift
   kommen aus denselben Tokens wie der Rest. Eine Seite, die wie ein
   Fremdkoerper aussieht, waere kein Osterei, sondern ein Fehler.
   ------------------------------------------------------------------------ */

.robi__buehne {
  display: grid;
  justify-items: center;
  gap: var(--s-3);
  margin-top: var(--s-7);
}
/* Die Figur sitzt in einem Knopf und nicht in einem Bild mit Klickereignis:
   Ein Spiel ist eine Funktion, und eine Funktion, die nur mit der Maus
   geht, gibt es hier nicht. Der Browser bringt Enter und Leertaste mit. */
.robi__knopf {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  border-radius: var(--r-lg);
}
.robi__knopf:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; }
.robi__platte {
  display: grid;
  place-items: center;
  width: clamp(168px, 34vw, 230px);
  aspect-ratio: 1 / 1;
  padding: clamp(16px, 3vw, 26px);
  border-radius: var(--r-lg);
}
.robi__figur { width: 100%; }

/* Ein kurzes Wackeln je Klick. Mehr nicht -- die Figur soll reagieren, nicht
   herumspringen. */
.robi__knopf.is-angestupst .robi__figur { animation: robi-stups 420ms var(--ease-out); }
@keyframes robi-stups {
  0%   { transform: translateY(0) rotate(0); }
  28%  { transform: translateY(-7px) rotate(-3.5deg); }
  58%  { transform: translateY(0) rotate(2.5deg); }
  100% { transform: translateY(0) rotate(0); }
}

.robi__hinweis { margin: 0; color: var(--muted); font-size: .92rem; }
.robi__zaehler {
  margin: 0;
  font-family: var(--mono);
  font-size: .86rem;
  letter-spacing: .08em;
  color: var(--accent);
}

/* --- Die Konsole --------------------------------------------------------- */

/* DIE KONSOLE IST IN BEIDEN THEMEN DUNKEL, aus demselben Grund wie Robis
   Platte: Ein Terminal ist dunkel. Eine helle Konsole im hellen Thema waere
   ein graues Rechteck mit Schrift darin und niemand erkennt, was gemeint
   ist. Deshalb stehen hier feste Werte statt Tokens -- die Tokens kippen mit
   dem Thema, und auf einem festen dunklen Grund kippte dann die Schrift ins
   Unlesbare. Die Werte sind die des dunklen Themas; nachgemessen in beiden
   Themen. */
.robi__konsole {
  margin-top: var(--s-6);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--bot-platte);
  box-shadow: var(--shadow-card);
}
.robi__balken {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .55rem .8rem;
  background: var(--bot-platte-2);
  border-bottom: 1px solid rgba(255,255,255,.1);
}
.robi__punkt {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: rgba(255,255,255,.22);
}
.robi__titel {
  margin-inline-start: .5rem;
  font-family: var(--mono);
  font-size: .78rem;
  letter-spacing: .06em;
  color: #B4C1CB;
}
.robi__schirm {
  display: grid;
  gap: var(--s-4);
  padding: clamp(.9rem, 3vw, 1.6rem);
  background:
    radial-gradient(80% 60% at 50% 0%, rgba(0,217,255,.07), transparent 70%),
    var(--bot-platte);
}
/* Die ASCII-Figur und die Ausgabe teilen sich die Schriftgroesse: Steht die
   Zeichnung in einer anderen Groesse als der Text darunter, liest sich das
   wie zwei Fenster. */
.robi__ascii,
.robi__ausgabe {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(.7rem, 2.6vw, .82rem);
  line-height: 1.5;
  white-space: pre;
}
.robi__ascii { color: #00D9FF; }
.robi__ausgabe { white-space: pre-wrap; }
.robi__zeile { display: block; }
.robi__zeile--befehl  { color: #B4C1CB; }
.robi__zeile--antwort { color: #38E8A5; }
/* Die letzte Zeile ist der leere Prompt; der Cursor dahinter kommt aus
   ::after. So steht er in derselben Zeile wie der Prompt -- als eigenes
   Element waere er umgebrochen, weil die Zeilen Bloecke sind. */
.robi__zeile--prompt { color: #B4C1CB; }
.robi__zeile--prompt::after {
  content: '_';
  color: #00D9FF;
  animation: robi-cursor 1.1s steps(1) infinite;
}
@keyframes robi-cursor { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

.robi__zurueck { margin-top: var(--s-7); }

/* --- Fragen und Antworten ------------------------------------------------ */
/* Auf- und Zuklappen macht <details> selbst. Das funktioniert ohne
   JavaScript, ist fuer Screenreader ohne Zutun richtig ausgezeichnet, und
   der Browser findet damit auch Text in geschlossenen Abschnitten. */
.faq { display: grid; gap: .6rem; }
.faq__item {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: rgba(var(--tint-rgb),.022);
  transition: border-color var(--dur) var(--spring), background-color var(--dur) var(--spring);
}
.faq__item[open] { border-color: var(--border-accent); background: rgba(var(--accent-rgb),.04); }
.faq__item > summary {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s-4);
  padding: 1.1rem 1.35rem;
  cursor: pointer;
  list-style: none;
  font-weight: 570;
  color: var(--white);
  font-size: 1.04rem;
  line-height: 1.4;
  letter-spacing: -.008em;
  border-radius: var(--r-md);
}
.faq__item > summary::-webkit-details-marker { display: none; }
.faq__item > summary:hover { color: var(--accent); }
.faq__sign {
  flex: none; margin-top: .1em; color: var(--accent);
  transition: transform var(--dur) var(--spring);
}
.faq__item[open] .faq__sign { transform: rotate(45deg); }
.faq__answer { padding: 0 1.35rem 1.3rem; color: var(--muted-strong); max-width: 80ch; }
.faq__answer > :first-child { margin-top: 0; }

/* --- Abschluss-CTA ------------------------------------------------------- */
.cta-band { position: relative; overflow: hidden; }
.cta-band__inner {
  position: relative;
  display: grid;
  gap: var(--s-6);
  padding: clamp(2.5rem, 6vw, 5rem) clamp(1.5rem, 5vw, 4.5rem);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xl);
  background:
    radial-gradient(80% 140% at 20% 0%, rgba(var(--accent-rgb),.15), transparent 60%),
    radial-gradient(60% 120% at 90% 100%, rgba(var(--accent2-rgb),.16), transparent 62%),
    linear-gradient(180deg, var(--surface), var(--bg-2));
  overflow: hidden;
}
.cta-band__inner::before {
  content: '';
  position: absolute; inset: 0;
  background-image:
    linear-gradient(rgba(var(--tint-rgb),.025) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--tint-rgb),.025) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(70% 100% at 50% 50%, #000, transparent 78%);
  -webkit-mask-image: radial-gradient(70% 100% at 50% 50%, #000, transparent 78%);
  pointer-events: none;
}
.cta-band h2 { max-width: 22ch; }
.cta-band__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.cta-band__note {
  font-family: var(--mono); font-size: .72rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); margin: 0;
}

/* --- Leistungsband ------------------------------------------------------- */
.trust {
  display: flex;
  flex-wrap: wrap;
  gap: .55rem .5rem;
  align-items: center;
  justify-content: center;
}
.trust__item {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .5rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: rgba(var(--tint-rgb),.028);
  font-family: var(--mono);
  font-size: .72rem; letter-spacing: .13em; text-transform: uppercase;
  color: var(--muted-strong);
  font-weight: 550;
  text-decoration: none;
  white-space: nowrap;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
a.trust__item:hover { border-color: var(--border-accent); color: var(--accent); }
.trust__item::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent); opacity: .65; flex: none;
}
@media (max-width: 720px) {
  /* Waagerecht rollbar statt umgebrochen -- das Band bleibt ein Band. */
  .trust {
    flex-wrap: nowrap; justify-content: flex-start;
    overflow-x: auto; scrollbar-width: none;
    margin-inline: calc(-1 * var(--gutter));
    padding-inline: var(--gutter);
    scroll-snap-type: x proximity;
  }
  .trust::-webkit-scrollbar { display: none; }
  .trust__item { scroll-snap-align: start; }
}

/* --- Verweise und Artikel ------------------------------------------------ */
.linkcard {
  position: relative;
  display: flex; flex-direction: column; gap: .5rem;
  padding: 1.2rem 1.35rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: rgba(var(--tint-rgb),.022);
  text-decoration: none;
  transition: border-color var(--dur) var(--spring), transform var(--dur) var(--spring), background-color var(--dur) var(--spring);
}
.linkcard:hover { border-color: var(--border-accent); background: rgba(var(--accent-rgb),.05); transform: translateY(-2px); }
.linkcard__title { color: var(--white); font-weight: 570; display: flex; align-items: center; gap: .5rem; justify-content: space-between; }
.linkcard__title .icon { color: var(--accent); flex: none; transition: transform var(--dur) var(--spring); }
.linkcard:hover .linkcard__title .icon { transform: translateX(3px); }
.linkcard__text { color: var(--muted); font-size: .9rem; margin: 0; }
.linkcard__meta { font-family: var(--mono); font-size: .68rem; letter-spacing: .14em; text-transform: uppercase; color: var(--accent); }

/* --- Logos --------------------------------------------------------------- */
/* ---------------------------------------------------------------- Logowand

   Gleiche Kacheln, gleiche Groesse.

   Bis 1.7.1 stand hier `filter: grayscale(1) brightness(1.8); opacity: .55`
   -- Logos grau, erst beim Zeigen farbig. Das ist eine uebliche Behandlung
   fuer KUNDENlogos, wo die Wand als Flaeche wirken soll. Fuer HERSTELLER-
   logos ist sie falsch: Wer ein Partnerlogo zeigt, zeigt eine Marke, und
   Marken haben ihre Farbe. Grau gefiltert sieht Lenovo-Rot aus wie
   HPE-Gruen.

   Die Kachel ist in BEIDEN Farbschemata weiss. Das ist eine Entscheidung,
   keine Nachlaessigkeit: Von den gelieferten Logos traegt keines einen
   Grund, der hell und dunkel zugleich funktioniert -- Fortinet ist weiss,
   HPE und MikroTik sind schwarz, drei weitere bringen ihren eigenen Kasten
   mit. Auf Weiss stimmen sechs von sieben. Eine Kachel, die im dunklen Thema
   mitwandert, haette zwei davon verschluckt. */
.logos {
  display: grid;
  gap: var(--s-4);
  align-items: stretch;
  list-style: none;
  margin: 0;
  padding: 0;
}
.logos__kachel { display: block; }
.logos__flaeche {
  display: grid;
  place-items: center;
  /* Festes Verhaeltnis: Ohne es haengt die Kachelhoehe am hoechsten Logo
     der Reihe, und die Reihen darunter werden ungleich. */
  aspect-ratio: 20 / 9;
  padding: var(--s-4);
  border-radius: var(--r-md);
  background: #FFFFFF;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-card);
  transition: transform var(--dur) var(--spring), box-shadow var(--dur) var(--spring);
}
.logos__flaeche > picture,
.logos__flaeche > img,
.logos__flaeche picture img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
a.logos__flaeche:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-float);
}
@media (prefers-reduced-motion: reduce) {
  a.logos__flaeche:hover { transform: none; }
}
.placeholder {
  display: grid; place-items: center;
  padding: var(--s-8);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-lg);
  color: var(--muted);
  font-family: var(--mono);
  font-size: .8rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-align: center;
}

/* --- Rechtstexte --------------------------------------------------------- */
.legal { max-width: 76ch; }
.legal__item + .legal__item { margin-top: var(--s-8); }
.legal__item h2 { font-size: clamp(1.25rem, 1.1rem + .6vw, 1.6rem); }

/* ---------------------------------------------------------- 09 Formulare */

.form { display: grid; gap: var(--s-5); }
.form__row { display: grid; gap: var(--s-5); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 680px) { .form__row { grid-template-columns: minmax(0, 1fr); } }

.field { display: grid; gap: .45rem; }
.field > label, .field__legend {
  font-size: .88rem;
  font-weight: 560;
  color: var(--white);
  letter-spacing: .004em;
}
.field__req { color: var(--accent); }

.input, .textarea, .select {
  width: 100%;
  padding: .78rem .95rem;
  min-height: 48px;
  font: inherit;
  font-size: 1rem;
  color: var(--white);
  background: rgba(var(--tint-rgb),.035);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
  appearance: none;
}
.textarea { min-height: 148px; resize: vertical; line-height: 1.6; }
.select {
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 20px) 55%, calc(100% - 14px) 55%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2.6rem;
}
.select option { background: var(--surface); color: var(--white); }
.input::placeholder, .textarea::placeholder { color: rgba(var(--muted-rgb),.7); }
.input:hover, .textarea:hover, .select:hover { border-color: rgba(var(--tint-rgb),.26); }
.input:focus, .textarea:focus, .select:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: transparent;
  background: rgba(var(--tint-rgb),.05);
}
.input[aria-invalid='true'], .textarea[aria-invalid='true'] {
  border-color: var(--danger);
  background: rgba(var(--danger-rgb),.05);
}

.field-help { font-size: .82rem; color: var(--muted); }
.field-error {
  display: flex; align-items: flex-start; gap: .45rem;
  font-size: .84rem; color: var(--danger);
}
.field-error .icon { flex: none; margin-top: .12em; }

.choices { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: .5rem; }
.choice {
  display: flex; align-items: center; gap: .65rem;
  padding: .7rem .9rem;
  min-height: 48px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: rgba(var(--tint-rgb),.022);
  cursor: pointer;
  font-size: .93rem;
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.choice:hover { border-color: var(--border-accent); }
.choice:has(input:checked) { border-color: var(--border-accent); background: rgba(var(--accent-rgb),.08); color: var(--white); }
.choice input { accent-color: var(--accent); width: 18px; height: 18px; flex: none; }

.consent { display: flex; gap: .75rem; align-items: flex-start; font-size: .9rem; color: var(--muted-strong); }
.consent input { accent-color: var(--accent); width: 20px; height: 20px; flex: none; margin-top: .15em; }
.consent a { color: var(--accent); }

/* Honigtopf. Absichtlich nicht display:none -- manche Automaten fuellen
   ausgeblendete Felder gerade deshalb nicht aus. Fuer Screenreader ist es
   ueber aria-hidden und tabindex ausgenommen. */
.hp {
  position: absolute !important;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.notice {
  display: flex; gap: var(--s-3);
  padding: 1rem 1.15rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--muted);
  border-radius: var(--r-sm);
  background: rgba(var(--tint-rgb),.03);
  font-size: .94rem;
}
.notice .icon { flex: none; margin-top: .15em; }
.notice--ok     { border-left-color: var(--success); background: rgba(var(--success-rgb),.06); }
.notice--ok .icon { color: var(--success); }
.notice--error  { border-left-color: var(--danger); background: rgba(var(--danger-rgb),.06); }
.notice--error .icon { color: var(--danger); }
.notice--info   { border-left-color: var(--accent); background: rgba(var(--accent-rgb),.05); }
.notice--info .icon { color: var(--accent); }
.notice__title { font-weight: 600; color: var(--white); display: block; margin-bottom: .2rem; }

/* Kontaktangaben */
.contact-info { display: grid; gap: var(--s-5); }
.contact-item { display: grid; grid-template-columns: 42px minmax(0, 1fr); gap: var(--s-4); align-items: start; }
.contact-item__icon {
  display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: 12px;
  background: rgba(var(--accent-rgb),.08);
  border: 1px solid rgba(var(--accent-rgb),.18);
  color: var(--accent);
}
.contact-item__label {
  font-family: var(--mono); font-size: .68rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); display: block; margin-bottom: .2rem;
}
.contact-item__value { color: var(--white); font-weight: 540; font-style: normal; }
.contact-item__value a { color: var(--white); text-decoration: none; }
.contact-item__value a:hover { color: var(--accent); }
/* Eine Nebenangabe unter dem Hauptwert -- Fax unter der Telefonnummer, ein
   Hinweis unter einem Link. Sie steht in einer eigenen Zeile, weil sie sonst
   an das Ende der Nummer rutscht und wie ein Teil davon gelesen wird. */
.contact-item__sub {
  display: block;
  color: var(--muted);
  font-size: .86rem;
}
/* In der schmalen Fusszeilenspalte bleibt sonst gern ein einzelnes Wort in
   der letzten Zeile stehen. */
.contact-item__value { text-wrap: pretty; }
/* Der Verweis nach draussen traegt sein Symbol in derselben Zeile: umbricht
   es allein, steht ein Pfeil ohne Bezug unter dem Text. */
.contact-item__ext { display: inline-flex; align-items: center; gap: .4em; }
.contact-item__ext .icon { flex: none; opacity: .6; }

/* ------------------------------------------------- 09b Lichtwellenleiter

   Vier Zeichnungen zum Thema Glasfaser, alle als SVG mit Klassen. Kein
   Style-Attribut (die CSP laesst keines zu), keine Bilddatei, keine
   Bibliothek -- die gesamte Bewegung steht hier.

   Zwei Regeln gelten fuer alle vier:
     - Die Zeichnung muss ohne Bewegung vollstaendig lesbar sein. Wer weniger
       Bewegung eingestellt hat, sieht ein ruhiges Diagramm und verpasst
       nichts.
     - Bewegt wird nur, was sich in Wirklichkeit auch bewegt: Licht, das
       durch eine Faser laeuft. Die Faser selbst zappelt nicht.
   ------------------------------------------------------------------------ */

.fiber { margin: 0; display: grid; gap: var(--s-4); }
.fiber__stage {
  position: relative;
  padding: clamp(1rem, 2.4vw, 1.75rem);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xl);
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(var(--accent-rgb),.10), transparent 62%),
    linear-gradient(180deg, var(--surface), var(--bg-2));
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
/* Ein feines technisches Raster im Hintergrund. Es liegt unter der Zeichnung
   und gibt ihr den Charakter einer Messaufnahme statt einer Illustration. */
.fiber__stage::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(var(--tint-rgb),.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--tint-rgb),.05) 1px, transparent 1px);
  background-size: 32px 32px;
  mask-image: radial-gradient(80% 80% at 50% 50%, #000, transparent 82%);
  -webkit-mask-image: radial-gradient(80% 80% at 50% 50%, #000, transparent 82%);
  pointer-events: none;
}
.fiber__stage > svg { position: relative; display: block; width: 100%; height: auto; }
.fiber__caption {
  color: var(--muted);
  font-size: .88rem;
  line-height: 1.5;
  max-width: 56ch;
}

/* Beschriftungsliste neben der Zeichnung */
.fiber-legend { list-style: none; margin: var(--s-6) 0 0; padding: 0; display: grid; gap: var(--s-3); }
.fiber-legend__item { display: flex; align-items: baseline; gap: .7rem; }
/* Bezeichnung und Erklaerung in einer Zeile. Vorher stand jede unter der
   anderen (display: block), und fuenf Eintraege wurden zu zehn Zeilen mit
   vollem Abstand dazwischen -- das sah aus wie eine kaputte Liste, nicht wie
   eine Legende. */
.fiber-legend { gap: var(--s-2); }
.fiber-legend__item strong { font-weight: 600; color: var(--white); }
.fiber-legend__item .muted { font-size: .9rem; }
.fiber-legend__item .muted::before { content: '·'; margin: 0 .35em 0 .15em; }
.fiber-legend__dot {
  width: 10px; height: 10px; flex: none;
  border-radius: 50%;
  transform: translateY(1px);
  box-shadow: 0 0 0 3px rgba(var(--tint-rgb),.05);
}
.fiber-legend__dot--0 { background: #2E8BFF; }
.fiber-legend__dot--1 { background: #FF8A3D; }
.fiber-legend__dot--2 { background: #3BD37A; }
.fiber-legend__dot--3 { background: #A9713F; }
.fiber-legend__dot--4 { background: #9AA7B4; }

/* ------------------------------------------------------- Faserbuendel */

.fib { --fib-dur: 3.2s; }
.fib__sleeve   { stroke: var(--border-strong); stroke-width: 1.5; fill: rgba(var(--tint-rgb),.04); }
.fib__sleeve path { stroke: var(--border); }
.fib__ends circle { fill: var(--accent); opacity: .5; }

.fib__line  { stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; opacity: .5; }
.fib__glow  { stroke: currentColor; stroke-width: 8;   stroke-linecap: round; opacity: .12; filter: blur(3px); }

/* Der Lichtimpuls: ein kurzes Stueck Linie, das an der Faser entlangwandert.
   stroke-dasharray macht daraus eine Luecke mit einem sichtbaren Abschnitt,
   stroke-dashoffset schiebt diesen Abschnitt durch die ganze Laenge. Das ist
   billiger als ein bewegtes Element -- der Browser laesst es auf der Grafik-
   karte laufen und muss nichts neu berechnen. */
.fib__pulse {
  stroke: var(--white);
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-dasharray: 26 1200;
  stroke-dashoffset: 1226;
  filter: drop-shadow(0 0 6px currentColor);
  animation: fib-run var(--fib-dur) linear infinite;
}
@keyframes fib-run {
  from { stroke-dashoffset: 1226; }
  to   { stroke-dashoffset: -60; }
}

/* Die Aderkennung nach EN 50377, erste sechs Farben. Jede Faser bekommt eine
   eigene Laufzeit und einen eigenen Versatz -- gleichzeitig laufende Impulse
   saehen nach Dekoration aus, nicht nach Datenverkehr. */
.fib--0 { color: #2E8BFF; }
.fib--1 { color: #FF8A3D; }
.fib--2 { color: #3BD37A; }
.fib--3 { color: #A9713F; }
.fib--4 { color: #9AA7B4; }
.fib--5 { color: #E7EEF5; }
.fib--0 .fib__pulse { animation-duration: 3.1s; animation-delay: 0s;    }
.fib--1 .fib__pulse { animation-duration: 3.9s; animation-delay: -.7s;  }
.fib--2 .fib__pulse { animation-duration: 2.7s; animation-delay: -1.5s; }
.fib--3 .fib__pulse { animation-duration: 4.4s; animation-delay: -2.2s; }
.fib--4 .fib__pulse { animation-duration: 3.5s; animation-delay: -.3s;  }
.fib--5 .fib__pulse { animation-duration: 4.9s; animation-delay: -3.1s; }

/* ---------------------------------------------------------- Querschnitt */

.fibx__ring { fill: none; stroke-width: 1.5; }
.fibx__ring--buffer  { stroke: var(--border-strong); fill: rgba(var(--tint-rgb),.025); }
.fibx__ring--coating { stroke: rgba(var(--accent2-rgb),.45); fill: rgba(var(--accent2-rgb),.06); }
.fibx__ring--clad    { stroke: rgba(var(--accent-rgb),.5);  fill: rgba(var(--accent-rgb),.07); }

.fibx__core {
  fill: var(--white);
  filter: drop-shadow(0 0 10px rgba(var(--accent-rgb),.9));
  animation: fibx-breathe 3.4s var(--ease-out) infinite;
}
.fibx__core-halo {
  fill: rgba(var(--accent-rgb),.30);
  transform-origin: 210px 210px;
  animation: fibx-halo 3.4s var(--ease-out) infinite;
}
@keyframes fibx-breathe {
  0%, 100% { opacity: .82; }
  50%      { opacity: 1; }
}
@keyframes fibx-halo {
  0%, 100% { transform: scale(.86); opacity: .5; }
  50%      { transform: scale(1.12); opacity: .18; }
}

/* Der umlaufende Bogen. Ein Kreis mit Strichmuster, der sich dreht -- so
   entsteht der Eindruck, dass der Schnitt durch etwas Laufendes geht. */
.fibx__sweep {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-dasharray: 40 538;
  transform-origin: 210px 210px;
  opacity: .9;
  animation: fibx-sweep 4.2s linear infinite;
}
@keyframes fibx-sweep {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.fibx__lead path { stroke: var(--border-strong); stroke-width: 1; fill: none; }
.fibx__label text {
  fill: var(--muted-strong);
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: .04em;
}
.fibx__label--end { text-anchor: end; }

/* ------------------------------------------------------------- Spleiss */

.fibs { --fibs-dur: 7s; }
.fibs__holder rect {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.fibs__holder path { stroke: var(--border); stroke-width: 1.2; }
.fibs__electrode path {
  fill: rgba(var(--muted-rgb),.55);
  stroke: var(--border-strong);
  stroke-width: 1;
}
.fibs__coating { stroke: rgba(var(--tint-rgb),.20); stroke-width: 18; stroke-linecap: round; }
.fibs__clad    { stroke: rgba(var(--accent-rgb),.45); stroke-width: 11; stroke-linecap: round; }
.fibs__core    { stroke: var(--white); stroke-width: 2.5; stroke-linecap: round; opacity: .6; }

/* Die Enden fahren zusammen. Nur zwei Gruppen bewegen sich, und nur in einer
   Achse -- alles andere bleibt, wo es ist. */
.fibs__fiber--left  { animation: fibs-left  var(--fibs-dur) var(--ease-out) infinite; }
.fibs__fiber--right { animation: fibs-right var(--fibs-dur) var(--ease-out) infinite; }
@keyframes fibs-left {
  0%, 12%   { transform: translateX(-34px); }
  30%, 100% { transform: translateX(0); }
}
@keyframes fibs-right {
  0%, 12%   { transform: translateX(34px); }
  30%, 100% { transform: translateX(0); }
}

/* Der Lichtbogen zuendet erst, wenn die Enden beieinander sind. */
.fibs__arc { opacity: 0; animation: fibs-arc var(--fibs-dur) linear infinite; }
.fibs__arc-core { fill: var(--white); }
.fibs__arc-halo { fill: rgba(var(--warn-rgb),.35); }
.fibs__arc-bolt { stroke: var(--warn); stroke-width: 2; fill: none; stroke-linecap: round; }
@keyframes fibs-arc {
  0%, 30%       { opacity: 0; }
  34%           { opacity: 1; }
  38%           { opacity: .35; }
  42%           { opacity: 1; }
  50%           { opacity: .9; }
  58%, 100%     { opacity: 0; }
}

/* Danach laeuft Licht durch die verschmolzene Stelle -- die Probe, dass der
   Spleiss haelt. */
.fibs__through {
  stroke: var(--accent);
  stroke-width: 4;
  stroke-linecap: round;
  stroke-dasharray: 34 700;
  stroke-dashoffset: 734;
  opacity: 0;
  filter: drop-shadow(0 0 8px var(--accent));
  animation: fibs-through var(--fibs-dur) linear infinite;
}
@keyframes fibs-through {
  0%, 60%   { stroke-dashoffset: 734; opacity: 0; }
  62%       { opacity: 1; }
  92%       { stroke-dashoffset: -40; opacity: 1; }
  94%, 100% { opacity: 0; }
}

.fibs__scale path { stroke: var(--border); stroke-width: 1; }
.fibs__label text {
  fill: var(--muted);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .05em;
}
.fibs__label--mid { text-anchor: middle; fill: var(--accent); }
.fibs__label--end { text-anchor: end; }
.fibs__label--small { font-size: 11px; fill: var(--muted); letter-spacing: .03em; }

/* -------------------------------------------------------------- OTDR */

.fibo__grid path { stroke: var(--border); stroke-width: 1; fill: none; }
.fibo__grid path:first-child { stroke: var(--border-strong); }

/* Die Kurve zeichnet sich einmal auf. Die Laenge ist grosszuegig geschaetzt;
   ein zu grosser Wert schadet nicht, er verlaengert nur den Anlauf. */
.fibo__trace {
  stroke: var(--accent);
  stroke-width: 2.4;
  stroke-linejoin: round;
  stroke-linecap: round;
  fill: none;
  filter: drop-shadow(0 0 6px rgba(var(--accent-rgb),.55));
  stroke-dasharray: 1400;
  stroke-dashoffset: 1400;
  animation: fibo-draw 2.8s var(--ease-out) .2s forwards;
}
@keyframes fibo-draw { to { stroke-dashoffset: 0; } }

.fibo__event circle {
  fill: var(--bg);
  stroke: var(--accent);
  stroke-width: 2;
  opacity: 0;
  animation: fibo-pop .5s var(--spring-soft) forwards;
}
.fibo__event--1 circle { animation-delay: .7s; }
.fibo__event--2 circle { animation-delay: 1.6s; }
.fibo__event--3 circle { animation-delay: 2.1s; }
.fibo__event--4 circle { animation-delay: 2.7s; }
@keyframes fibo-pop { from { opacity: 0; } to { opacity: 1; } }

.fibo__cursor {
  stroke: var(--white);
  stroke-width: 1;
  stroke-dasharray: 4 5;
  opacity: 0;
  animation: fibo-scan 6s var(--ease-out) 3.2s infinite;
}
@keyframes fibo-scan {
  0%        { transform: translateX(0);     opacity: 0; }
  8%        { opacity: .55; }
  70%       { transform: translateX(414px); opacity: .55; }
  80%, 100% { transform: translateX(414px); opacity: 0; }
}

.fibo__label text {
  fill: var(--muted-strong);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .05em;
}
.fibo__label--end  { text-anchor: end; }
.fibo__axis        { fill: var(--muted); font-size: 11px; }

/* Ohne Bewegung bleibt jede Zeichnung in ihrem Endzustand stehen: die
   Impulse ruhen sichtbar auf der Faser, der Spleiss ist geschlossen, die
   Messkurve ist fertig gezeichnet. Nichts fehlt, nichts blinkt. */
@media (prefers-reduced-motion: reduce) {
  /* Der Impuls bleibt sichtbar stehen, etwa in der Mitte der Faser. Ein
     negativer Versatz schiebt das sichtbare Stueck den Pfad entlang nach
     vorn -- genau dorthin, wo die laufende Fassung es unterwegs zeigt. */
  .fib__pulse { animation: none; opacity: .95; }
  /* Jede Faser haelt ihren Impuls an einer anderen Stelle an -- sonst stehen
     alle sechs uebereinander in der Mitte und sehen aus wie ein Fehler.
     Ein negativer Versatz schiebt das sichtbare Stueck den Pfad entlang. */
  .fib--0 .fib__pulse { stroke-dashoffset: -120; }
  .fib--1 .fib__pulse { stroke-dashoffset: -380; }
  .fib--2 .fib__pulse { stroke-dashoffset: -210; }
  .fib--3 .fib__pulse { stroke-dashoffset: -460; }
  .fib--4 .fib__pulse { stroke-dashoffset: -300; }
  .fib--5 .fib__pulse { stroke-dashoffset: -160; }
  .fibx__core, .fibx__core-halo, .fibx__sweep,
  .fibs__fiber--left, .fibs__fiber--right, .fibs__arc, .fibs__through,
  .fibo__cursor {
    animation: none;
  }
  .fibx__core-halo { opacity: .35; }
  .fibs__arc { opacity: 0; }
  .fibs__through { opacity: .9; stroke-dashoffset: 0; stroke-dasharray: none; }
  .fibo__trace { animation: none; stroke-dashoffset: 0; }
  .fibo__event circle { animation: none; opacity: 1; }
}

/* ------------------------------------------------- 09c Kleine Zeichnungen

   Sechs Bilder fuer Themen, bei denen eine Zeichnung mehr sagt als eine
   Aufzaehlung: Netztopologie, 3-2-1-Regel, Messkurve, Funkzellen, die
   Firewall-Entscheidung (abgewiesen gegen durchgelassen) und ein Regelwerk,
   das von oben nach unten geprueft wird. Dieselbe
   Bauart wie die LWL-Grafiken -- SVG mit Klassen, kein Style-Attribut, keine
   Bibliothek, Bewegung nur dort, wo sich in Wirklichkeit auch etwas bewegt.

   Die Huelle ist absichtlich fuer alle dieselbe, damit sie im Layout wie ein
   Satz und nicht wie Einzelstuecke wirken.
   ------------------------------------------------------------------------ */

.mini { margin: 0; display: grid; gap: var(--s-3); }
.mini__stage {
  position: relative;
  padding: clamp(.75rem, 1.6vw, 1.25rem);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(var(--accent-rgb),.09), transparent 62%),
    linear-gradient(180deg, var(--surface), var(--bg-2));
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
.mini__stage::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(var(--tint-rgb),.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--tint-rgb),.045) 1px, transparent 1px);
  background-size: 28px 28px;
  mask-image: radial-gradient(80% 80% at 50% 50%, #000, transparent 84%);
  -webkit-mask-image: radial-gradient(80% 80% at 50% 50%, #000, transparent 84%);
  pointer-events: none;
}
.mini__stage > svg { position: relative; display: block; width: 100%; height: auto; }

/* Auf Telefonen bis an den Fensterrand. Die Zeichnung ist das Breiteste auf
   der Seite und verliert am meisten, wenn sie sich die Rinne teilt. Der
   negative Aussenabstand hebt die Rinne des Containers auf; die Ecken fallen
   weg, weil eine abgerundete Ecke an der Fensterkante keine Ecke mehr ist. */
@media (max-width: 700px) {
  .mini__stage,
  .fiber__stage {
    margin-inline: calc(-1 * var(--gutter));
    border-radius: 0;
    border-inline: 0;
  }
  /* Die Glasfaser-Buehne polstert innen mehr als die kleine (bis 1,75rem
     gegen 1,25rem). An der Fensterkante ist das verschenkte Breite --
     gemessen waren ihre drei Zeichnungen mit 246 Punkten die schmalsten der
     ganzen Website, Massstab 0,44. */
  .fiber__stage { padding-inline: .75rem; }
}
.mini__caption {
  color: var(--muted);
  font-size: .84rem;
  line-height: 1.5;
  max-width: 52ch;
}
/* Beschriftungen in allen Zeichnungen gleich.

   UNTERGRENZE IST 9 -- und was daraus auf dem Telefon wird, stand hier
   bisher geschaetzt und falsch. Nachgemessen auf 320 Punkten, alle 45
   Zeichnungen:

     viewBox-Breiten        420 bis 560 Einheiten
     gerendert              254 Punkte
     Massstab               0,44 bis 0,60   (nicht "etwa zwei Drittel")
     kleinster Text         4,8 Punkte      (nicht "rund 6")

   47 von 47 Zeichnungen mit Text liegen unter 9 Punkten am Schirm, ein
   Drittel unter 6. Kein Text ragt aus seiner Zeichnung -- die Geometrie ist
   in Ordnung, es ist reine Groesse.

   DER SCHLUSS BLEIBT TROTZDEM DERSELBE: Auf dem Telefon ist die Zeichnung
   ein Bild, und die Bildunterschrift traegt den Inhalt. Das ist geprueft --
   alle 45 Zeichnungen haben eine, keine unter 60 Zeichen. Die Beschriftungen
   groesser zu setzen hiesse, 45 Zeichnungen neu zu bauen: Bei dem Massstab
   muesste die kleinste Klasse von 9 auf 17 Einheiten, und dann stoesst sie
   ueberall an die Formen, in denen sie steht.

   Was ohne Umbau zu holen war, ist die Breite -- siehe .mini__stage weiter
   unten: Auf Telefonen laeuft die Buehne bis an den Fensterrand statt in der
   Spalte zu bleiben. Nachgemessen bei 320 Punkten Fensterbreite:

                       vorher      nachher
     Buehne            254 px      296 px      +17 %
     Massstab          0,53        0,62
     kleinster Text    4,8 px      5,6 px
     Text ragt heraus  0           0

   Bei 390 Punkten liegt der Massstab danach bei 0,76 -- die 12er
   Beschriftungen landen dort bei 9,1 Punkten und sind lesbar.

   Ehrlich bleibt: Bei 320 Punkten sind 5,6 Punkte immer noch Textur, nicht
   Text. Das ist die Grenze dessen, was ohne Neubau der Zeichnungen geht. */
.mini text {
  fill: var(--muted-strong);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .04em;
  text-anchor: middle;
}

/* --- Netztopologie ------------------------------------------------------- */

.mininet__wire path { stroke: var(--border-strong); stroke-width: 1.2; }
.mininet__hub rect {
  fill: rgba(var(--accent-rgb),.10);
  stroke: var(--border-accent);
  stroke-width: 1.2;
}
.mininet__hub path { stroke: var(--accent); stroke-width: 2.4; stroke-linecap: round; }
.mininet__node rect {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.mininet__hublabel text { fill: var(--accent); font-size: 11px; }

/* Datenpunkte auf den Strecken. stroke-dasharray statt bewegter Elemente:
   der Browser laesst das auf der Grafikkarte laufen. */
.mininet__flow path {
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 4 260;
  stroke-dashoffset: 264;
  filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.7));
  animation: mini-flow 2.8s linear infinite;
}
@keyframes mini-flow {
  from { stroke-dashoffset: 264; }
  to   { stroke-dashoffset: -8; }
}

/* --- 3-2-1-Regel --------------------------------------------------------- */

.minibk__wire path { stroke: var(--border-strong); stroke-width: 1.2; }
.minibk__source rect {
  fill: rgba(var(--accent-rgb),.10);
  stroke: var(--border-accent);
  stroke-width: 1.2;
}
.minibk__source path { stroke: var(--accent); stroke-width: 2; stroke-linecap: round; }
.minibk__copy rect {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.minibk__offsite rect {
  fill: rgba(var(--success-rgb),.08);
  stroke: rgba(var(--success-rgb),.45);
  stroke-width: 1.2;
}
/* Die Grundstueckgrenze: gestrichelt, weil sie keine Leitung ist. */
.minibk__offsite path {
  stroke: var(--border-strong);
  stroke-width: 1;
  stroke-dasharray: 3 5;
}
.mini text.minibk__label--small { font-size: 10px; fill: var(--muted); }
.minibk__rule text {
  fill: var(--accent);
  font-size: 11px;
  letter-spacing: .1em;
}
.minibk__flow path {
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 4 300;
  stroke-dashoffset: 304;
  filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.7));
  animation: mini-flow-lang 3.4s linear infinite;
}
@keyframes mini-flow-lang {
  from { stroke-dashoffset: 304; }
  to   { stroke-dashoffset: -8; }
}

/* --- Messkurve ----------------------------------------------------------- */

.minimo__grid path { stroke: var(--border); stroke-width: 1; }
.minimo__grid path:first-child { stroke: var(--border-strong); }
.minimo__limit {
  stroke: var(--warn);
  stroke-width: 1.4;
  stroke-dasharray: 6 5;
  opacity: .8;
}
/* text.… statt .… -- sonst gewinnt `.mini text` mit seiner hoeheren
   Spezifitaet und die Beschriftung wird mittig gesetzt statt rechts
   angeschlagen. Sie ragte dadurch aus dem Bild ("Grenzwe"). */
.mini text.minimo__limit-label { fill: var(--warn); font-size: 10px; text-anchor: end; }
.minimo__trace {
  stroke: var(--accent);
  stroke-width: 2.4;
  stroke-linejoin: round;
  stroke-linecap: round;
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.5));
  stroke-dasharray: 1200;
  stroke-dashoffset: 1200;
  animation: minimo-draw 2.4s var(--ease-out) .2s forwards;
}
@keyframes minimo-draw { to { stroke-dashoffset: 0; } }

.minimo__event circle { fill: var(--bg); stroke-width: 2; opacity: 0; animation: minimo-pop .4s var(--spring-soft) forwards; }
.minimo__event--ok circle   { stroke: var(--accent); animation-delay: 1.6s; }
.minimo__event--warn circle { stroke: var(--warn);   animation-delay: 2.2s; }
@keyframes minimo-pop { to { opacity: 1; } }

.minimo__cursor {
  stroke: var(--white);
  stroke-width: 1;
  stroke-dasharray: 4 5;
  opacity: 0;
  animation: minimo-scan 7s var(--ease-out) 2.8s infinite;
}
@keyframes minimo-scan {
  0%        { transform: translateX(0);     opacity: 0; }
  8%        { opacity: .5; }
  72%       { transform: translateX(384px); opacity: .5; }
  82%, 100% { transform: translateX(384px); opacity: 0; }
}
.mini text.minimo__label--y    { text-anchor: start; fill: var(--muted); font-size: 10px; }
.mini text.minimo__label--warn { fill: var(--warn); font-size: 10px; }

/* --- Funkzellen ---------------------------------------------------------- */

.miniwl__plan rect,
.miniwl__plan path { stroke: var(--border-strong); stroke-width: 1.2; fill: none; }
.miniwl__plan path { stroke: var(--border); }
.miniwl__ap rect {
  fill: rgba(var(--accent-rgb),.12);
  stroke: var(--border-accent);
  stroke-width: 1.2;
}
/* Die Ringe wachsen nach aussen und verlieren dabei an Kraft -- so wie eine
   Funkzelle es tatsaechlich tut. */
.miniwl__cell circle {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.2;
  transform-origin: center;
  animation: miniwl-pulse 4.5s var(--ease-out) infinite;
}
.miniwl__cell circle:nth-child(1) { animation-delay: 0s;    }
.miniwl__cell circle:nth-child(2) { animation-delay: -1.5s; }
.miniwl__cell circle:nth-child(3) { animation-delay: -3s;   }
.miniwl__cell--b circle { animation-duration: 5.2s; }
@keyframes miniwl-pulse {
  0%   { opacity: .55; }
  70%  { opacity: .10; }
  100% { opacity: .55; }
}
.mini text.miniwl__label--small { font-size: 10px; fill: var(--muted); }

/* --- Firewall: abgewiesen und durchgelassen ------------------------------ */

/* Ein Schild allein zeigt nur, dass es Schutz gibt. Hier tragen die Farben
   die Aussage: rot endet, gruen kommt durch. Deshalb steht neben jeder Farbe
   auch ein Wort -- Farbe allein waere fuer Farbsehschwaechen keine
   Information (WCAG 1.4.1).

   Die Farbverlaeufe liegen als <linearGradient>/<radialGradient> im Markup;
   gefaerbt werden sie hier ueber die Stopp-Klassen. Das ist der einzige Weg,
   der ohne style-Attribut auskommt und trotzdem beide Themen bedient: die
   Stopps rechnen mit den -rgb-Token, und die kippen mit dem Thema mit. */

.minifw__s-a  { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .32; }
.minifw__s-b  { stop-color: rgb(var(--accent-rgb));  stop-opacity: .15; }
.minifw__s-c  { stop-color: rgb(var(--accent-rgb));  stop-opacity: .05; }
.minifw__k-a  { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minifw__k-b  { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .24; }
.minifw__au-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .15; }
.minifw__au-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .07; }
.minifw__au-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minifw__bl-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minifw__bl-b { stop-color: rgb(var(--accent-rgb));  stop-opacity: .28; }
.minifw__bl-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minifw__fk-a { stop-color: rgb(var(--danger-rgb));  stop-opacity: .8;  }
.minifw__fk-b { stop-color: rgb(var(--danger-rgb));  stop-opacity: 0;   }
.minifw__kp-a { stop-color: rgb(var(--success-rgb)); stop-opacity: .95; }
.minifw__kp-b { stop-color: rgb(var(--success-rgb)); stop-opacity: 0;   }

.minifw__outside path { stroke: var(--border-strong); stroke-width: 1.2; }
.minifw__inside rect {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.minifw__inside path { stroke: var(--muted); stroke-width: 1.6; stroke-linecap: round; }

/* Die drei Wege liegen ruhig da; die Bewegung laeuft darueber. */
.minifw__path path { stroke: var(--border-strong); stroke-width: 1.2; stroke-dasharray: 3 4; }

/* Das Schild. Der Weichzeichner liegt als eigene Kopie darunter, damit die
   Kante scharf bleibt -- ein filter auf dem Koerper selbst weicht sie auf. */
.minifw__shield-glow {
  fill: rgba(var(--accent-rgb),.14);
  stroke: none;
  filter: blur(14px);
}
.minifw__shield-body {
  stroke: var(--accent);
  stroke-width: 1.8;
  stroke-linejoin: round;
}
/* Feines Raster in der Flaeche: eine technische Oberflaeche statt einer
   leeren Farbflaeche. Es ist absichtlich fast nicht zu sehen -- sichtbar
   wird es erst als Wirkung, nicht als Muster. */
.minifw__mesh path { stroke: rgba(var(--accent-rgb),.16); stroke-width: .7; }
/* Der Sockel im unteren Drittel gibt dem Schild Gewicht. */
.minifw__shield-kern { stroke: none; }

/* Die Blende wandert einmal je Runde ueber das Schild -- der Moment, in dem
   eine glatte Flaeche zu Material wird. Sie ist auf die Schildform
   beschnitten und laeuft deshalb nicht ueber den Rand hinaus. */
.minifw__sheen { animation: minifw-sheen 4.2s var(--ease-out) infinite; }
@keyframes minifw-sheen {
  0%, 8%    { transform: translateX(60px);  }
  46%, 100% { transform: translateX(300px); }
}

/* Kantenlicht oben links. Ohne diesen Strich bleibt die Kante flach; mit ihm
   sitzt eine Lichtquelle im Bild. */
.minifw__rim {
  stroke: rgba(var(--accent-rgb),.55);
  stroke-width: 1.6;
  stroke-linecap: round;
}
.minifw__rim--seite { stroke: rgba(var(--accent-rgb),.3); }

.minifw__shield-mark {
  stroke: var(--accent);
  stroke-width: 3.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 66;
  stroke-dashoffset: 66;
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.5));
  animation: minifw-mark .9s var(--ease-out) .3s forwards;
}
@keyframes minifw-mark { to { stroke-dashoffset: 0; } }

/* Nach jedem Aufprall geht eine Welle nach aussen -- und zwar vom
   Aufprallpunkt, nicht von der Schildmitte. Der erste Entwurf hatte einen
   grossen Ring um das ganze Schild; der lag im Standbild einfach quer
   darueber und sagte nichts. Die Verzoegerungen sind die Ankunftszeiten der
   beiden Striche: 40 Prozent von 4,2 s plus deren eigener Versatz. */
.minifw__wave circle {
  fill: none;
  stroke: rgba(var(--accent-rgb),.6);
  stroke-width: 1.4;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: minifw-wave 4.2s var(--ease-out) infinite;
}
.minifw__wave--a { animation-delay: .5s;  }
.minifw__wave--b { animation-delay: 2.4s; }
@keyframes minifw-wave {
  0%, 38%   { opacity: 0;   transform: scale(.3); }
  44%       { opacity: .8;  transform: scale(.7); }
  70%, 100% { opacity: 0;   transform: scale(2.1); }
}

/* Die abgewiesenen Verbindungen: ein kurzer Strich laeuft auf das Schild zu
   und ist dort zu Ende. Kein Zurueckprallen -- eine geblockte Verbindung
   kommt nicht zurueck, sie verfaellt. */
.minifw__shot path {
  stroke: var(--danger);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 14 300;
  stroke-dashoffset: 314;
  filter: drop-shadow(0 0 4px rgba(var(--danger-rgb),.6));
  animation: minifw-shot 4.2s cubic-bezier(.3,0,.7,1) infinite;
}
.minifw__shot--a { animation-delay: .5s;  }
.minifw__shot--b { animation-delay: 2.4s; }
/* 314 - 108 = 206: der Strich steht genau an der Kante, dann verfaellt er. */
@keyframes minifw-shot {
  0%       { stroke-dashoffset: 314; opacity: 1; }
  40%      { stroke-dashoffset: 206; opacity: 1; }
  50%,100% { stroke-dashoffset: 206; opacity: 0; }
}

/* Der Funke im Moment des Aufpralls, auf dieselben 40 Prozent gelegt. */
.minifw__burst {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: minifw-burst 4.2s var(--ease-out) infinite;
}
.minifw__burst--a { animation-delay: .5s;  }
.minifw__burst--b { animation-delay: 2.4s; }
@keyframes minifw-burst {
  0%, 38%   { opacity: 0; transform: scale(.25); }
  43%       { opacity: 1; transform: scale(.85); }
  62%, 100% { opacity: 0; transform: scale(1.5); }
}

/* Die erlaubte Verbindung: laeuft durch, ohne zu stocken. */
.minifw__pass path {
  stroke: var(--success);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 16 320;
  stroke-dashoffset: 336;
  filter: drop-shadow(0 0 4px rgba(var(--success-rgb),.6));
  animation: minifw-pass 4.2s linear 1.3s infinite;
}
/* Endwert 38, nicht 0: bei 38 steht der Strichanfang genau auf Weglaenge
   298, also am Netzwerkrand. Damit kommen Strich und Lichtkopf im selben
   Moment an -- vorher lief der Strich dem Kopf davon. */
@keyframes minifw-pass {
  0%       { stroke-dashoffset: 336; }
  62%,100% { stroke-dashoffset: 38;  }
}
/* Der Lichtkopf laeuft dem Strich voran -- dieselbe Dauer, dieselbe
   Verzoegerung, 298 Einheiten von x=58 bis x=356. */
.minifw__comet {
  opacity: 0;
  animation: minifw-comet 4.2s linear 1.3s infinite;
}
@keyframes minifw-comet {
  0%        { transform: translateX(0);     opacity: 0;  }
  8%        { opacity: .85; }
  54%       { opacity: .85; }
  62%       { transform: translateX(298px); opacity: 0;  }
  100%      { transform: translateX(298px); opacity: 0;  }
}

/* Das Aufprallzeichen steht immer -- auch ohne Bewegung erkennt man daran,
   wo die zwei oberen Wege enden. */
.minifw__hit circle { fill: var(--danger); }
.minifw__hit path { stroke: var(--danger); stroke-width: 1.8; stroke-linecap: round; opacity: .75; }

.minifw__return path { stroke: var(--border-strong); stroke-width: 1.2; stroke-linecap: round; }

.mini text.minifw__label--block { fill: var(--danger);  font-size: 11px; }
.mini text.minifw__label--pass  { fill: var(--success); font-size: 11px; }
.mini text.minifw__label--small { font-size: 10px; fill: var(--muted); }

/* --- Wartung: der Kreislauf, der nicht aufhoert ------------------------- */

/* Der helle Bogen ist die Aussage: 90 von 653,5 Einheiten Umfang sind
   sichtbar, der Rest bleibt leer, und das Ganze dreht sich. Wer die Groesse
   des Rings aendert, muss beide Zahlen neu rechnen (2*pi*r). */

.miniwa__au-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .10; }
.miniwa__au-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .05; }
.miniwa__au-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.miniwa__sc-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .16; }
.miniwa__sc-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .07; }
.miniwa__mi-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .07; }
.miniwa__mi-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .14; }

.miniwa__bahn { stroke: var(--border-strong); stroke-width: 1.4; }
.miniwa__lauf {
  transform-origin: 230px 164px;
  animation: miniwa-sweep 9s linear infinite;
}
@keyframes miniwa-sweep { to { transform: rotate(360deg); } }
/* Der Schweif liegt hinter dem Punkt: der Kreis beginnt bei 3 Uhr, dort
   sitzt auch der Punkt, und der Versatz schiebt das sichtbare Stueck auf das
   Ende des Umfangs -- also dahinter. Drei Stuecke statt einem, jedes blasser
   als das vorige: ein einzelner Bogen gleicher Helligkeit sah aus wie ein
   Kabel, erst das Abklingen macht daraus Bewegung. */
.miniwa__spur {
  stroke: var(--accent);
  stroke-width: 2.4;
  stroke-linecap: butt;
}
.miniwa__spur--1 { stroke-dasharray: 16 637.5; stroke-dashoffset: 16; opacity: .85; }
.miniwa__spur--2 { stroke-dasharray: 16 637.5; stroke-dashoffset: 32; opacity: .45; }
.miniwa__spur--3 { stroke-dasharray: 18 635.5; stroke-dashoffset: 50; opacity: .18; }
.miniwa__kopf {
  fill: var(--accent);
  filter: drop-shadow(0 0 7px rgba(var(--accent-rgb),.75));
}

.miniwa__pfeil path {
  stroke: var(--muted-strong);
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.miniwa__scheibe { stroke: var(--border-strong); stroke-width: 1.2; }
.miniwa__glyph {
  stroke: var(--accent);
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}
.miniwa__glyph--warn { stroke: var(--warn); }
.miniwa__punkt { fill: var(--warn); }

.miniwa__kern-scheibe { stroke: var(--border); stroke-width: 1.2; }
/* Die Kurve steht vollstaendig da, damit die Mitte auch im Standbild etwas
   zeigt; darueber laeuft ein helles Stueck. Zwei Pfade, derselbe d-Wert. */
.miniwa__kurve-basis {
  stroke: rgba(var(--success-rgb),.34);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Die Messkurve laeuft durch: Strichmuster plus wandernder Versatz. Die
   Kurve ist rund 90 Einheiten lang, das Muster deckt sie deshalb ganz ab. */
.miniwa__kurve {
  stroke: var(--success);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 26 96;
  filter: drop-shadow(0 0 4px rgba(var(--success-rgb),.5));
  animation: miniwa-kurve 3.4s linear infinite;
}
@keyframes miniwa-kurve {
  from { stroke-dashoffset: 122; }
  to   { stroke-dashoffset: 0;   }
}

.mini text.miniwa__label { fill: var(--muted-strong); font-size: 11px; }
.mini text.miniwa__label--links  { text-anchor: start; }
.mini text.miniwa__label--rechts { text-anchor: end;   }

/* --- Standorte: zwei Haeuser und ein Homeoffice -------------------------- */

/* Dieselbe Schraege und dieselben Rollen wie bei der hybriden Umgebung:
   Front am hellsten, Dach mittel, Flanke am dunkelsten. Wer hier etwas
   aendert, sollte dort nachsehen -- die beiden gehoeren zusammen. */

.minist__au-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .08; }
.minist__au-b { stop-color: rgb(var(--accent-rgb)); stop-opacity: 0;   }
.minist__fr-a { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .10; }
.minist__fr-b { stop-color: rgb(var(--accent-rgb));  stop-opacity: .03; }

.minist__ground path { stroke: var(--border-strong); stroke-width: 1.2; }
.minist__front  { stroke: var(--border-strong); stroke-width: 1.2; }
.minist__dach   { fill: rgba(var(--tint-rgb),.05); stroke: var(--border-strong); stroke-width: 1.1; }
.minist__flanke { fill: var(--rack-2); stroke: var(--border-strong); stroke-width: 1.1; }

.minist__fenster rect, .minist__fenster-klein {
  fill: rgba(var(--accent-rgb),.12);
  stroke: rgba(var(--accent-rgb),.3);
  stroke-width: .8;
}
.minist__tuer { fill: var(--bg-deep); stroke: var(--border-strong); stroke-width: 1; }
.minist__schrank rect { fill: var(--bg-deep); stroke: rgba(var(--tint-rgb),.2); stroke-width: 1; }
.minist__schrank path { stroke: rgba(var(--tint-rgb),.22); stroke-width: 1.4; }

/* Die Strecke selbst liegt ruhig da; die Bewegung laeuft darueber. */
.minist__leitung path { stroke: var(--border-strong); stroke-width: 1.6; }
.minist__fluss path {
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.7));
  stroke-dasharray: 5 400;
  stroke-dashoffset: 405;
  animation: minicl-fluss 3.6s linear infinite;
}
.minist__fluss--a { animation-duration: 3.4s; }
.minist__fluss--b { animation-duration: 2.6s; animation-delay: -.9s; }

/* Das Schloss sitzt auf der Strecke und ist der Punkt der ganzen Zeichnung:
   die Standorte haengen nicht einfach zusammen, die Strecke ist gesichert. */
.minist__schloss circle {
  fill: var(--surface);
  stroke: rgba(var(--accent-rgb),.5);
  stroke-width: 1.4;
}
.minist__buegel { stroke: var(--accent); stroke-width: 1.8; fill: none; }
.minist__korpus { fill: rgba(var(--accent-rgb),.22); stroke: var(--accent); stroke-width: 1.2; }

.mini .minist__haus text { fill: var(--muted-strong); font-size: 11px; }
.mini .minist__wegtext text { fill: var(--muted); font-size: 9px; letter-spacing: .06em; }

/* --- Endpoint: verschiedene Geraete, dieselben Regeln -------------------- */

.miniep__ge-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .12; }
.miniep__ge-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .05; }
.miniep__ko-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .16; }
.miniep__ko-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .08; }

.miniep__konsole rect { stroke: rgba(var(--accent-rgb),.42); stroke-width: 1.4; }
.miniep__zeile path { stroke: var(--border-strong); stroke-width: 1.2; stroke-dasharray: 3 4; }

.miniep__draht path { stroke: var(--border-strong); stroke-width: 1.2; }
/* Die Punkte laufen nach oben: die Geraete melden sich, nicht umgekehrt.
   Deshalb sind die Pfade von unten nach oben geschrieben. */
.miniep__melden path {
  stroke: var(--accent);
  stroke-width: 2.6;
  stroke-linecap: round;
  filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.65));
  stroke-dasharray: 5 400;
  stroke-dashoffset: 405;
  animation: minicl-fluss 4s linear infinite;
}
.miniep__melden--a { animation-duration: 3.8s; }
.miniep__melden--b { animation-duration: 3.0s; animation-delay: -1.1s; }
.miniep__melden--c { animation-duration: 4.6s; animation-delay: -2.0s; }
.miniep__melden--d { animation-duration: 3.3s; animation-delay: -.6s;  }

.miniep__geraet rect { stroke: var(--border-strong); stroke-width: 1.3; }
.miniep__schirm { stroke: rgba(var(--accent-rgb),.5); stroke-width: 1.6; stroke-linecap: round; }
.miniep__einschub { stroke: rgba(var(--tint-rgb),.22); stroke-width: 1.6; stroke-linecap: round; }
.miniep__fuss, .miniep__knopf {
  stroke: var(--border-strong);
  stroke-width: 2;
  stroke-linecap: round;
}
.miniep__tastatur { fill: var(--rack-2); stroke: var(--border-strong); stroke-width: 1.2; }

/* Dasselbe Schild an allen vier Geraeten -- die Aussage steckt in der
   Wiederholung, nicht im einzelnen Zeichen. */
.miniep__marke-eins path:first-child {
  fill: rgba(var(--success-rgb),.16);
  stroke: var(--success);
  stroke-width: 1.3;
}
.miniep__haken { stroke: var(--success); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; fill: none; }

.mini .miniep__konsole text { fill: var(--accent); font-size: 11px; letter-spacing: .06em; }
.mini .miniep__geraet text  { fill: var(--muted-strong); font-size: 11px; }
.mini .miniep__fussnote text { fill: var(--muted); font-size: 9px; letter-spacing: .06em; }

/* --- Microsoft 365: Zugang, Plattform, zweite Ablage --------------------- */

.minim3__pa-a { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .10; }
.minim3__pa-b { stop-color: rgb(var(--accent-rgb));  stop-opacity: .04; }
.minim3__ka-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .12; }
.minim3__ka-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .05; }

.minim3__person circle { fill: rgba(var(--tint-rgb),.07); stroke: var(--muted); stroke-width: 1.6; }
.minim3__person path   { stroke: var(--muted); stroke-width: 1.6; fill: none; }

.minim3__bogen  { stroke: var(--accent); stroke-width: 2; stroke-linecap: round; }
.minim3__sockel { stroke: var(--border-strong); stroke-width: 2; stroke-linecap: round; }
.minim3__schloss rect { fill: rgba(var(--accent-rgb),.2); stroke: var(--accent); stroke-width: 1.6; }
.minim3__schloss path { stroke: var(--accent); stroke-width: 1.6; fill: none; }

.minim3__weg path { stroke: var(--border-strong); stroke-width: 1.4; stroke-linecap: round; }
.minim3__gestrichelt { stroke-dasharray: 4 5; }
.minim3__fluss path {
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.7));
  stroke-dasharray: 5 220;
  stroke-dashoffset: 225;
  animation: minim3-fluss 3.2s linear infinite;
}
@keyframes minim3-fluss {
  from { stroke-dashoffset: 225; }
  to   { stroke-dashoffset: -5;  }
}

.minim3__panel > rect  { stroke: rgba(var(--accent-rgb),.34); stroke-width: 1.4; }
.minim3__kachel rect   { stroke: var(--border-strong); stroke-width: 1.2; }
.minim3__glyph { stroke: var(--accent); stroke-width: 1.7; fill: none; stroke-linecap: round; stroke-linejoin: round; }

/* Die zweite Ablage: ein Behaelter, kein weiterer Kasten. Der Unterschied
   der Form sagt, dass sie nicht Teil der Plattform ist. */
.minim3__deckel { fill: rgba(var(--success-rgb),.16); stroke: var(--success); stroke-width: 1.4; }
.minim3__mantel { fill: rgba(var(--success-rgb),.08); stroke: var(--success); stroke-width: 1.4; }
.minim3__band   { stroke: rgba(var(--success-rgb),.5); stroke-width: 1.1; fill: none; }

.mini .minim3__panel > text  { fill: var(--accent); font-size: 12px; letter-spacing: .06em; }
.mini text.minim3__kacheltext { text-anchor: start; fill: var(--muted-strong); font-size: 11px; }
.mini .minim3__person text   { fill: var(--muted); font-size: 10px; }
.mini .minim3__tor text      { fill: var(--muted-strong); font-size: 11px; }
.mini text.minim3__klein     { fill: var(--muted); font-size: 9px; }
.mini .minim3__tresor text   { fill: var(--muted-strong); font-size: 11px; }

/* --- Migration: der Weg hinauf und der Rueckweg -------------------------- */

.minimg__bl-a { stop-color: rgb(var(--tint-rgb));    stop-opacity: .06; }
.minimg__bl-b { stop-color: rgb(var(--tint-rgb));    stop-opacity: .02; }
.minimg__zi-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .16; }
.minimg__zi-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .06; }

.minimg__stand rect { stroke: var(--border-strong); stroke-width: 1.3; }
.minimg__einschub { stroke: rgba(var(--tint-rgb),.2); stroke-width: 1.6; stroke-linecap: round; }
.minimg__ziel rect  { stroke: rgba(var(--accent-rgb),.45); stroke-width: 1.4; }
.minimg__glyph { stroke: var(--accent); stroke-width: 1.7; fill: none; stroke-linecap: round; stroke-linejoin: round; }

.minimg__bahn { stroke: var(--border-strong); stroke-width: 2; stroke-linecap: round; }
/* Der helle Punkt laeuft die Strecke ab und faengt wieder vorn an. Die
   Pfadlaenge liegt bei rund 300 Einheiten; das Muster deckt sie ab. */
.minimg__fortschritt {
  stroke: var(--accent);
  stroke-width: 3.4;
  stroke-linecap: round;
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.7));
  stroke-dasharray: 14 320;
  stroke-dashoffset: 334;
  animation: minimg-lauf 4.4s linear infinite;
}
@keyframes minimg-lauf {
  from { stroke-dashoffset: 334; }
  to   { stroke-dashoffset: -14; }
}

.minimg__halt circle {
  fill: var(--surface);
  stroke: var(--accent);
  stroke-width: 1.6;
}
.mini text.minimg__nr   { fill: var(--accent); font-size: 10px; }
.mini text.minimg__name { fill: var(--muted-strong); font-size: 11px; }
.mini .minimg__stand text, .mini .minimg__ziel text { fill: var(--muted-strong); font-size: 11px; }

/* Der Rueckweg: gestrichelt und gegenlaeufig -- er ist eine Zusicherung,
   keine Strecke, die tatsaechlich begangen wird. */
.minimg__rueck path { stroke: var(--muted); stroke-width: 1.3; stroke-linecap: round; }
.minimg__rueck path:first-child { stroke-dasharray: 5 5; }
.mini .minimg__rueck text { fill: var(--muted); font-size: 9px; letter-spacing: .06em; }

/* --- Audit: das Blatt, das am Ende dasteht ------------------------------- */

.miniau__bl-a { stop-color: rgb(var(--tint-rgb)); stop-opacity: .07; }
.miniau__bl-b { stop-color: rgb(var(--tint-rgb)); stop-opacity: .02; }
.miniau__gl-a { stop-color: rgb(var(--tint-rgb)); stop-opacity: .10; }
.miniau__gl-b { stop-color: rgb(var(--accent-rgb)); stop-opacity: .04; }

.miniau__seite { stroke: var(--border-strong); stroke-width: 1.4; }
.miniau__ecke  { fill: rgba(var(--tint-rgb),.09); stroke: var(--border-strong); stroke-width: 1.2; }
.miniau__trenner { stroke: rgba(var(--accent-rgb),.45); stroke-width: 1.6; }
.miniau__linie   { stroke: var(--border); stroke-width: 1; }

.mini text.miniau__titel   { text-anchor: start; fill: var(--accent);      font-size: 13px; letter-spacing: .06em; }
.mini text.miniau__bereich { text-anchor: start; fill: var(--muted-strong); font-size: 11px; }
.mini text.miniau__fuss    { text-anchor: start; fill: var(--muted);        font-size: 9px; letter-spacing: .06em; }
.mini text.miniau__urteil  { font-size: 10px; }

/* Farbe und Wort tragen die Bewertung gemeinsam. */
.miniau__pille--ok   { fill: rgba(var(--success-rgb),.16); stroke: rgba(var(--success-rgb),.5); stroke-width: 1; }
.miniau__pille--warn { fill: rgba(var(--warn-rgb),.16);    stroke: rgba(var(--warn-rgb),.5);    stroke-width: 1; }
.miniau__pille--stop { fill: rgba(var(--danger-rgb),.16);  stroke: rgba(var(--danger-rgb),.5);  stroke-width: 1; }
.mini text.miniau__urteil--ok   { fill: var(--success); }
.mini text.miniau__urteil--warn { fill: var(--warn); }
.mini text.miniau__urteil--stop { fill: var(--danger); }

/* Die Lupe. Das Glas ist gefuellt, aber sehr durchsichtig -- eine Lupe, die
   verdeckt, was sie zeigen soll, waere ein Widerspruch. */
.miniau__glas     { stroke: none; }
.miniau__fassung  { fill: none; stroke: var(--accent); stroke-width: 3; }
.miniau__griff    { stroke: var(--muted-strong); stroke-width: 7; stroke-linecap: round; }
.miniau__reflex   { stroke: rgba(var(--tint-rgb),.35); stroke-width: 2.4; stroke-linecap: round; fill: none; }

/* --- Regelwerk: die erste passende Regel gewinnt -------------------------- */

/* Die Zeilen sind links angeschlagen, nicht mittig -- eine Tabelle liest man
   an der Kante. `.mini text` mittet, deshalb hier mit `text.` davor, sonst
   gewinnt die allgemeine Regel. */
.mini text.minirw__nr  { text-anchor: middle; fill: var(--muted); font-size: 11px; }
.mini text.minirw__von { text-anchor: start;  fill: var(--text);  font-size: 11px; }
.mini text.minirw__was { text-anchor: start;  fill: var(--muted); font-size: 11px; }
.mini text.minirw__tun { text-anchor: start;  font-size: 11px; }
.mini text.minirw__tun--ok { fill: var(--success); }
.mini text.minirw__tun--no { fill: var(--danger); }

.minirw__frame rect {
  fill: rgba(var(--tint-rgb),.03);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.minirw__frame path { stroke: var(--border-strong); stroke-width: 1.2; }
.mini .minirw__head text { fill: var(--muted); font-size: 10px; letter-spacing: .12em; }

/* Die getroffene Regel bleibt hinterlegt -- sie ist das Ergebnis. */
.minirw__hit {
  fill: rgba(var(--success-rgb),.12);
  stroke: rgba(var(--success-rgb),.45);
  stroke-width: 1;
}

/* Die Marke laeuft die vier Regeln entlang und bleibt auf der dritten.
   Vier Halte, dann Ruhe: 0/1/2 kurz, dann bis zum Ende auf Zeile 3. */
.minirw__mark {
  fill: rgba(var(--accent-rgb),.16);
  stroke: var(--accent);
  stroke-width: 1.2;
  animation: minirw-mark 5s steps(1, end) infinite;
}
@keyframes minirw-mark {
  0%,  14% { transform: translateY(0);   opacity: 1; }
  14.01%, 28% { transform: translateY(34px); opacity: 1; }
  28.01%, 100% { transform: translateY(68px); opacity: 1; }
}

/* Die Schlussregel: keine Regel wie die anderen, sondern was uebrig bleibt. */
.minirw__rest path { stroke: var(--border-strong); stroke-width: 1; stroke-dasharray: 3 5; }

.minirw__in path, .minirw__out path { stroke: var(--muted-strong); stroke-width: 1.6; stroke-linecap: round; }
.mini .minirw__in text  { fill: var(--muted);   font-size: 10px; text-anchor: start; }
.mini .minirw__out text { fill: var(--success); font-size: 10px; text-anchor: start; }
/* Der Ausgang gehoert zur getroffenen Regel -- er kommt, wenn die Marke steht. */
.minirw__out {
  opacity: 0;
  animation: minirw-out 5s var(--ease-out) infinite;
}
@keyframes minirw-out {
  0%,   28% { opacity: 0; }
  36%, 100% { opacity: 1; }
}
.minirw__note text { fill: var(--accent); font-size: 11px; letter-spacing: .08em; }

/* --- Serverraum ----------------------------------------------------------

   Tiefe entsteht hier ueber drei Mittel und nicht ueber Linien: Die
   Schrankfronten werden nach hinten dunkler (Luft nimmt Kontrast), das
   Bodenraster wird nach hinten dichter, und die Lampen sitzen nur im
   vordersten Schrank. Alles drei zusammen liest man als Raum, jedes einzeln
   nicht.
   ------------------------------------------------------------------------ */

.minisr__back-wand {
  fill: var(--rack-2);
  stroke: var(--border-strong);
  stroke-width: 1;
}
/* Eine Tuer am Ende des Gangs. Eine schwarze Flaeche allein liest man als
   Loch, nicht als Wand. */
.minisr__back-tuer {
  fill: rgba(var(--tint-rgb),.03);
  stroke: var(--border-strong);
  stroke-width: 1;
}
.minisr__back path { stroke: rgba(var(--tint-rgb),.3); stroke-width: 1.6; stroke-linecap: round; }

.minisr__floor path { stroke: var(--border); stroke-width: .8; }
.minisr__floor-flaeche { fill: rgba(var(--tint-rgb),.04); stroke: none; }
.minisr__aisle path {
  fill: rgba(var(--accent-rgb),.05);
  stroke: rgba(var(--accent-rgb),.16);
  stroke-width: 1;
}
.minisr__tray path { stroke: var(--border-strong); stroke-width: 1.1; }

/* Schrankfronten und Seitenflaechen. Die Front ist heller als die Flanke --
   sonst kippt der Koerper optisch weg. */
/* Jeder Schrank ist EINE Flaeche, deren Ecken auf den Fluchtlinien liegen.
   Front und Flanke getrennt zu setzen war der Fehler des ersten Entwurfs. */
.minisr__cab {
  fill: var(--rack-1);
  stroke: var(--border-strong);
  stroke-width: 1.1;
}
/* Je weiter hinten, desto weniger Kontrast. d1 vorn, d4 hinten. */
.minisr__cab--d1 { opacity: 1;   }
.minisr__cab--d2 { opacity: .82; }
.minisr__cab--d3 { opacity: .62; }
.minisr__cab--d4 { opacity: .44; }

.minisr__units { stroke: rgba(var(--tint-rgb),.15); stroke-width: 2.2; fill: none; }
.minisr__units--fern { stroke: rgba(var(--tint-rgb),.10); stroke-width: 1.8; }
.minisr__led { fill: var(--success); animation: minirk-blink 4s ease-in-out infinite; }
.sl-1 { animation-delay: 0s;    animation-duration: 3.8s; }
.sl-2 { animation-delay: -.6s;  animation-duration: 5s;   }
.sl-3 { animation-delay: -1.3s; animation-duration: 3.2s; }
.sl-4 { animation-delay: -2s;   animation-duration: 4.4s; }
.sl-5 { animation-delay: -.9s;  animation-duration: 2.6s; }
.sl-6 { animation-delay: -1.7s; animation-duration: 4.8s; }
.sl-7 { animation-delay: -2.4s; animation-duration: 3.5s; }

/* Deckenlicht: zwei Streifen, der hintere schmaler -- dieselbe Verjuengung
   wie am Boden, nur oben. */
.minisr__light path {
  stroke: rgba(var(--accent-rgb),.5);
  stroke-width: 3;
  stroke-linecap: round;
  filter: drop-shadow(0 0 6px rgba(var(--accent-rgb),.45));
}
.minisr__light path:last-child { stroke-width: 2; opacity: .6; }


/* --- Hybride Umgebung: Standort, Rechenzentrum, Cloud ---------------------

   Dieselbe Schraege wie beim Serverschrank (Dach nach rechts oben, Flanke
   rechts). Die Zeichnungen sollen wie ein Satz wirken, nicht wie vier
   Handschriften -- deshalb bekommen Dach, Front und Flanke ueberall dieselben
   Rollen: Front am hellsten, Dach mittel, Flanke am dunkelsten.
   ------------------------------------------------------------------------ */

.minicl__ground path { stroke: var(--border-strong); stroke-width: 1.2; }

.minicl__front  { fill: var(--rack-1); stroke: var(--border-strong); stroke-width: 1.2; }
.minicl__dach   { fill: rgba(var(--tint-rgb),.05); stroke: var(--border-strong); stroke-width: 1.1; }
.minicl__flanke { fill: var(--rack-2); stroke: var(--border-strong); stroke-width: 1.1; }

.minicl__fenster rect {
  fill: rgba(var(--accent-rgb),.10);
  stroke: rgba(var(--accent-rgb),.28);
  stroke-width: .8;
}
.minicl__tuer {
  fill: var(--bg-deep);
  stroke: var(--border-strong);
  stroke-width: 1;
}
.minicl__schrank rect {
  fill: var(--bg-deep);
  stroke: rgba(var(--tint-rgb),.2);
  stroke-width: 1;
}
.minicl__schrank path { stroke: rgba(var(--tint-rgb),.22); stroke-width: 1.4; }

.minicl__reihe rect {
  fill: var(--bg-deep);
  stroke: rgba(var(--tint-rgb),.16);
  stroke-width: .8;
}

/* Die Cloud steht ueber beidem und hat keinen Ort -- deshalb ohne Standlinie
   und mit weicherem Rand als die Gebaeude. */
.minicl__cloud-body {
  fill: rgba(var(--accent-rgb),.07);
  stroke: rgba(var(--accent-rgb),.45);
  stroke-width: 1.4;
}
.minicl__cloud-line { stroke: rgba(var(--accent-rgb),.4); stroke-width: 1.6; stroke-linecap: round; }

.minicl__schrank circle, .minicl__rzled circle,
.minist__schrank circle, .miniep__zeile circle, .miniep__geraet circle { fill: var(--success); }
.minicl__schrank circle, .minicl__rzled circle,
.minist__schrank circle, .miniep__zeile circle, .miniep__geraet circle {
  animation: minirk-blink 4s ease-in-out infinite;
}
.cl-1 { animation-delay: 0s;    animation-duration: 3.6s; }
.cl-2 { animation-delay: -.8s;  animation-duration: 4.8s; }
.cl-3 { animation-delay: -1.5s; animation-duration: 3.1s; }
.cl-4 { animation-delay: -2.2s; animation-duration: 4.2s; }
.cl-5 { animation-delay: -.5s;  animation-duration: 2.7s; }

/* Die Wege. Die feste Strecke durchgezogen, die Internetwege gestrichelt --
   der Unterschied zwischen gemieteter Leitung und offenem Netz. */
.minicl__weg path { stroke: var(--border-strong); stroke-width: 1.4; }
.minicl__weg path:nth-child(1),
.minicl__weg path:nth-child(2) { stroke-dasharray: 4 5; }

/* Ein Lichtpunkt je Weg, unterschiedlich schnell. Gleichschnelle Punkte auf
   drei Wegen sehen aus wie ein Zahnrad. */
.minicl__fluss path {
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.7));
  stroke-dasharray: 5 400;
  stroke-dashoffset: 405;
  animation: minicl-fluss 3.6s linear infinite;
}
.minicl__fluss--a { animation-duration: 3.2s; }
.minicl__fluss--b { animation-duration: 4.4s; animation-delay: -1.2s; }
.minicl__fluss--c { animation-duration: 2.6s; animation-delay: -.6s; }
@keyframes minicl-fluss {
  from { stroke-dashoffset: 405; }
  to   { stroke-dashoffset: -5;  }
}

.mini .minicl__haus text, .mini .minicl__rz text { fill: var(--muted-strong); font-size: 11px; }
.mini .minicl__cloud text { fill: var(--accent); font-size: 11px; letter-spacing: .08em; }
.mini .minicl__wegtext text { fill: var(--muted); font-size: 9px; letter-spacing: .06em; }


/* --- Ueberblick: die ganze Strecke, einmal durchleuchtet ------------------

   Die Verzoegerungen der drei Statuspunkte sind aus ihren x-Werten gerechnet:
   der Balken laeuft in 9s von -15 auf 525, also erreicht er x bei
   t = (x+15)/540*9. Damit die Spitze der Lichtkurve genau dann faellt, steht
   die Verzoegerung negativ als 9s - t. Wer eine Station verschiebt, rechnet
   das mit -- sonst blinken die Punkte an der falschen Stelle, und das Bild
   zeigt drei zufaellige Lampen statt eines Durchlaufs.
   ------------------------------------------------------------------------ */

.miniub__au-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .11; }
.miniub__au-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .05; }
.miniub__au-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.miniub__fe-a { stop-color: rgb(var(--tint-rgb));    stop-opacity: .05; }
.miniub__fe-b { stop-color: rgb(var(--tint-rgb));    stop-opacity: .015; }
.miniub__ba-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .05; }
.miniub__ba-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .12; }
.miniub__sn-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.miniub__sn-b { stop-color: rgb(var(--accent-rgb));  stop-opacity: .13; }
.miniub__sn-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }

.miniub__band  { stroke: var(--border-strong); stroke-width: 1.2; }
.miniub__feld  { stroke: var(--border-strong); stroke-width: 1.2; }

/* Die Messlinie im Band steht vollstaendig da -- im Standbild soll das Band
   nicht leer sein -- und darueber laeuft ein helles Stueck durch. */
.miniub__spurbasis {
  stroke: rgba(var(--accent-rgb),.26);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.miniub__spur {
  stroke: var(--accent);
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 74 460;
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.6));
  animation: miniub-spur 9s linear infinite;
}
@keyframes miniub-spur {
  from { stroke-dashoffset: 534; }
  to   { stroke-dashoffset: 0;   }
}

.miniub__abgang path {
  stroke: var(--border-strong);
  stroke-width: 1.2;
  stroke-dasharray: 3 4;
}

.miniub__scan { animation: miniub-scan 9s linear infinite; }
@keyframes miniub-scan {
  from { transform: translateX(-56px); }
  to   { transform: translateX(498px); }
}

/* --- Arbeitsplaetze --- */
.miniub__arbeit rect {
  fill: rgba(var(--accent-rgb),.08);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.miniub__arbeit path {
  stroke: var(--border-strong);
  stroke-width: 1.4;
  stroke-linecap: round;
}
.miniub__draht path {
  stroke: var(--border-strong);
  stroke-width: 1.1;
  fill: none;
}
.miniub__switch {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.miniub__port rect { fill: rgba(var(--accent-rgb),.16); }

/* --- Firewall --- */
.miniub__schild path {
  fill: rgba(var(--accent-rgb),.12);
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-linejoin: round;
}
.miniub__haken path {
  stroke: var(--success);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Rechenzentrum --- */
.miniub__schrank {
  fill: var(--rack-2);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.miniub__einschub rect {
  fill: var(--rack-1);
  stroke: var(--border);
  stroke-width: 1;
}

/* Betriebslampen. Vier Takte, damit es nicht im Gleichschritt blinkt. */
.miniub__led { fill: var(--success); }
.miniub__led--a { animation: miniub-led 2.4s ease-in-out infinite; }
.miniub__led--b { animation: miniub-led 3.1s ease-in-out infinite -0.7s; }
.miniub__led--c { animation: miniub-led 2.7s ease-in-out infinite -1.5s; }
.miniub__led--d { animation: miniub-led 3.5s ease-in-out infinite -2.2s; }
@keyframes miniub-led {
  0%, 100% { opacity: .35; }
  50%      { opacity: 1;   }
}

/* --- Verkehr auf den Strecken --- */
.miniub__strecke path {
  stroke: var(--border-strong);
  stroke-width: 1.3;
}
.miniub__hin path, .miniub__rueck path {
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 4 56;
}
.miniub__hin path {
  stroke: var(--accent);
  filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.7));
  stroke-dashoffset: 60;
  animation: miniub-lauf 1.9s linear infinite;
}
.miniub__rueck path {
  stroke: var(--success);
  filter: drop-shadow(0 0 4px rgba(var(--success-rgb),.6));
  stroke-dashoffset: 60;
  animation: miniub-lauf 2.6s linear infinite -1.1s;
}
@keyframes miniub-lauf {
  from { stroke-dashoffset: 60; }
  to   { stroke-dashoffset: 0;  }
}

/* --- Die Statuspunkte --- */
.miniub__punkt { fill: var(--accent); }
.miniub__halo  { fill: rgba(var(--accent-rgb),.22); }
.miniub__punkt--1, .miniub__halo--1 { animation: miniub-status 9s linear infinite -7.18s; }
.miniub__punkt--2, .miniub__halo--2 { animation: miniub-status 9s linear infinite -4.75s; }
.miniub__punkt--3, .miniub__halo--3 { animation: miniub-status 9s linear infinite -2.32s; }
@keyframes miniub-status {
  0%        { opacity: 1;   }
  3%        { opacity: 1;   }
  16%       { opacity: .22; }
  100%      { opacity: .22; }
}
.miniub__halo { opacity: .22; }

.mini text.miniub__bandtext { fill: var(--accent); font-size: 10px; letter-spacing: .12em; }
.mini text.miniub__name     { fill: var(--muted-strong); font-size: 11.5px; }
.mini text.miniub__sub      { fill: var(--muted); font-size: 9.5px; letter-spacing: .02em; }
.mini text.miniub__fuss     { fill: var(--muted); font-size: 10px; letter-spacing: .14em; }


/* --- Leistungen: eine Nabe, sechs Felder, ein Rundlauf -------------------

   Der Takt dauert 3,6s, jede Speiche hat 0,6s davon. Die Verzoegerung einer
   Speiche k ist deshalb -(3,6s - k*0,6s). Der Ring des Feldes bekommt
   dieselbe Verzoegerung und leuchtet bei 18% des Taktes auf -- also genau
   dann, wenn der Impuls am Ende der Speiche ankommt.
   ------------------------------------------------------------------------ */

.minils__au-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .12; }
.minils__au-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .05; }
.minils__au-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minils__na-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .18; }
.minils__na-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .08; }
.minils__fe-a { stop-color: rgb(var(--tint-rgb));    stop-opacity: .06; }
.minils__fe-b { stop-color: rgb(var(--tint-rgb));    stop-opacity: .02; }

.minils__bahn {
  stroke: var(--border);
  stroke-width: 1.1;
  stroke-dasharray: 2 6;
}
.minils__speiche path { stroke: var(--border-strong); stroke-width: 1.2; }

.minils__impuls path {
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 4 40;
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.75));
  animation: minils-impuls 3.6s linear infinite;
}
.minils__i-2 { animation-delay: -3.0s; }
.minils__i-3 { animation-delay: -2.4s; }
.minils__i-4 { animation-delay: -1.8s; }
.minils__i-5 { animation-delay: -1.2s; }
.minils__i-6 { animation-delay: -0.6s; }
@keyframes minils-impuls {
  0%          { stroke-dashoffset: 44; opacity: 1; }
  16.6%       { stroke-dashoffset: 0;  opacity: 1; }
  16.7%, 100% { stroke-dashoffset: 0;  opacity: 0; }
}

.minils__nabering { stroke: var(--border-accent); stroke-width: 1.4; }
.minils__kranz {
  stroke: rgba(var(--accent-rgb),.34);
  stroke-width: 1.2;
  stroke-dasharray: 10 8;
  transform-origin: 240px 160px;
  animation: minils-kranz 26s linear infinite;
}
@keyframes minils-kranz { to { transform: rotate(360deg); } }

.minils__ring { animation: minils-ring 3.6s linear infinite; }
.minils__ring--2 { animation-delay: -3.0s; }
.minils__ring--3 { animation-delay: -2.4s; }
.minils__ring--4 { animation-delay: -1.8s; }
.minils__ring--5 { animation-delay: -1.2s; }
.minils__ring--6 { animation-delay: -0.6s; }
@keyframes minils-ring {
  0%, 13%   { stroke: var(--border-strong); stroke-width: 1.3; }
  18%       { stroke: var(--accent);        stroke-width: 2.4; }
  44%, 100% { stroke: var(--border-strong); stroke-width: 1.3; }
}

.minils__glyph {
  stroke: var(--accent);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}
/* Der Name in der Nabe. Steht Robi darueber, wird er kleiner: Bei 12 Punkten
   ist "Netzware" 63 Einheiten breit, und die Nabe gibt auf dieser Hoehe nur
   67 her -- zwei Einheiten Luft auf jeder Seite heisst, die Schrift steht auf
   der Linie. Bei 10 Punkten sind es 52 in 68, also knapp acht je Seite.
   Ohne Robi steht der Name allein und mittig und darf gross bleiben. */
.mini text.minils__marke       { fill: var(--white); font-size: 10px; letter-spacing: .06em; }
.mini text.minils__marke--gross{ font-size: 12px; }
.mini text.minils__name      { fill: var(--muted-strong); font-size: 11px; }
.mini text.minils__name--links  { text-anchor: start; }
.mini text.minils__name--rechts { text-anchor: end;   }

/* --- Betreuung: drei Spuren, ein Abspielkopf ------------------------------

   Der Kopf laeuft in 11s von x=92 bis x=452. Alles, was aufleuchten soll,
   bekommt die Verzoegerung -(11s - t) mit t = (x-92)/360*11s. Wer ein Fenster
   verschiebt, rechnet das mit; sonst haken sich die Fenster ab, bevor der
   Kopf da war.
   ------------------------------------------------------------------------ */

.minibt__wa-a { stop-color: rgb(var(--success-rgb)); stop-opacity: .05; }
.minibt__wa-b { stop-color: rgb(var(--success-rgb)); stop-opacity: .12; }
.minibt__ko-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minibt__ko-b { stop-color: rgb(var(--accent-rgb));  stop-opacity: .16; }
.minibt__ko-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }

.minibt__bahn {
  fill: rgba(var(--tint-rgb),.03);
  stroke: var(--border-strong);
  stroke-width: 1.1;
}
.minibt__wach { stroke: none; }
.minibt__kurvebasis {
  stroke: rgba(var(--success-rgb),.34);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.minibt__kurve {
  stroke: var(--success);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 60 420;
  filter: drop-shadow(0 0 4px rgba(var(--success-rgb),.5));
  animation: minibt-kurve 11s linear infinite;
}
@keyframes minibt-kurve {
  from { stroke-dashoffset: 480; }
  to   { stroke-dashoffset: 0;   }
}

/* Zustand statt Takt: Ein Wartungsfenster ist vor dem Abspielkopf offen und
   danach erledigt -- es bleibt erledigt, bis der Kopf von vorne beginnt. Das
   laesst sich nicht mit einer gemeinsamen Kurve und verschobenem Start
   machen, weil jedes Fenster unterschiedlich lange erledigt ist: das erste
   fast den ganzen Takt, das letzte nur ein Fuenftel davon. Deshalb hat jedes
   Fenster seine eigene Kurve, und die Prozentwerte darin sind die Stelle auf
   der Zeitachse: (x-92)/360.
   Ein frueherer Versuch mit einer Kurve und negativem Start sah im Standbild
   aus, als waeren alle vier Fenster schon abgehakt, obwohl der Kopf erst beim
   zweiten stand. */

.minibt__fe {
  fill: rgba(var(--accent-rgb),.22);
  stroke: var(--border-accent);
  stroke-width: 1;
  opacity: .28;
}
.minibt__haken path {
  stroke: var(--success);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 22;
  stroke-dashoffset: 22;
  opacity: 0;
}
.minibt__fe--1 { animation: minibt-fe1 11s linear infinite; }
.minibt__ha--1 { animation: minibt-ha1 11s linear infinite; }
@keyframes minibt-fe1 {
  0%, 13.89%       { opacity: .28; }
  15.09%       { opacity: 1;   }
  18.89%, 100%   { opacity: .8;  }
}
@keyframes minibt-ha1 {
  0%, 13.89%       { opacity: 0; stroke-dashoffset: 22; }
  16.89%, 100%   { opacity: 1; stroke-dashoffset: 0;  }
}
.minibt__fe--2 { animation: minibt-fe2 11s linear infinite; }
.minibt__ha--2 { animation: minibt-ha2 11s linear infinite; }
@keyframes minibt-fe2 {
  0%, 36.11%       { opacity: .28; }
  37.31%       { opacity: 1;   }
  41.11%, 100%   { opacity: .8;  }
}
@keyframes minibt-ha2 {
  0%, 36.11%       { opacity: 0; stroke-dashoffset: 22; }
  39.11%, 100%   { opacity: 1; stroke-dashoffset: 0;  }
}
.minibt__fe--3 { animation: minibt-fe3 11s linear infinite; }
.minibt__ha--3 { animation: minibt-ha3 11s linear infinite; }
@keyframes minibt-fe3 {
  0%, 58.33%       { opacity: .28; }
  59.53%       { opacity: 1;   }
  63.33%, 100%   { opacity: .8;  }
}
@keyframes minibt-ha3 {
  0%, 58.33%       { opacity: 0; stroke-dashoffset: 22; }
  61.33%, 100%   { opacity: 1; stroke-dashoffset: 0;  }
}
.minibt__fe--4 { animation: minibt-fe4 11s linear infinite; }
.minibt__ha--4 { animation: minibt-ha4 11s linear infinite; }
@keyframes minibt-fe4 {
  0%, 80.56%       { opacity: .28; }
  81.76%       { opacity: 1;   }
  85.56%, 100%   { opacity: .8;  }
}
@keyframes minibt-ha4 {
  0%, 80.56%       { opacity: 0; stroke-dashoffset: 22; }
  83.56%, 100%   { opacity: 1; stroke-dashoffset: 0;  }
}
.minibt__sicht {
  stroke: var(--warn);
  stroke-width: 1.2;
  stroke-dasharray: 4 4;
  opacity: 0;
  animation: minibt-melden 11s linear infinite;
}
.minibt__ezeichen {
  fill: none;
  stroke: var(--warn);
  stroke-width: 1.8;
  stroke-linejoin: round;
  opacity: 0;
  animation: minibt-melden 11s linear infinite;
}
@keyframes minibt-melden {
  0%, 47.22%     { opacity: 0;  }
  48.22%, 100% { opacity: .85; }
}
.minibt__ehalo {
  fill: rgba(var(--warn-rgb),.22);
  opacity: 0;
  animation: minibt-halo 11s linear infinite;
}
@keyframes minibt-halo {
  0%, 47.22%      { opacity: 0;  }
  47.82%      { opacity: 1;  }
  53.22%, 100%  { opacity: .4; }
}
.minibt__antwort {
  fill: rgba(var(--success-rgb),.3);
  stroke: rgba(var(--success-rgb),.55);
  stroke-width: 1;
  transform-origin: 274px 0;
  animation: minibt-antwort 11s linear infinite;
}
@keyframes minibt-antwort {
  0%, 50.56%  { transform: scaleX(0); }
  67.78%      { transform: scaleX(1); }
  100%           { transform: scaleX(1); }
}
.minibt__ehaken {
  stroke: var(--success);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 22;
  stroke-dashoffset: 22;
  opacity: 0;
  animation: minibt-ehaken 11s linear infinite;
}
.mini text.minibt__erledigt {
  fill: var(--muted);
  font-size: 9.5px;
  opacity: 0;
  animation: minibt-ehaken 11s linear infinite;
}
@keyframes minibt-ehaken {
  0%, 72.22%      { opacity: 0; stroke-dashoffset: 22; }
  75.22%, 100%  { opacity: 1; stroke-dashoffset: 0;  }
}
.minibt__kopf { animation: minibt-kopf 11s linear infinite; }
@keyframes minibt-kopf {
  from { transform: translateX(80px);  }
  to   { transform: translateX(440px); }
}
.minibt__kopfstrich { stroke: rgba(var(--accent-rgb),.65); stroke-width: 1.2; }
.minibt__kopfspitze { fill: var(--accent); }

.minibt__achse path, .minibt__achse { stroke: var(--border-strong); stroke-width: 1.2; }
.mini text.minibt__spurname { fill: var(--muted-strong); font-size: 10.5px; text-anchor: end; }
.mini text.minibt__klein    { fill: var(--muted); font-size: 9.5px; }
.mini text.minibt__klein--links { text-anchor: start; }

/* --- Schichten: der Stapel, durch den eine Anfrage faellt -----------------

   Der Impuls braucht 2,4s fuer die Spindel, der ganze Takt 4,8s. Eine Platte
   wird erreicht bei t = (Mitte-56)/214*2,4s, die Verzoegerung ihrer Kante
   steht negativ als 4,8s - t.
   ------------------------------------------------------------------------ */

.minisc__au-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .10; }
.minisc__au-b { stop-color: rgb(var(--accent-rgb)); stop-opacity: 0;   }
.minisc__pl-a { stop-color: rgb(var(--tint-rgb));   stop-opacity: .055; }
.minisc__pl-b { stop-color: rgb(var(--tint-rgb));   stop-opacity: .015; }

.minisc__platte { stroke: var(--border-strong); stroke-width: 1.2; }
.minisc__ka {
  fill: var(--accent);
  opacity: .22;
  animation: minisc-kante 4.8s linear infinite;
}
/* Beim Durchlauf hebt sich die ganze Platte, nicht nur ihre Kante -- ein
   fuenf Einheiten breiter Streifen allein war im fertigen Layout kaum zu
   sehen. */
.minisc__platte { animation: minisc-platte 4.8s linear infinite; }
.minisc__platte:nth-of-type(1) { animation-delay: -4.53s; }
.minisc__platte:nth-of-type(2) { animation-delay: -3.88s; }
.minisc__platte:nth-of-type(3) { animation-delay: -3.23s; }
.minisc__platte:nth-of-type(4) { animation-delay: -2.58s; }
@keyframes minisc-platte {
  0%        { stroke: var(--accent);        stroke-width: 1.8; }
  14%       { stroke: var(--border-strong); stroke-width: 1.2; }
  100%      { stroke: var(--border-strong); stroke-width: 1.2; }
}
.minisc__ka--1 { animation-delay: -4.53s; }
.minisc__ka--2 { animation-delay: -3.88s; }
.minisc__ka--3 { animation-delay: -3.23s; }
.minisc__ka--4 { animation-delay: -2.58s; }
@keyframes minisc-kante {
  0%        { opacity: 1;   }
  10%       { opacity: .22; }
  100%      { opacity: .22; }
}

.minisc__glyph {
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}
.minisc__spindel { stroke: var(--border-strong); stroke-width: 1.4; }
.minisc__anschluss path { stroke: var(--border-strong); stroke-width: 1.2; }

.minisc__hinab, .minisc__herauf {
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-dasharray: 7 214;
}
.minisc__hinab {
  stroke: var(--accent);
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.75));
  animation: minisc-lauf 4.8s linear infinite;
}
.minisc__herauf {
  stroke: var(--success);
  filter: drop-shadow(0 0 5px rgba(var(--success-rgb),.7));
  animation: minisc-lauf 4.8s linear infinite -2.4s;
}
@keyframes minisc-lauf {
  0%        { stroke-dashoffset: 221; opacity: 1; }
  48%       { stroke-dashoffset: 0;   opacity: 1; }
  49%       { stroke-dashoffset: 0;   opacity: 0; }
  100%      { stroke-dashoffset: 0;   opacity: 0; }
}

.mini text.minisc__name  { fill: var(--muted-strong-contrast); font-size: 11.5px; text-anchor: start; }
.mini text.minisc__sub   { fill: var(--muted); font-size: 9.5px; text-anchor: start; }
.mini text.minisc__achse { fill: var(--success); font-size: 9.5px; letter-spacing: .1em; }
.mini text.minisc__achse--oben { fill: var(--accent); }

/* --- Partner: eine Stelle vorne, der ganze Betrieb dahinter ---------------

   Vier Viertel eines Taktes von 6s. Jede Strecke hat ihr eigenes
   Strichmuster, weil sie verschieden lang sind: 76 (hin und zurueck), 155,
   95, 101 und 161 (die vier Aeste). Der Punkt wandert, indem der Versatz von
   Laenge+6 auf 0 faellt.
   ------------------------------------------------------------------------ */

.minipa__au-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .10; }
.minipa__au-b { stop-color: rgb(var(--accent-rgb)); stop-opacity: 0;   }
.minipa__mi-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .20; }
.minipa__mi-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .09; }

.minipa__haus path, .minipa__haus rect {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.3;
  stroke-linejoin: round;
}
.minipa__haus path:first-child { fill: none; }
.minipa__scheibe { stroke: var(--border-accent); stroke-width: 1.4; }
.minipa__kranz {
  fill: none;
  stroke: rgba(var(--accent-rgb),.3);
  stroke-width: 1.2;
  stroke-dasharray: 9 7;
  transform-origin: 240px 150px;
  animation: minipa-kranz 24s linear infinite;
}
@keyframes minipa-kranz { to { transform: rotate(-360deg); } }
.minipa__glyph {
  stroke: var(--accent);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}
.minipa__bereich rect {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.minipa__strecke path { stroke: var(--border-strong); stroke-width: 1.2; }

.minipa__hin, .minipa__zurueck,
.minipa__fan path, .minipa__sammeln path {
  stroke-width: 3.4;
  stroke-linecap: round;
  opacity: 0;
}
.minipa__hin        { stroke: var(--accent);  stroke-dasharray: 6 76;
                      filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.75));
                      animation: minipa-s76 6s linear infinite; }
.minipa__zurueck    { stroke: var(--success); stroke-dasharray: 6 76;
                      filter: drop-shadow(0 0 5px rgba(var(--success-rgb),.7));
                      animation: minipa-s76 6s linear infinite -1.5s; }
.minipa__fan path     { stroke: var(--accent);
                        filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.7)); }
.minipa__sammeln path { stroke: var(--success);
                        filter: drop-shadow(0 0 5px rgba(var(--success-rgb),.65)); }
.minipa__fan .minipa__fan--a { stroke-dasharray: 6 155; animation: minipa-s155 6s linear infinite -4.5s; }
.minipa__fan .minipa__fan--b { stroke-dasharray: 6 95;  animation: minipa-s95  6s linear infinite -4.5s; }
.minipa__fan .minipa__fan--c { stroke-dasharray: 6 101; animation: minipa-s101 6s linear infinite -4.5s; }
.minipa__fan .minipa__fan--d { stroke-dasharray: 6 161; animation: minipa-s161 6s linear infinite -4.5s; }
.minipa__sammeln .minipa__fan--a { stroke-dasharray: 6 155; animation: minipa-s155 6s linear infinite -3s; }
.minipa__sammeln .minipa__fan--b { stroke-dasharray: 6 95;  animation: minipa-s95  6s linear infinite -3s; }
.minipa__sammeln .minipa__fan--c { stroke-dasharray: 6 101; animation: minipa-s101 6s linear infinite -3s; }
.minipa__sammeln .minipa__fan--d { stroke-dasharray: 6 161; animation: minipa-s161 6s linear infinite -3s; }

@keyframes minipa-s76 {
  0%   { stroke-dashoffset: 82; opacity: 0; }
  2%   { opacity: 1; }
  23%  { opacity: 1; }
  25%  { stroke-dashoffset: 0;  opacity: 0; }
  100% { stroke-dashoffset: 0;  opacity: 0; }
}
@keyframes minipa-s95 {
  0%   { stroke-dashoffset: 101; opacity: 0; }
  2%   { opacity: 1; }
  23%  { opacity: 1; }
  25%  { stroke-dashoffset: 0;   opacity: 0; }
  100% { stroke-dashoffset: 0;   opacity: 0; }
}
@keyframes minipa-s101 {
  0%   { stroke-dashoffset: 107; opacity: 0; }
  2%   { opacity: 1; }
  23%  { opacity: 1; }
  25%  { stroke-dashoffset: 0;   opacity: 0; }
  100% { stroke-dashoffset: 0;   opacity: 0; }
}
@keyframes minipa-s155 {
  0%   { stroke-dashoffset: 161; opacity: 0; }
  2%   { opacity: 1; }
  23%  { opacity: 1; }
  25%  { stroke-dashoffset: 0;   opacity: 0; }
  100% { stroke-dashoffset: 0;   opacity: 0; }
}
@keyframes minipa-s161 {
  0%   { stroke-dashoffset: 167; opacity: 0; }
  2%   { opacity: 1; }
  23%  { opacity: 1; }
  25%  { stroke-dashoffset: 0;   opacity: 0; }
  100% { stroke-dashoffset: 0;   opacity: 0; }
}

.mini text.minipa__name       { fill: var(--muted-strong); font-size: 11px; }
.mini text.minipa__mittename  { fill: var(--white); font-size: 11.5px; }
.mini text.minipa__sub        { fill: var(--muted); font-size: 9.5px; }
.mini text.minipa__bname      { fill: var(--muted-strong); font-size: 10.5px; }
.mini text.minipa__klein      { fill: var(--accent); font-size: 9.5px; letter-spacing: .08em; }
.mini text.minipa__klein--gut { fill: var(--success); }


/* --- Waage: Server oder Cloud, ohne Sieger -------------------------------

   Nur der Balken dreht sich (um 240,110). Die Schalen haengen frei und
   bleiben waagrecht; ihr Weg ist 140*sin(Winkel), also bei -6 Grad 14,63
   Einheiten. Wer den Winkel aendert, rechnet die Schalen mit -- sonst reissen
   die Seile optisch ab.
   ------------------------------------------------------------------------ */

.miniwg__au-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .10; }
.miniwg__au-b { stop-color: rgb(var(--accent-rgb)); stop-opacity: 0;   }
.miniwg__sa-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .16; }
.miniwg__sa-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .07; }

.miniwg__fuss   { fill: rgba(var(--tint-rgb),.05); stroke: var(--border-strong); stroke-width: 1.3; }
.miniwg__saeule { stroke: var(--border-strong); stroke-width: 3; stroke-linecap: round; }
.miniwg__gelenk { fill: rgba(var(--accent-rgb),.2); stroke: var(--accent); stroke-width: 1.4; }

.miniwg__balken, .miniwg__zeiger {
  transform-origin: 240px 110px;
  animation: miniwg-kippen 7s var(--ease-out) infinite;
}
.miniwg__balken path   { stroke: var(--muted-strong); stroke-width: 2.6; stroke-linecap: round; }
.miniwg__balken circle { fill: var(--muted-strong); }
.miniwg__zeiger path   { stroke: var(--accent); stroke-width: 2; stroke-linecap: round; }
@keyframes miniwg-kippen {
  0%   { transform: rotate(0deg);    }
  18%  { transform: rotate(-6deg);   }
  40%  { transform: rotate(5deg);    }
  60%  { transform: rotate(-3deg);   }
  78%  { transform: rotate(1.6deg);  }
  100% { transform: rotate(0deg);    }
}
.miniwg__schale--links  { animation: miniwg-links  7s var(--ease-out) infinite; }
.miniwg__schale--rechts { animation: miniwg-rechts 7s var(--ease-out) infinite; }
@keyframes miniwg-links {
  0%   { transform: translateY(0);        }
  18%  { transform: translateY(14.6px);   }
  40%  { transform: translateY(-12.2px);  }
  60%  { transform: translateY(7.3px);    }
  78%  { transform: translateY(-3.9px);   }
  100% { transform: translateY(0);        }
}
@keyframes miniwg-rechts {
  0%   { transform: translateY(0);        }
  18%  { transform: translateY(-14.6px);  }
  40%  { transform: translateY(12.2px);   }
  60%  { transform: translateY(-7.3px);   }
  78%  { transform: translateY(3.9px);    }
  100% { transform: translateY(0);        }
}
.miniwg__seil    { stroke: var(--border-strong); stroke-width: 1.2; }
.miniwg__teller  { stroke: var(--border-accent); stroke-width: 1.3; stroke-linejoin: round; }
.miniwg__glyph   { stroke: var(--accent); stroke-width: 1.7; stroke-linecap: round;
                   stroke-linejoin: round; fill: none; }
.miniwg__skala   { stroke: var(--border-strong); stroke-width: 1.2; }

.mini text.miniwg__seite     { fill: var(--muted-strong); font-size: 11.5px; }
.mini text.miniwg__kriterium { fill: var(--muted); font-size: 9.5px; }
.mini text.miniwg__mitte     { fill: var(--accent); font-size: 11px; letter-spacing: .06em; }
.mini text.miniwg__fuss-text { fill: var(--muted); font-size: 9px; letter-spacing: .04em; }

/* --- Hybrid: zwei Orte, eine gesicherte Verbindung ----------------------- */

.minihy__au-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .10; }
.minihy__au-b { stop-color: rgb(var(--accent-rgb)); stop-opacity: 0;   }
.minihy__se-a { stop-color: rgb(var(--tint-rgb));   stop-opacity: .055; }
.minihy__se-b { stop-color: rgb(var(--tint-rgb));   stop-opacity: .015; }

.minihy__feld  { stroke: var(--border-strong); stroke-width: 1.2; }
.minihy__glyph { stroke: var(--accent); stroke-width: 1.8; stroke-linecap: round;
                 stroke-linejoin: round; fill: none; }
.minihy__zeile rect { fill: none; stroke: var(--border); stroke-width: 1.1; }
.minihy__an {
  fill: rgba(var(--accent-rgb),.2);
  stroke: var(--border-accent);
  stroke-width: 1;
  animation: minihy-an 3.4s ease-in-out infinite;
}
.minihy__an:nth-of-type(2) { animation-delay: -1.1s; }
.minihy__an:nth-of-type(3) { animation-delay: -2.2s; }
.minihy__an:nth-of-type(4) { animation-delay: -2.2s; }
@keyframes minihy-an {
  0%, 100% { opacity: .72; }
  50%      { opacity: 1;   }
}

.minihy__trasse { stroke: var(--border-strong); stroke-width: 1.3; }
.minihy__hin, .minihy__rueck {
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-dasharray: 6 112;
}
.minihy__hin {
  stroke: var(--accent);
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.75));
  animation: minihy-lauf 2.6s linear infinite;
}
.minihy__rueck {
  stroke: var(--success);
  filter: drop-shadow(0 0 5px rgba(var(--success-rgb),.7));
  animation: minihy-lauf 2.6s linear infinite -1.3s;
}
@keyframes minihy-lauf {
  from { stroke-dashoffset: 118; }
  to   { stroke-dashoffset: 0;   }
}
.minihy__ring {
  fill: rgba(var(--bg2-rgb),.92);
  stroke: var(--border-accent);
  stroke-width: 1.3;
  transform-origin: 240px 90px;
  animation: minihy-ring 2.6s ease-out infinite;
}
@keyframes minihy-ring {
  0%       { transform: scale(1);    stroke: var(--accent); }
  22%      { transform: scale(1.1);  }
  60%,100% { transform: scale(1);    stroke: var(--border-accent); }
}
.minihy__schloss rect, .minihy__schloss path {
  stroke: var(--accent);
  stroke-width: 1.5;
  fill: none;
  stroke-linejoin: round;
}

.mini text.minihy__ort          { fill: var(--muted-strong-contrast); font-size: 12px; }
.mini text.minihy__ortsub       { fill: var(--muted); font-size: 9px; }
.mini text.minihy__sache        { fill: var(--muted-strong); font-size: 9.5px; }
.mini text.minihy__trassentext  { fill: var(--accent); font-size: 9px; letter-spacing: .06em; }
.mini text.minihy__fuss         { fill: var(--muted); font-size: 10px; letter-spacing: .04em; }

/* --- Private Cloud: ein eigener Bereich im Saal -------------------------- */

.minipc__au-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .11; }
.minipc__au-b { stop-color: rgb(var(--accent-rgb)); stop-opacity: 0;   }
.minipc__ei-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .10; }
.minipc__ei-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .05; }

.minipc__saalrand {
  stroke: var(--border);
  stroke-width: 1.1;
  stroke-dasharray: 6 6;
}
.minipc__eigen { stroke: none; }
/* Die Umrandung laeuft um -- so sieht man, dass die Grenze aktiv ist und
   nicht nur eingezeichnet. */
.minipc__zaun {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-dasharray: 12 7;
  animation: minipc-zaun 3.2s linear infinite;
}
@keyframes minipc-zaun { to { stroke-dashoffset: -38; } }

.minipc__schrank rect {
  fill: var(--rack-2);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.minipc__einschub rect { fill: var(--rack-1); stroke: var(--border); stroke-width: 1; }
.minipc__led circle    { fill: var(--success); }
.minipc__l--a { animation: minipc-led 2.6s ease-in-out infinite; }
.minipc__l--b { animation: minipc-led 3.3s ease-in-out infinite -0.9s; }
.minipc__l--c { animation: minipc-led 2.9s ease-in-out infinite -1.8s; }
@keyframes minipc-led { 0%,100% { opacity: .35; } 50% { opacity: 1; } }

.minipc__fremd rect {
  fill: rgba(var(--tint-rgb),.02);
  stroke: var(--border);
  stroke-width: 1;
}
/* Der Weg, das Tor und der laufende Punkt standen bis 1.7.3 hier. Die
   Zeichnung zeigt seit 1.7.4 keinen Zugang mehr -- siehe den Kopf von
   part-mini-privatecloud.php. Regeln ohne Markup sind Ballast und verleiten
   beim naechsten Mal zu der Annahme, es gaebe das Element noch. */

/* Die Fahne traegt ihre eigenen Farben, nicht die der Marke -- eine Fahne in
   Hausfarben ist keine Fahne. Deshalb stehen hier feste Werte und keine
   Tokens. Rot nach der amtlichen Beschreibung (#ED2939), Weiss echtes Weiss;
   der duenne Rand gibt dem weissen Streifen auch auf hellem Grund eine
   Kante. Nur die drei Streifen, kein Wappen. */
.minipc__fahne-rot   { fill: #ED2939; }
.minipc__fahne-weiss { fill: #FFFFFF; }
.minipc__fahne-rand  { fill: none; stroke: var(--border-strong); stroke-width: 1; }

/* Die Fuehrungsstriche von der Rahmenunterkante zur Beschriftung. Sie
   ersetzen, was bis 1.7.3 die Platzierung im Rahmen geleistet hat: die
   Zuordnung von Wort zu Flaeche. */
.minipc__fuehrung { stroke: var(--border-strong); stroke-width: 1; }

.mini text.minipc__saal       { fill: var(--muted); font-size: 10px; letter-spacing: .14em;
                                text-anchor: start; }
.mini text.minipc__eigenname  { fill: var(--white); font-size: 12px; }
.mini text.minipc__eigensub   { fill: var(--muted-strong); font-size: 9px; }
.mini text.minipc__fremdtext  { fill: var(--muted); font-size: 9px; }

/* --- Cloud-Modelle: drei Spalten, kein Sieger ---------------------------- */

.minicm__sp-a { stop-color: rgb(var(--tint-rgb)); stop-opacity: .05; }
.minicm__sp-b { stop-color: rgb(var(--tint-rgb)); stop-opacity: .015; }

.minicm__spalte { stroke: var(--border-strong); stroke-width: 1.2; }
.minicm__zaun {
  fill: none;
  stroke: rgba(var(--accent-rgb),.5);
  stroke-width: 1.3;
  stroke-dasharray: 9 6;
}
.minicm__kasten rect   { fill: var(--rack-2); stroke: var(--border-strong); stroke-width: 1.1; }
.minicm__led circle    { fill: var(--success); }
.minicm__l--a { animation: minipc-led 2.6s ease-in-out infinite; }
.minicm__l--b { animation: minipc-led 3.3s ease-in-out infinite -0.9s; }
.minicm__l--c { animation: minipc-led 2.9s ease-in-out infinite -1.8s; }

/* Umfang des Rechtecks 88x76 mit Radius 6: 2*(88-12) + 2*(76-12) + 2*pi*6
   = 152 + 128 + 37,7 = rund 318. */
.minicm__bahn { fill: none; stroke: var(--border); stroke-width: 1.1; }
.minicm__kreisen {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-dasharray: 26 292;
  filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.65));
  animation: minicm-kreisen 4.4s linear infinite;
}
@keyframes minicm-kreisen {
  from { stroke-dashoffset: 318; }
  to   { stroke-dashoffset: 0;   }
}

.minicm__glyph { stroke: var(--accent); stroke-width: 1.7; stroke-linecap: round;
                 stroke-linejoin: round; fill: none; }
.minicm__trasse { stroke: var(--border-strong); stroke-width: 1.2; }
.minicm__hin, .minicm__rueck {
  stroke-width: 3.2;
  stroke-linecap: round;
  stroke-dasharray: 5 32;
}
.minicm__hin   { stroke: var(--accent);  animation: minicm-pendeln 2.2s linear infinite;
                 filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.7)); }
.minicm__rueck { stroke: var(--success); animation: minicm-pendeln 2.2s linear infinite -1.1s;
                 filter: drop-shadow(0 0 4px rgba(var(--success-rgb),.65)); }
@keyframes minicm-pendeln {
  from { stroke-dashoffset: 37; }
  to   { stroke-dashoffset: 0;  }
}

.minicm__regen path { stroke: var(--border); stroke-width: 1.1; stroke-dasharray: 3 4; }
.minicm__tropfen path {
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 5 34;
  filter: drop-shadow(0 0 4px rgba(var(--accent-rgb),.7));
  animation: minicm-tropfen 1.9s linear infinite;
}
.minicm__t--b { animation-delay: -0.63s; }
.minicm__t--c { animation-delay: -1.26s; }
@keyframes minicm-tropfen {
  from { stroke-dashoffset: 39; }
  to   { stroke-dashoffset: 0;  }
}
.minicm__platz rect {
  fill: rgba(var(--accent-rgb),.08);
  stroke: var(--border-strong);
  stroke-width: 1.1;
}

.mini text.minicm__kopf        { fill: var(--white); font-size: 11.5px; }
.mini text.minicm__satz        { fill: var(--muted-strong); font-size: 10px; }
.mini text.minicm__wo          { fill: var(--accent); font-size: 9px; letter-spacing: .04em; }
.mini text.minicm__wo--zeile2  { fill: var(--muted); }
.mini text.minicm__klein       { fill: var(--muted); font-size: 9px; text-anchor: end; }
.mini text.minicm__klein--rechts { text-anchor: end; }
.mini text.minicm__fuss        { fill: var(--muted); font-size: 9.5px; letter-spacing: .03em; }


/* --- Managed Server: drei Bahnen um die Maschine -------------------------

   Umfaenge: r=78 -> 490,1 · r=104 -> 653,5 · r=130 -> 816,8. Die Strichmuster
   sind daraus gerechnet. Der Kopf laeuft dem Schweif voraus: dasselbe
   Strichmuster, aber der Versatz ist um die Schweiflaenge minus Kopflaenge
   verschoben. Wer einen Radius aendert, rechnet beide Zahlen neu (2*pi*r).
   Die drei Umlaufzeiten sind absichtlich verschieden -- Ueberwachung laeuft
   dauernd, eine Sicherung in groesseren Abstaenden.
   ------------------------------------------------------------------------ */

.minims__au-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .12; }
.minims__au-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .05; }
.minims__au-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minims__ke-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .16; }
.minims__ke-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .07; }

.minims__bahn { stroke: var(--border); stroke-width: 1.1; }
.minims__zug, .minims__kopf { fill: none; stroke-linecap: butt; }
.minims__zug  { stroke: rgba(var(--accent-rgb),.42); stroke-width: 2.2; }
.minims__kopf { stroke: var(--accent); stroke-width: 3;
                filter: drop-shadow(0 0 6px rgba(var(--accent-rgb),.8)); }

.minims__zug--1  { stroke-dasharray: 40 450.1; animation: minims-z1 14s linear infinite; }
.minims__kopf--1 { stroke-dasharray: 7 483.1;  animation: minims-k1 14s linear infinite; }
.minims__zug--2  { stroke-dasharray: 40 613.5; animation: minims-z2 6s  linear infinite; }
.minims__kopf--2 { stroke-dasharray: 7 646.5;  animation: minims-k2 6s  linear infinite; }
.minims__zug--3  { stroke-dasharray: 40 776.8; animation: minims-z3 22s linear infinite; }
.minims__kopf--3 { stroke-dasharray: 7 809.8;  animation: minims-k3 22s linear infinite; }
@keyframes minims-z1 { from { stroke-dashoffset: 490.1; } to { stroke-dashoffset: 0; } }
@keyframes minims-k1 { from { stroke-dashoffset: 457.1; } to { stroke-dashoffset: -33; } }
@keyframes minims-z2 { from { stroke-dashoffset: 653.5; } to { stroke-dashoffset: 0; } }
@keyframes minims-k2 { from { stroke-dashoffset: 620.5; } to { stroke-dashoffset: -33; } }
@keyframes minims-z3 { from { stroke-dashoffset: 816.8; } to { stroke-dashoffset: 0; } }
@keyframes minims-k3 { from { stroke-dashoffset: 783.8; } to { stroke-dashoffset: -33; } }

.minims__kern { stroke: var(--border-accent); stroke-width: 1.4; }
.minims__einschub rect { fill: var(--rack-1); stroke: var(--border); stroke-width: 1; }
.minims__led circle { fill: var(--success); }
.minims__l--a { animation: minipc-led 2.6s ease-in-out infinite; }
.minims__l--b { animation: minipc-led 3.3s ease-in-out infinite -0.9s; }
.minims__l--c { animation: minipc-led 2.9s ease-in-out infinite -1.8s; }

.minims__punkt { fill: var(--accent); }
.minims__punkt--1 { animation: minims-punkt 14s linear infinite; }
.minims__punkt--2 { animation: minims-punkt 6s  linear infinite; }
.minims__punkt--3 { animation: minims-punkt 22s linear infinite; }
@keyframes minims-punkt {
  0%        { opacity: 1;   }
  8%        { opacity: .35; }
  100%      { opacity: .35; }
}

.mini text.minims__kernname { fill: var(--white); font-size: 12px; }
.mini text.minims__legname  { fill: var(--muted-strong-contrast); font-size: 11.5px; text-anchor: start; }
.mini text.minims__legsub   { fill: var(--muted); font-size: 9px; text-anchor: start; }
.mini text.minims__fuss     { fill: var(--muted); font-size: 9.5px; letter-spacing: .04em; }

/* --- Virtuelle Server: eine Maschine zieht um, ohne stehenzubleiben ------ */

.minivs__wi-a { stop-color: rgb(var(--tint-rgb)); stop-opacity: .05; }
.minivs__wi-b { stop-color: rgb(var(--tint-rgb)); stop-opacity: .015; }
.minivs__vm-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .20; }
.minivs__vm-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .10; }

.minivs__wirt { stroke: var(--border-strong); stroke-width: 1.2; }
.minivs__fach rect {
  /* --rack-1 und nicht rgba(--shade-rgb): der Schattenton gehoert in
     Schatten. Als Flaeche ergibt er im hellen Thema ein mittleres Grau, und
     aus dem leeren Fach wird ein gefuellter Balken -- also das Gegenteil von
     dem, was es zeigen soll. */
  fill: var(--rack-1);
  stroke: var(--border);
  stroke-width: 1;
}
.minivs__leer {
  fill: none;
  stroke: rgba(var(--accent-rgb),.4);
  stroke-width: 1.2;
  stroke-dasharray: 6 5;
}
.minivs__vm rect { stroke: var(--border-accent); stroke-width: 1.2; }
.minivs__bahn {
  stroke: rgba(var(--accent-rgb),.3);
  stroke-width: 1.2;
  stroke-dasharray: 5 5;
}
.minivs__led { fill: var(--success); }
.minivs__l--a { animation: minipc-led 2.6s ease-in-out infinite; }
.minivs__l--b { animation: minipc-led 3.3s ease-in-out infinite -0.9s; }
.minivs__l--c { animation: minipc-led 2.9s ease-in-out infinite -1.8s; }
/* Die Lampe der wandernden Maschine blinkt waehrend der ganzen Fahrt weiter.
   Das ist die Aussage des Bildes, nicht Schmuck. */
.minivs__ledzug { animation: minipc-led 1.6s ease-in-out infinite; }

.minivs__zugkasten { stroke: var(--accent); stroke-width: 1.6; }
.minivs__zug {
  filter: drop-shadow(0 6px 18px rgba(var(--shade-rgb),.6));
  animation: minivs-umzug 9s var(--spring) infinite;
}
/* Nach dem Umzug steht die Maschine lange drueben -- das ist der Zustand, den
   man sehen soll. Der Ruecksprung passiert unsichtbar in einem halben
   Prozent des Taktes; ein sichtbarer Rueckweg wuerde das Gegenteil von dem
   zeigen, worum es geht. */
@keyframes minivs-umzug {
  0%, 8%  { transform: translate(0, 0);           opacity: 1; }
  18%     { transform: translate(0, -60px);       opacity: 1; }
  42%     { transform: translate(244px, -60px);   opacity: 1; }
  52%     { transform: translate(244px, 36px);    opacity: 1; }
  90%     { transform: translate(244px, 36px);    opacity: 1; }
  96%     { transform: translate(244px, 36px);    opacity: 0; }
  97%     { transform: translate(0, 0);           opacity: 0; }
  100%    { transform: translate(0, 0);           opacity: 1; }
}

.mini text.minivs__wirtname    { fill: var(--muted-strong); font-size: 11px; letter-spacing: .08em; }
.mini text.minivs__vmname      { fill: var(--muted-strong); font-size: 10px; text-anchor: start; }
.mini text.minivs__vmname--hell { fill: var(--white); }
.mini text.minivs__hinweis     { fill: var(--success); font-size: 9.5px; letter-spacing: .06em; }
.mini text.minivs__fuss        { fill: var(--muted); font-size: 9.5px; }

/* --- Business-Hosting: was ankommt und was mitlaeuft ---------------------

   Takt 6s, drei Dienste. Jeder hat ein Drittel: Anfrage faellt von 0 bis 15%,
   Antwort steigt von 15 bis 30%. Die Verzoegerungen 0, -4s und -2s lassen sie
   bei 0s, 2s und 4s starten.
   ------------------------------------------------------------------------ */

.minihs__pl-a { stop-color: rgb(var(--tint-rgb)); stop-opacity: .055; }
.minihs__pl-b { stop-color: rgb(var(--tint-rgb)); stop-opacity: .015; }
.minihs__ba-a { stop-color: rgb(var(--success-rgb)); stop-opacity: .05; }
.minihs__ba-b { stop-color: rgb(var(--success-rgb)); stop-opacity: .13; }

.minihs__platte { stroke: var(--border-strong); stroke-width: 1.2; }
.minihs__quelle rect {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.1;
}
.minihs__weg path { stroke: var(--border-strong); stroke-width: 1.1; stroke-dasharray: 3 4; }
.minihs__anfrage path, .minihs__antwort path {
  stroke-width: 3.2;
  stroke-linecap: round;
  stroke-dasharray: 5 44;
  opacity: 0;
}
.minihs__anfrage path {
  stroke: var(--accent);
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.75));
  animation: minihs-hin 6s linear infinite;
}
.minihs__antwort path {
  stroke: var(--success);
  filter: drop-shadow(0 0 5px rgba(var(--success-rgb),.7));
  animation: minihs-zurueck 6s linear infinite;
}
.minihs__anfrage .minihs__a--2, .minihs__antwort .minihs__a--2 { animation-delay: -4s; }
.minihs__anfrage .minihs__a--3, .minihs__antwort .minihs__a--3 { animation-delay: -2s; }
@keyframes minihs-hin {
  0%   { stroke-dashoffset: 49; opacity: 1; }
  15%  { stroke-dashoffset: 0;  opacity: 1; }
  16%  { stroke-dashoffset: 0;  opacity: 0; }
  100% { stroke-dashoffset: 0;  opacity: 0; }
}
@keyframes minihs-zurueck {
  0%, 15% { stroke-dashoffset: 49; opacity: 0; }
  16%     { stroke-dashoffset: 48; opacity: 1; }
  30%     { stroke-dashoffset: 0;  opacity: 1; }
  31%     { stroke-dashoffset: 0;  opacity: 0; }
  100%    { stroke-dashoffset: 0;  opacity: 0; }
}

.minihs__dienst rect {
  fill: rgba(var(--accent-rgb),.07);
  stroke: var(--border-strong);
  stroke-width: 1.2;
  animation: minihs-dienst 6s linear infinite;
}
.minihs__d--2 { animation-delay: -4s; }
.minihs__d--3 { animation-delay: -2s; }
@keyframes minihs-dienst {
  0%, 12%   { stroke: var(--border-strong); stroke-width: 1.2; }
  17%       { stroke: var(--accent);        stroke-width: 2.2; }
  42%, 100% { stroke: var(--border-strong); stroke-width: 1.2; }
}
.minihs__glyph g {
  stroke: var(--accent);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.minihs__bandrand { fill: none; stroke: rgba(var(--success-rgb),.4); stroke-width: 1.2; }
.minihs__sweep rect { fill: rgba(var(--success-rgb),.14); }
.minihs__sweep { animation: minihs-sweep 7s linear infinite; }
@keyframes minihs-sweep {
  from { transform: translateX(40px);  }
  to   { transform: translateX(370px); }
}
.minihs__trenner { stroke: rgba(var(--success-rgb),.3); stroke-width: 1.1; }

.mini text.minihs__oben        { fill: var(--muted); font-size: 10px; letter-spacing: .12em; }
.mini text.minihs__qname       { fill: var(--muted-strong); font-size: 10px; }
.mini text.minihs__plattename  { fill: var(--accent); font-size: 10px; letter-spacing: .1em;
                                 text-anchor: start; }
.mini text.minihs__dname       { fill: var(--muted-strong-contrast); font-size: 11px; }
.mini text.minihs__bname       { fill: var(--success); font-size: 10.5px; }
.mini text.minihs__fuss        { fill: var(--muted); font-size: 9.5px; }

/* --- Serverplatz: was ein Rechenzentrum beisteuert ----------------------- */

.minisv__au-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .10; }
.minisv__au-b { stop-color: rgb(var(--accent-rgb)); stop-opacity: 0;   }
.minisv__me-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .4;  }
.minisv__me-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .22; }

.minisv__schrank {
  fill: var(--rack-2);
  stroke: var(--border-strong);
  stroke-width: 1.3;
}
.minisv__einschub rect { fill: var(--rack-1); stroke: var(--border); stroke-width: 1; }
.minisv__meins     { stroke: none; }
.minisv__meinsrand {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.5;
  filter: drop-shadow(0 0 7px rgba(var(--accent-rgb),.5));
}
.minisv__meinsled { fill: var(--success); animation: minipc-led 1.8s ease-in-out infinite; }

.minisv__zulauf path { stroke: var(--border-strong); stroke-width: 1.2; }
.minisv__puls path {
  stroke: var(--accent);
  stroke-width: 3.2;
  stroke-linecap: round;
  stroke-dasharray: 5 114;
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.7));
  animation: minisv-puls 4.4s linear infinite;
}
.minisv__p--2 { animation-delay: -1.1s; }
.minisv__p--3 { animation-delay: -2.2s; }
.minisv__p--4 { animation-delay: -3.3s; }
@keyframes minisv-puls {
  0%   { stroke-dashoffset: 119; opacity: 1; }
  55%  { stroke-dashoffset: 0;   opacity: 1; }
  56%  { stroke-dashoffset: 0;   opacity: 0; }
  100% { stroke-dashoffset: 0;   opacity: 0; }
}
.minisv__marke circle {
  fill: rgba(var(--accent-rgb),.10);
  stroke: var(--border-accent);
  stroke-width: 1.3;
}
.minisv__glyph g {
  stroke: var(--accent);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.mini text.minisv__meinsname { fill: var(--white); font-size: 11.5px; }
.mini text.minisv__name      { fill: var(--muted-strong-contrast); font-size: 11px; }
.mini text.minisv__sub       { fill: var(--muted); font-size: 9px; }
.mini text.minisv__name--l, .mini text.minisv__sub--l { text-anchor: middle; }
.mini text.minisv__name--r, .mini text.minisv__sub--r { text-anchor: middle; }
.mini text.minisv__fuss      { fill: var(--muted); font-size: 9.5px; }


/* --- Support: der Weg einer Meldung -------------------------------------

   Takt 8s. Die drei Meldewege pulsen in den ersten 12%, danach laeuft der
   Punkt das Gleis entlang (403 Einheiten) bis 70%. Die Stationen liegen auf
   Weglaenge 157, 231, 305 und 379 -- daraus sind die Prozentwerte unten
   gerechnet. Wer eine Station verschiebt, rechnet mit:
   12% + 58% * (Weglaenge/403).
   ------------------------------------------------------------------------ */

.minisp__bk-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .55; }
.minisp__bk-b { stop-color: rgb(var(--success-rgb)); stop-opacity: .75; }

.minisp__kanal rect {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.1;
}
.minisp__zulauf path, .minisp__gleis {
  stroke: var(--border-strong);
  stroke-width: 1.2;
  fill: none;
}
.minisp__melden path {
  stroke: var(--accent);
  stroke-width: 3.2;
  stroke-linecap: round;
  fill: none;
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.7));
  animation: minisp-melden84 8s linear infinite;
}
.minisp__m--2 { stroke-dasharray: 5 48; animation-name: minisp-melden48; }
.minisp__m--1, .minisp__m--3 { stroke-dasharray: 5 84; }
.minisp__m--3 { animation-delay: -0.3s; }
@keyframes minisp-melden84 {
  0%   { stroke-dashoffset: 89; opacity: 1; }
  12%  { stroke-dashoffset: 0;  opacity: 1; }
  13%  { stroke-dashoffset: 0;  opacity: 0; }
  100% { stroke-dashoffset: 0;  opacity: 0; }
}
@keyframes minisp-melden48 {
  0%   { stroke-dashoffset: 53; opacity: 1; }
  12%  { stroke-dashoffset: 0;  opacity: 1; }
  13%  { stroke-dashoffset: 0;  opacity: 0; }
  100% { stroke-dashoffset: 0;  opacity: 0; }
}

.minisp__lauf {
  stroke: var(--accent);
  stroke-width: 4;
  stroke-linecap: round;
  fill: none;
  stroke-dasharray: 6 403;
  filter: drop-shadow(0 0 6px rgba(var(--accent-rgb),.8));
  animation: minisp-lauf 8s linear infinite;
}
@keyframes minisp-lauf {
  0%, 12% { stroke-dashoffset: 409; opacity: 0; }
  13%     { stroke-dashoffset: 403; opacity: 1; }
  70%     { stroke-dashoffset: 0;   opacity: 1; }
  71%     { stroke-dashoffset: 0;   opacity: 0; }
  100%    { stroke-dashoffset: 0;   opacity: 0; }
}

.minisp__balkenbett { fill: rgba(var(--tint-rgb),.06); }
.minisp__balken {
  transform-origin: 214px 0;
  animation: minisp-balken 8s linear infinite;
}
@keyframes minisp-balken {
  0%, 34.6% { transform: scaleX(0); }
  66.5%     { transform: scaleX(1); }
  94%       { transform: scaleX(1); }
  100%      { transform: scaleX(0); }
}

.minisp__station circle {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.minisp__st--1 { animation: minisp-st1 8s linear infinite; }
@keyframes minisp-st1 {
  0%, 34.6%      { stroke: var(--border-strong); stroke-width: 1.2; }
  36.6%        { stroke: var(--accent);        stroke-width: 2.4; }
  39.6%, 100%  { stroke: var(--border-accent); stroke-width: 1.6; }
}
.minisp__st--2 { animation: minisp-st2 8s linear infinite; }
@keyframes minisp-st2 {
  0%, 45.2%      { stroke: var(--border-strong); stroke-width: 1.2; }
  47.2%        { stroke: var(--accent);        stroke-width: 2.4; }
  50.2%, 100%  { stroke: var(--border-accent); stroke-width: 1.6; }
}
.minisp__st--3 { animation: minisp-st3 8s linear infinite; }
@keyframes minisp-st3 {
  0%, 55.9%      { stroke: var(--border-strong); stroke-width: 1.2; }
  57.9%        { stroke: var(--accent);        stroke-width: 2.4; }
  60.9%, 100%  { stroke: var(--border-accent); stroke-width: 1.6; }
}
.minisp__st--4 { animation: minisp-st4 8s linear infinite; }
@keyframes minisp-st4 {
  0%, 66.5%      { stroke: var(--border-strong); stroke-width: 1.2; }
  68.5%        { stroke: var(--accent);        stroke-width: 2.4; }
  71.5%, 100%  { stroke: var(--border-accent); stroke-width: 1.6; }
}
.minisp__glyph g {
  stroke: var(--accent);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}
.mini text.minisp__kopf  { fill: var(--muted); font-size: 10px; letter-spacing: .12em; }
.mini text.minisp__kname { fill: var(--muted-strong); font-size: 10.5px; }
.mini text.minisp__sname { fill: var(--muted-strong-contrast); font-size: 9.5px; }
.mini text.minisp__fuss  { fill: var(--muted); font-size: 9.5px; }

/* --- Managed IT Services: die Betreuungshuelle --------------------------

   Dieselbe Bildsprache wie die Firewall-Zeichnung -- Koerper mit Verlauf,
   Kantenlicht, feines Raster, eine Blende je Runde --, aber eine andere
   Aussage: Das Schild steht ZWISCHEN zwei Seiten, die Kuppel UMSCHLIESST
   etwas. Deshalb liegt der Sockel hier unten und nicht als Kern in der
   Mitte: Eine Kuppel steht auf, ein Schild schwebt.

   Die eine Bewegung ist der Lichtpunkt, der die Huelle abfaehrt. Mehr
   bewegt sich nicht; die Zeichnung zeigt einen Zustand, keinen Vorgang.
   ------------------------------------------------------------------------ */

.minimd__au-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .14; }
.minimd__au-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .06; }
.minimd__au-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minimd__ko-a { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .40; }
.minimd__ko-b { stop-color: rgb(var(--accent-rgb));  stop-opacity: .19; }
.minimd__ko-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: .07; }
.minimd__so-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minimd__so-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .28; }
.minimd__bl-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minimd__bl-b { stop-color: rgb(var(--accent-rgb));  stop-opacity: .26; }
.minimd__bl-c { stop-color: rgb(var(--accent-rgb));  stop-opacity: 0;   }
.minimd__sc-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .34; }
.minimd__sc-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .14; }

/* Der Weichzeichner liegt als eigene Kopie darunter, damit die Kante scharf
   bleibt -- ein filter auf dem Koerper selbst weicht sie auf. */
.minimd__glanz {
  fill: rgba(var(--accent-rgb),.13);
  stroke: none;
  filter: blur(16px);
}
.minimd__koerper {
  /* Bewusst nicht die volle Akzentfarbe: Der Lichtpunkt laeuft auf derselben
     Linie, und auf einer schon hellen Kante ist er nicht zu sehen. */
  stroke: rgba(var(--accent-rgb),.6);
  stroke-width: 1.6;
  stroke-linejoin: round;
}
.minimd__raster path { stroke: rgba(var(--accent-rgb),.19); stroke-width: .7; }
.minimd__sockel { stroke: none; }

.minimd__blende { animation: minimd-blende 5.4s var(--ease-out) infinite; }
@keyframes minimd-blende {
  0%, 10%   { transform: translateX(20px);  }
  52%, 100% { transform: translateX(360px); }
}

/* Kantenlicht oben links. Ohne diesen Strich bleibt die Kante flach. */
.minimd__kante {
  stroke: rgba(var(--accent-rgb),.55);
  stroke-width: 1.6;
  stroke-linecap: round;
}
.minimd__kante--flanke { stroke: rgba(var(--accent-rgb),.28); }

/* Die Geraete darin. Sie sind NICHT abgedunkelt: Es geht nicht darum, dass
   sie geschuetzt werden, sondern dass sie laufen. */
.minimd__geraet {
  fill: var(--rack-2);
  stroke: var(--border-strong);
  stroke-width: 1.2;
}
.minimd__einschub rect { fill: var(--rack-1); stroke: var(--border); stroke-width: 1; }
.minimd__schirm { fill: rgba(var(--accent-rgb),.12); stroke: var(--border-accent); stroke-width: 1; }
.minimd__fuss   { stroke: var(--border-strong); stroke-width: 1.6; stroke-linecap: round; }
.minimd__port circle { fill: rgba(var(--accent-rgb),.5); }
.minimd__led circle  { fill: var(--success); }
.minimd__l--a { animation: minipc-led 2.6s ease-in-out infinite; }
.minimd__l--b { animation: minipc-led 3.3s ease-in-out infinite -0.9s; }
.minimd__l--c { animation: minipc-led 2.9s ease-in-out infinite -1.8s; }
.minimd__bus { stroke: var(--border-strong); stroke-width: 1.2; }
.minimd__knoten circle { fill: var(--border-accent); }

.minimd__platte {
  fill: rgba(var(--tint-rgb),.07);
  stroke: var(--border-strong);
  stroke-width: 1;
}

/* Der Umlauf. Die halbe Ellipse ist rund 361 Einheiten lang; Schweif und
   Punkt sind Strichmuster auf derselben Bahn, damit beide exakt denselben
   Weg nehmen. Wer rx oder ry aendert, rechnet die Laenge neu -- sonst
   springt der Punkt am Ende der Runde. */
.minimd__spur    { stroke: rgba(var(--accent-rgb),.22); stroke-width: 1.2; }
.minimd__schweif {
  stroke: rgba(var(--accent-rgb),.5);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 24 337;
  animation: minimd-lauf 9s linear infinite;
}
.minimd__punkt {
  stroke: var(--accent);
  stroke-width: 4.6;
  stroke-linecap: round;
  stroke-dasharray: 5 356;
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.8));
  animation: minimd-lauf 9s linear infinite;
}
@keyframes minimd-lauf {
  from { stroke-dashoffset: 361; }
  to   { stroke-dashoffset: 0;   }
}

/* Die drei Taetigkeiten. Der Leitstrich geht von der Beschriftung bis an den
   Rand der Scheibe, nicht bis zu ihrer Mitte -- sonst steht er im Zeichen. */
.minimd__leiter { stroke: var(--border-strong); stroke-width: 1; }
.minimd__scheibe { stroke: var(--border-accent); stroke-width: 1.4; }
.minimd__glyph {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.mini text.minimd__tat        { fill: var(--muted-strong-contrast); font-size: 11px; }
.mini text.minimd__systemname { fill: var(--white); font-size: 11.5px; letter-spacing: .04em; }
/* --muted-strong statt --muted: nachgemessen 4,55:1 im hellen Thema gegen
   den Lichthof der Zeichnung -- das haelt die Grenze, aber ohne Luft. Mit
   --muted-strong sind es 7,4. */
.mini text.minimd__fussnote   { fill: var(--muted-strong); font-size: 9.5px; }

/* --- Wartungsplan: vier Wochen als Raster -------------------------------

   Das wandernde Feld entsteht aus zwei ineinandergesteckten Bewegungen: innen
   sieben Schritte nach rechts in 4,2s, aussen vier Schritte nach unten in
   16,8s -- also genau viermal so lang. Daraus ergibt sich Zeile fuer Zeile
   von selbst; eine einzelne Kurve mit 28 Stuetzpunkten waere nicht zu pflegen.
   ------------------------------------------------------------------------ */

.miniwp__fe-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .3; }
.miniwp__fe-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .16; }

.miniwp__raster rect { fill: rgba(var(--tint-rgb),.025); stroke: var(--border); stroke-width: 1; }
.miniwp__taeglich circle { fill: var(--accent); opacity: .6; }
.miniwp__woche rect { fill: var(--success); opacity: .75; }
.miniwp__monat rect { fill: rgba(var(--warn-rgb),.12); stroke: rgba(var(--warn-rgb),.5);
                      stroke-width: 1.2; }
.miniwp__monat path { stroke: var(--warn); stroke-width: 1.8; stroke-linecap: round; }

.miniwp__zeiger   { animation: miniwp-ab 16.8s steps(4) infinite; }
.miniwp__zeiger-x { animation: miniwp-quer 4.2s steps(7) infinite; }
.miniwp__zeiger rect { stroke: var(--accent); stroke-width: 1.6;
                       filter: drop-shadow(0 0 7px rgba(var(--accent-rgb),.45)); }
@keyframes miniwp-ab   { from { transform: translateY(0); } to { transform: translateY(176px); } }
@keyframes miniwp-quer { from { transform: translateX(0); } to { transform: translateX(308px); } }

.miniwp__ltag   { fill: var(--accent); opacity: .7; }
.miniwp__lwoche { fill: var(--success); opacity: .8; }
.miniwp__lmonat { stroke: var(--warn); stroke-width: 1.8; stroke-linecap: round; }

.mini text.miniwp__kopf  { fill: var(--muted); font-size: 10px; letter-spacing: .12em; }
.mini text.miniwp__lname { fill: var(--muted-strong-contrast); font-size: 11px; }
.mini text.miniwp__lsub  { fill: var(--muted); font-size: 9px; }
.mini text.miniwp__fuss  { fill: var(--muted-strong); font-size: 10px; }
.mini text.miniwp__fuss--leise { fill: var(--muted); font-size: 9px; }

/* --- Schutz: Schichten, ein Angriff und ein berechtigter Zugriff ---------

   Laengen: der Angriffsweg 128,5 Einheiten, der berechtigte Weg 218,4. Beide
   sind aus den Endpunkten gerechnet (Satz des Pythagoras); wer einen Punkt
   verschiebt, muss die Strichmuster neu rechnen, sonst bleibt der Punkt
   stehen oder schiesst ueber.
   ------------------------------------------------------------------------ */

.minisz__ke-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .26; }
.minisz__ke-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .08; }

.minisz__ring { fill: none; stroke-width: 1.3; }
.minisz__ring--4 { stroke: rgba(var(--accent-rgb),.22); }
.minisz__ring--3 { stroke: rgba(var(--accent-rgb),.3); }
.minisz__ring--2 { stroke: rgba(var(--accent-rgb),.38); animation: minisz-getroffen 5s linear infinite; }
.minisz__ring--1 { stroke: rgba(var(--accent-rgb),.5); }
@keyframes minisz-getroffen {
  0%, 38%   { stroke: rgba(var(--accent-rgb),.38); stroke-width: 1.3; }
  42%       { stroke: var(--danger);                stroke-width: 2.4; }
  58%, 100% { stroke: rgba(var(--accent-rgb),.38); stroke-width: 1.3; }
}
.minisz__kern { stroke: var(--border-accent); stroke-width: 1.4; }

.minisz__wegrot, .minisz__weggut { stroke-width: 1.2; stroke-dasharray: 4 4; }
.minisz__wegrot { stroke: rgba(var(--danger-rgb),.35); }
.minisz__weggut { stroke: rgba(var(--success-rgb),.35); }

.minisz__angriff {
  stroke: var(--danger);
  stroke-width: 3.6;
  stroke-linecap: round;
  /* 7 Einheiten Puls, danach so viel Luecke, dass nur ein Punkt unterwegs
     ist. Der Weg ist 113,1 Einheiten lang (80 x 80, also 45 Grad); 7 + 107
     deckt ihn knapp ab. Aendert sich der Weg, muessen diese Zahlen mit --
     sonst laeuft der Punkt ins Leere oder es sind zwei. */
  stroke-dasharray: 7 107;
  filter: drop-shadow(0 0 5px rgba(var(--danger-rgb),.75));
  animation: minisz-angriff 5s linear infinite;
}
@keyframes minisz-angriff {
  0%      { stroke-dashoffset: 114; opacity: 1; }
  38%     { stroke-dashoffset: 0;   opacity: 1; }
  40%     { stroke-dashoffset: 0;   opacity: 0; }
  100%    { stroke-dashoffset: 0;   opacity: 0; }
}
.minisz__zugriff {
  stroke: var(--success);
  stroke-width: 3.6;
  stroke-linecap: round;
  /* Weg 190,9 Einheiten lang (136 x 134). Siehe oben. */
  stroke-dasharray: 7 184;
  filter: drop-shadow(0 0 5px rgba(var(--success-rgb),.7));
  animation: minisz-zugriff 5s linear infinite;
}
@keyframes minisz-zugriff {
  0%, 10% { stroke-dashoffset: 191; opacity: 0; }
  11%     { stroke-dashoffset: 189; opacity: 1; }
  54%     { stroke-dashoffset: 0;   opacity: 1; }
  56%     { stroke-dashoffset: 0;   opacity: 0; }
  100%    { stroke-dashoffset: 0;   opacity: 0; }
}
.minisz__welle {
  fill: none;
  stroke: var(--danger);
  stroke-width: 2;
  transform-origin: 108px 188px;
  opacity: 0;
  animation: minisz-welle 5s ease-out infinite;
}
.minisz__welle--2 { animation-delay: .14s; }
@keyframes minisz-welle {
  0%, 38%  { transform: scale(.4); opacity: 0;   }
  40%      { transform: scale(.6); opacity: .9;  }
  56%      { transform: scale(2.6); opacity: 0;  }
  100%     { transform: scale(2.6); opacity: 0;  }
}
.minisz__kreuz {
  stroke: var(--danger);
  stroke-width: 2.4;
  stroke-linecap: round;
  opacity: 0;
  animation: minisz-kreuz 5s linear infinite;
}
@keyframes minisz-kreuz {
  0%, 38%   { opacity: 0;  }
  41%       { opacity: 1;  }
  70%, 100% { opacity: .35; }
}
.minisz__gutpunkt circle {
  fill: var(--success);
  opacity: 0;
  filter: drop-shadow(0 0 6px rgba(var(--success-rgb),.8));
  animation: minisz-gutpunkt 5s linear infinite;
}
@keyframes minisz-gutpunkt {
  0%, 54%   { opacity: 0; }
  56%       { opacity: 1; }
  78%, 100% { opacity: 0; }
}
.minisz__rueckfall rect {
  fill: rgba(var(--success-rgb),.10);
  stroke: rgba(var(--success-rgb),.45);
  stroke-width: 1.2;
}
.minisz__rueckfall path { stroke: var(--success); stroke-width: 2;
                          stroke-linecap: round; stroke-linejoin: round; }

.mini text.minisz__schicht  { fill: var(--muted-strong); font-size: 9px; letter-spacing: .03em; }
.mini text.minisz__kernname { fill: var(--white); font-size: 12px; }
.mini text.minisz__kernsub  { fill: var(--muted); font-size: 9px; }
.mini text.minisz__rot      { fill: var(--danger); font-size: 9px; text-anchor: start;
                              letter-spacing: .06em; }
.mini text.minisz__gut      { fill: var(--success); font-size: 9px; text-anchor: end;
                              letter-spacing: .06em; }
.mini text.minisz__rfname   { fill: var(--success); font-size: 10px; letter-spacing: .03em; }


/* --- Angriff: vier Wege hinein, an jedem eine Kontrolle ------------------

   Takt 6,4s, vier Versuche nacheinander -- einer alle 1,6s. Jeder Weg bis
   zur Kontrolle ist 148 Einheiten lang (94 nach innen, 54 zur Seite).
   ------------------------------------------------------------------------ */

.miniag__ke-a { stop-color: rgb(var(--accent-rgb));  stop-opacity: .20; }
.miniag__ke-b { stop-color: rgb(var(--accent2-rgb)); stop-opacity: .07; }

.miniag__kern { stroke: var(--border-accent); stroke-width: 1.4; }
.miniag__weg path { stroke: var(--border-strong); stroke-width: 1.2; stroke-dasharray: 4 5; }
.miniag__versuch path {
  stroke: var(--danger);
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-dasharray: 6 148;
  filter: drop-shadow(0 0 5px rgba(var(--danger-rgb),.7));
  animation: miniag-versuch 6.4s linear infinite;
}
.miniag__v--2 { animation-delay: -4.8s; }
.miniag__v--3 { animation-delay: -3.2s; }
.miniag__v--4 { animation-delay: -1.6s; }
@keyframes miniag-versuch {
  0%   { stroke-dashoffset: 154; opacity: 1; }
  18%  { stroke-dashoffset: 0;   opacity: 1; }
  20%  { stroke-dashoffset: 0;   opacity: 0; }
  100% { stroke-dashoffset: 0;   opacity: 0; }
}
.miniag__tor rect {
  fill: rgba(var(--tint-rgb),.05);
  stroke: var(--border-accent);
  stroke-width: 1.3;
  animation: miniag-tor 6.4s linear infinite;
}
.miniag__t--2 { animation-delay: -4.8s; }
.miniag__t--3 { animation-delay: -3.2s; }
.miniag__t--4 { animation-delay: -1.6s; }
@keyframes miniag-tor {
  0%, 16%   { stroke: var(--border-accent); stroke-width: 1.3; }
  19%       { stroke: var(--danger);        stroke-width: 2.4; }
  34%, 100% { stroke: var(--border-accent); stroke-width: 1.3; }
}
.miniag__stop path {
  stroke: var(--danger);
  stroke-width: 2.2;
  stroke-linecap: round;
  opacity: .22;
  animation: miniag-stop 6.4s linear infinite;
}
.miniag__s--2 { animation-delay: -4.8s; }
.miniag__s--3 { animation-delay: -3.2s; }
.miniag__s--4 { animation-delay: -1.6s; }
@keyframes miniag-stop {
  0%, 16%   { opacity: .22; }
  19%       { opacity: 1;   }
  34%, 100% { opacity: .22; }
}

.mini text.miniag__kernname { fill: var(--white); font-size: 12px; }
.mini text.miniag__kernsub  { fill: var(--muted); font-size: 9px; }
.mini text.miniag__quelle   { fill: var(--muted-strong-contrast); font-size: 10.5px; }
.mini text.miniag__pruef    { fill: var(--accent); font-size: 9px; }
.mini text.miniag__quelle--l, .mini text.miniag__pruef--l { text-anchor: start; }
.mini text.miniag__quelle--r, .mini text.miniag__pruef--r { text-anchor: end;   }
.mini text.miniag__fuss     { fill: var(--muted); font-size: 9.5px; }

/* --- Wiederanlauf: zwei Strecken auf einer Zeitachse ---------------------

   Der Abspielkopf laeuft in 12s von x=60 bis x=440. Die drei Marken liegen
   bei 21,1% (letzte Sicherung), 47,4% (Ausfall) und 89,5% (wieder im
   Betrieb) -- gerechnet als (x-60)/380.
   ------------------------------------------------------------------------ */

.miniwr__ko-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: 0;   }
.miniwr__ko-b { stop-color: rgb(var(--accent-rgb)); stop-opacity: .16; }
.miniwr__ko-c { stop-color: rgb(var(--accent-rgb)); stop-opacity: 0;   }

.miniwr__bandbett { fill: rgba(var(--tint-rgb),.04); stroke: var(--border); stroke-width: 1; }
.miniwr__gruen    { fill: rgba(var(--success-rgb),.22); }
.miniwr__rot {
  fill: rgba(var(--danger-rgb),.26);
  opacity: 0;
  animation: miniwr-rot 12s linear infinite;
}
@keyframes miniwr-rot {
  0%, 47.4%   { opacity: 0; }
  49%, 100%   { opacity: 1; }
}
.miniwr__achse { stroke: var(--border-strong); stroke-width: 1.3; }
.miniwr__stiel path { stroke: var(--border-strong); stroke-width: 1.1; stroke-dasharray: 3 4; }
.miniwr__punkt circle { fill: var(--muted-strong); }
.miniwr__punkt--rot {
  fill: var(--danger);
  animation: miniwr-alarm 12s linear infinite;
}
@keyframes miniwr-alarm {
  0%, 47.4% { opacity: .3; }
  49%       { opacity: 1;  }
  89.5%     { opacity: 1;  }
  91%, 100% { opacity: .3; }
}
.miniwr__punkt--gruen {
  fill: var(--success);
  animation: miniwr-fertig 12s linear infinite;
}
@keyframes miniwr-fertig {
  0%, 89.5% { opacity: .3; }
  91%, 100% { opacity: 1;  }
}
.miniwr__spanne path { stroke: var(--border-strong); stroke-width: 1.2; }
.miniwr__bett { fill: rgba(var(--tint-rgb),.06); }
.miniwr__fuellung {
  fill: rgba(var(--accent-rgb),.6);
  transform-origin: 240px 0;
  animation: miniwr-fuellen 12s linear infinite;
}
@keyframes miniwr-fuellen {
  0%, 47.4% { transform: scaleX(0); }
  89.5%     { transform: scaleX(1); }
  100%      { transform: scaleX(1); }
}
.miniwr__haken {
  stroke: var(--success);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 22;
  stroke-dashoffset: 22;
  opacity: 0;
  animation: miniwr-haken 12s linear infinite;
}
@keyframes miniwr-haken {
  0%, 89.5% { opacity: 0; stroke-dashoffset: 22; }
  93%, 100% { opacity: 1; stroke-dashoffset: 0;  }
}
.miniwr__kopf { animation: miniwr-kopf 12s linear infinite; }
@keyframes miniwr-kopf {
  from { transform: translateX(49px);  }
  to   { transform: translateX(429px); }
}
.miniwr__kopfstrich { stroke: rgba(var(--accent-rgb),.6); stroke-width: 1.2; }

.mini text.miniwr__bandtext { fill: var(--muted); font-size: 9.5px; text-anchor: start;
                              letter-spacing: .1em; }
.mini text.miniwr__bandwort { fill: var(--danger); font-size: 9.5px; letter-spacing: .1em; }
.mini text.miniwr__marke       { fill: var(--muted-strong-contrast); font-size: 10px; }
.mini text.miniwr__marke--rot  { fill: var(--danger); }
.mini text.miniwr__marke--gruen { fill: var(--success); }
.mini text.miniwr__spname   { fill: var(--muted-strong-contrast); font-size: 10.5px; }
.mini text.miniwr__spsub    { fill: var(--muted); font-size: 9px; }
.mini text.miniwr__fuss     { fill: var(--muted); font-size: 9px; }

/* --- Frueherkennung: derselbe Verlauf, mit und ohne Ueberwachung --------- */

.minife__ro-a { stop-color: rgb(var(--danger-rgb)); stop-opacity: .10; }
.minife__ro-b { stop-color: rgb(var(--danger-rgb)); stop-opacity: 0;   }

.minife__gitter path { stroke: var(--border); stroke-width: 1; }
.minife__achse { stroke: var(--border-strong); stroke-width: 1.3; }
.minife__grenzflaeche { stroke: none; }
.minife__grenze { stroke: var(--danger); stroke-width: 1.3; stroke-dasharray: 7 5; opacity: .7; }

.minife__mit, .minife__ohne {
  fill: none;
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-dasharray: 440;
  animation: minife-zeichnen 9s var(--ease-out) infinite;
}
.minife__mit  { stroke: var(--success); filter: drop-shadow(0 0 5px rgba(var(--success-rgb),.4)); }
.minife__ohne { stroke: var(--danger);  filter: drop-shadow(0 0 5px rgba(var(--danger-rgb),.4));
                opacity: .9; }
@keyframes minife-zeichnen {
  0%        { stroke-dashoffset: 440; }
  62%       { stroke-dashoffset: 0;   }
  96%       { stroke-dashoffset: 0;   }
  100%      { stroke-dashoffset: 440; }
}

.minife__mhalo  { fill: rgba(var(--success-rgb),.22); }
.minife__mpunkt { fill: var(--success); filter: drop-shadow(0 0 6px rgba(var(--success-rgb),.8)); }
.minife__merker circle, .minife__meldung {
  opacity: 0;
  animation: minife-merker 9s linear infinite;
}
.minife__meldung { stroke: rgba(var(--success-rgb),.6); stroke-width: 1.2; stroke-dasharray: 3 4; }
@keyframes minife-merker {
  0%, 28%  { opacity: 0; }
  32%      { opacity: 1; }
  96%      { opacity: 1; }
  100%     { opacity: 0; }
}
.minife__blitz    { stroke: var(--danger); stroke-width: 1.4; stroke-dasharray: 3 4; }
.minife__ahalo    { fill: rgba(var(--danger-rgb),.24); }
.minife__azeichen { fill: none; stroke: var(--danger); stroke-width: 1.8; stroke-linejoin: round; }
.minife__ausfall path, .minife__ausfall circle {
  opacity: 0;
  animation: minife-ausfall 9s linear infinite;
}
@keyframes minife-ausfall {
  0%, 50%  { opacity: 0; }
  54%      { opacity: 1; }
  96%      { opacity: 1; }
  100%     { opacity: 0; }
}
.minife__lmit  { stroke: var(--success); stroke-width: 2.6; stroke-linecap: round; }
.minife__lohne { stroke: var(--danger);  stroke-width: 2.6; stroke-linecap: round; }

.mini text.minife__achsentext { fill: var(--muted); font-size: 9px; text-anchor: end; }
.mini text.minife__achsentext--y { text-anchor: end; }
.mini text.minife__grenztext  { fill: var(--danger); font-size: 9px; text-anchor: start;
                                letter-spacing: .06em; }
.mini text.minife__mittext { fill: var(--success); font-size: 10px; text-anchor: end; }
.mini text.minife__mitsub  { fill: var(--muted); font-size: 9px; text-anchor: end; }
.mini text.minife__ohnetext { fill: var(--danger); font-size: 10px; text-anchor: end;
                              opacity: 0; animation: minife-ausfall 9s linear infinite; }
.mini text.minife__lname      { fill: var(--muted-strong); font-size: 9.5px; text-anchor: start; }

/* --- Aufbewahrung: Staende ruecken nach, einer wird geprueft ------------- */

/* Die Maske arbeitet mit Helligkeit, nicht mit Farbe: Schwarz blendet aus,
   Weiss laesst stehen. Deshalb hier feste Werte und keine Tokens. */
.miniaf__ma-a { stop-color: #000; }
.miniaf__ma-b { stop-color: #fff; }

.miniaf__band rect {
  fill: rgba(var(--accent-rgb),.14);
  stroke: var(--border-accent);
  stroke-width: 1;
}
/* Die drei Baender laufen verschieden schnell -- genauso wie die Abstaende,
   in denen gesichert wird. Jedes schiebt sich um genau eine Kaestchenbreite
   (42 Einheiten), dann faengt es wieder an; dadurch ist der Uebergang nicht
   zu sehen. */
.miniaf__band--1 { animation: miniaf-ruecken 3s  linear infinite; }
.miniaf__band--2 { animation: miniaf-ruecken 6s  linear infinite; }
.miniaf__band--3 { animation: miniaf-ruecken 10s linear infinite; }
@keyframes miniaf-ruecken {
  from { transform: translateX(0);      }
  to   { transform: translateX(-42px);  }
}

.miniaf__abzweig { stroke: var(--border-strong); stroke-width: 1.2; stroke-dasharray: 4 5; fill: none; }
.miniaf__ziehen {
  fill: none;
  stroke: var(--accent);
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-dasharray: 7 140;
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb),.7));
  animation: miniaf-ziehen 8s linear infinite;
}
@keyframes miniaf-ziehen {
  0%   { stroke-dashoffset: 147; opacity: 1; }
  30%  { stroke-dashoffset: 0;   opacity: 1; }
  32%  { stroke-dashoffset: 0;   opacity: 0; }
  100% { stroke-dashoffset: 0;   opacity: 0; }
}
.miniaf__probe {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.2;
  animation: miniaf-probe 8s linear infinite;
}
@keyframes miniaf-probe {
  0%, 28%   { stroke: var(--border-strong); stroke-width: 1.2; }
  34%       { stroke: var(--accent);        stroke-width: 2.2; }
  58%, 100% { stroke: var(--border-strong); stroke-width: 1.2; }
}
.miniaf__probeglyph {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  transform-origin: 12px 12px;
  animation: miniaf-drehen 8s var(--ease-out) infinite;
}
@keyframes miniaf-drehen {
  0%, 30%   { transform: rotate(0deg);   }
  58%, 100% { transform: rotate(360deg); }
}
.miniaf__ergebnis circle {
  fill: rgba(var(--success-rgb),.16);
  stroke: var(--success);
  stroke-width: 1.4;
}
.miniaf__ergebnis path {
  fill: none;
  stroke: var(--success);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.miniaf__ergebnis, .mini text.miniaf__ok {
  opacity: 0;
  animation: miniaf-ok 8s linear infinite;
}
@keyframes miniaf-ok {
  0%, 58%   { opacity: 0; }
  64%, 94%  { opacity: 1; }
  100%      { opacity: 0; }
}

.mini text.miniaf__kopf      { fill: var(--muted); font-size: 10px; letter-spacing: .14em; }
.mini text.miniaf__zeile     { fill: var(--muted-strong-contrast); font-size: 10px; text-anchor: end; }
.mini text.miniaf__pfeil     { fill: var(--muted); font-size: 9px; text-anchor: start; }
.mini text.miniaf__pfeil--neu { text-anchor: end; fill: var(--accent); }
.mini text.miniaf__probename { fill: var(--muted-strong-contrast); font-size: 11px; }
.mini text.miniaf__probesub  { fill: var(--muted); font-size: 9px; }
.mini text.miniaf__ok        { fill: var(--success); font-size: 9.5px; text-anchor: start; }
.mini text.miniaf__fuss      { fill: var(--muted); font-size: 9.5px; }

/* --- Glasfaser: dieselbe Entfernung, zwei Ankuenfte ---------------------- */

.minigf__ku-a { stop-color: rgb(var(--warn-rgb)); stop-opacity: .3; }
.minigf__ku-b { stop-color: rgb(var(--warn-rgb)); stop-opacity: .04; }
.minigf__fa-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .22; }

.minigf__haus path, .minigf__haus rect {
  fill: rgba(var(--tint-rgb),.04);
  stroke: var(--border-strong);
  stroke-width: 1.3;
  stroke-linejoin: round;
}
.minigf__haus path:first-child { fill: none; }
.minigf__leitung { fill: rgba(var(--tint-rgb),.04); stroke: var(--border); stroke-width: 1; }

/* Der Impuls auf Kupfer verblasst auf dem Weg. Nicht kleiner werden lassen:
   Eine Welle, die schrumpft, sieht aus wie eine Welle in der Ferne; eine, die
   verblasst, sieht aus wie ein Signal, das verloren geht. */
.minigf__kupferpuls, .minigf__faserpuls {
  stroke-width: 6;
  stroke-linecap: round;
  stroke-dasharray: 10 320;
}
.minigf__kupferpuls {
  stroke: var(--warn);
  filter: drop-shadow(0 0 5px rgba(var(--warn-rgb),.6));
  animation: minigf-kupfer 3.4s linear infinite;
}
.minigf__faserpuls {
  stroke: var(--accent);
  filter: drop-shadow(0 0 7px rgba(var(--accent-rgb),.85));
  animation: minigf-faser 3.4s linear infinite;
}
@keyframes minigf-kupfer {
  from { stroke-dashoffset: 330; opacity: 1;   }
  to   { stroke-dashoffset: 0;   opacity: .12; }
}
@keyframes minigf-faser {
  from { stroke-dashoffset: 330; opacity: 1; }
  to   { stroke-dashoffset: 0;   opacity: 1; }
}
.minigf__ankunft path { stroke-width: 2.4; stroke-linecap: round; }
.minigf__ankunft--schwach path { stroke: var(--warn); opacity: .4; }
.minigf__ankunft--stark path   { stroke: var(--accent); }
.minigf__stich  { stroke: var(--border-strong); stroke-width: 1.2; fill: none; }
.minigf__quelle { fill: var(--accent); }
.minigf__mass   { stroke: var(--border); stroke-width: 1.1; }

.mini text.minigf__ort        { fill: var(--muted-strong); font-size: 10px; }
.mini text.minigf__spur       { fill: var(--warn); font-size: 11px; letter-spacing: .1em; }
.mini text.minigf__spur--gut  { fill: var(--accent); }
.mini text.minigf__bewertung         { fill: var(--muted); font-size: 9px; }
.mini text.minigf__bewertung--schwach { fill: var(--warn); }
.mini text.minigf__bewertung--gut     { fill: var(--accent); }
.mini text.minigf__masstext { fill: var(--muted); font-size: 9px; letter-spacing: .06em; }
.mini text.minigf__fuss     { fill: var(--muted); font-size: 9.5px; }

/* --- Kontakt: vier Fragen, die reichen ----------------------------------- */

.minikt__bl-a { stop-color: rgb(var(--tint-rgb)); stop-opacity: .055; }
.minikt__bl-b { stop-color: rgb(var(--tint-rgb)); stop-opacity: .015; }

.minikt__blatt { stroke: var(--border-strong); stroke-width: 1.2; }
.minikt__falz  { stroke: rgba(var(--accent-rgb),.3); stroke-width: 1.2; stroke-dasharray: 5 6; }
.minikt__linie { stroke: var(--border); stroke-width: 1.1; }
.minikt__kaestchen { fill: rgba(var(--tint-rgb),.04); stroke: var(--border-strong); stroke-width: 1.2; }
.minikt__haken {
  fill: none;
  stroke: var(--success);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 20;
  stroke-dashoffset: 20;
  opacity: 0;
}
.minikt__stift {
  fill: rgba(var(--accent-rgb),.18);
  stroke: var(--accent);
  stroke-width: 1.4;
  stroke-linejoin: round;
  animation: minikt-stift 9s var(--ease-out) infinite;
}
@keyframes minikt-stift {
  0%, 8%    { transform: translate(112px, 84px); opacity: 0; }
  12%       { transform: translate(112px, 84px); opacity: 1; }
  28%       { transform: translate(112px, 84px); opacity: 1; }
  32%       { transform: translate(112px, 124px); opacity: 1; }
  48%       { transform: translate(112px, 124px); opacity: 1; }
  52%       { transform: translate(112px, 164px); opacity: 1; }
  66%       { transform: translate(112px, 164px); opacity: 1; }
  70%       { transform: translate(112px, 204px); opacity: 1; }
  84%       { transform: translate(112px, 204px); opacity: 1; }
  90%, 100% { transform: translate(112px, 204px); opacity: 0; }
}

.mini text.minikt__kopf     { fill: var(--accent); font-size: 11px; text-anchor: start;
                              letter-spacing: .04em; }
.mini text.minikt__frage    { fill: var(--muted-strong-contrast); font-size: 11.5px; text-anchor: start; }
.mini text.minikt__hilfe    { fill: var(--muted); font-size: 9px; text-anchor: start; }
.mini text.minikt__schluss  { fill: var(--muted-strong); font-size: 10px; text-anchor: start; }
.mini text.minikt__fuss     { fill: var(--muted); font-size: 9.5px; }
.minikt__h--1 { animation: minikt-haken1 9s linear infinite; }
@keyframes minikt-haken1 {
  0%, 13%     { opacity: 0; stroke-dashoffset: 20; }
  17%, 92%  { opacity: 1; stroke-dashoffset: 0;  }
  100%           { opacity: 0; stroke-dashoffset: 20; }
}
.minikt__h--2 { animation: minikt-haken2 9s linear infinite; }
@keyframes minikt-haken2 {
  0%, 33%     { opacity: 0; stroke-dashoffset: 20; }
  37%, 92%  { opacity: 1; stroke-dashoffset: 0;  }
  100%           { opacity: 0; stroke-dashoffset: 20; }
}
.minikt__h--3 { animation: minikt-haken3 9s linear infinite; }
@keyframes minikt-haken3 {
  0%, 53%     { opacity: 0; stroke-dashoffset: 20; }
  57%, 92%  { opacity: 1; stroke-dashoffset: 0;  }
  100%           { opacity: 0; stroke-dashoffset: 20; }
}
.minikt__h--4 { animation: minikt-haken4 9s linear infinite; }
@keyframes minikt-haken4 {
  0%, 71%     { opacity: 0; stroke-dashoffset: 20; }
  75%, 92%  { opacity: 1; stroke-dashoffset: 0;  }
  100%           { opacity: 0; stroke-dashoffset: 20; }
}

/* --- Wissen: ein Netz, das entsteht --------------------------------------

   Vierzehn Kanten, eine alle 5,71% des Taktes. Jede hat ihr eigenes
   Strichmuster, weil jede anders lang ist -- die Laengen stehen im jeweiligen
   Kommentar und sind aus den Knotenpunkten gerechnet. Ein gemeinsamer Wert
   waere einfacher, aber dann zoegen sich die kurzen Kanten zu schnell und die
   langen zu langsam, und das Netz saehe aus wie ein Zufall statt wie ein
   Gedanke.
   ------------------------------------------------------------------------ */

.miniws__au-a { stop-color: rgb(var(--accent-rgb)); stop-opacity: .10; }
.miniws__au-b { stop-color: rgb(var(--accent-rgb)); stop-opacity: 0;   }

.miniws__kante path {
  stroke: rgba(var(--accent-rgb),.55);
  stroke-width: 1.6;
  stroke-linecap: round;
}
.miniws__knoten circle {
  fill: rgba(var(--bg2-rgb),.95);
  stroke: var(--border-accent);
  stroke-width: 1.4;
}
.miniws__puls circle {
  fill: var(--accent);
  opacity: 0;
  filter: drop-shadow(0 0 7px rgba(var(--accent-rgb),.8));
}
.miniws__k--1 { stroke-dasharray: 116; animation: miniws-k1 14s linear infinite; }
@keyframes miniws-k1 {   /* Laenge 116 */
  0%, 0.00%      { stroke-dashoffset: 116; }
  8.60%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 116; }
}
.miniws__k--2 { stroke-dasharray: 116; animation: miniws-k2 14s linear infinite; }
@keyframes miniws-k2 {   /* Laenge 116 */
  0%, 7.14%      { stroke-dashoffset: 116; }
  15.74%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 116; }
}
.miniws__k--3 { stroke-dasharray: 82; animation: miniws-k3 14s linear infinite; }
@keyframes miniws-k3 {   /* Laenge 82 */
  0%, 14.29%      { stroke-dashoffset: 82; }
  22.89%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 82; }
}
.miniws__k--4 { stroke-dasharray: 82; animation: miniws-k4 14s linear infinite; }
@keyframes miniws-k4 {   /* Laenge 82 */
  0%, 21.43%      { stroke-dashoffset: 82; }
  30.03%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 82; }
}
.miniws__k--5 { stroke-dasharray: 116; animation: miniws-k5 14s linear infinite; }
@keyframes miniws-k5 {   /* Laenge 116 */
  0%, 28.57%      { stroke-dashoffset: 116; }
  37.17%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 116; }
}
.miniws__k--6 { stroke-dasharray: 116; animation: miniws-k6 14s linear infinite; }
@keyframes miniws-k6 {   /* Laenge 116 */
  0%, 35.71%      { stroke-dashoffset: 116; }
  44.31%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 116; }
}
.miniws__k--7 { stroke-dasharray: 82; animation: miniws-k7 14s linear infinite; }
@keyframes miniws-k7 {   /* Laenge 82 */
  0%, 42.86%      { stroke-dashoffset: 82; }
  51.46%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 82; }
}
.miniws__k--8 { stroke-dasharray: 82; animation: miniws-k8 14s linear infinite; }
@keyframes miniws-k8 {   /* Laenge 82 */
  0%, 50.00%      { stroke-dashoffset: 82; }
  58.60%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 82; }
}
.miniws__k--9 { stroke-dasharray: 281; animation: miniws-k9 14s linear infinite; }
@keyframes miniws-k9 {   /* Laenge 281 */
  0%, 57.14%      { stroke-dashoffset: 281; }
  65.74%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 281; }
}
.miniws__k--10 { stroke-dasharray: 226; animation: miniws-k10 14s linear infinite; }
@keyframes miniws-k10 {   /* Laenge 226 */
  0%, 64.29%      { stroke-dashoffset: 226; }
  72.89%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 226; }
}
.miniws__k--11 { stroke-dasharray: 186; animation: miniws-k11 14s linear infinite; }
@keyframes miniws-k11 {   /* Laenge 186 */
  0%, 71.43%      { stroke-dashoffset: 186; }
  80.03%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 186; }
}
.miniws__k--12 { stroke-dasharray: 190; animation: miniws-k12 14s linear infinite; }
@keyframes miniws-k12 {   /* Laenge 190 */
  0%, 78.57%      { stroke-dashoffset: 190; }
  87.17%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 190; }
}
.miniws__k--13 { stroke-dasharray: 281; animation: miniws-k13 14s linear infinite; }
@keyframes miniws-k13 {   /* Laenge 281 */
  0%, 85.71%      { stroke-dashoffset: 281; }
  94.31%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 281; }
}
.miniws__k--14 { stroke-dasharray: 263; animation: miniws-k14 14s linear infinite; }
@keyframes miniws-k14 {   /* Laenge 263 */
  0%, 92.86%      { stroke-dashoffset: 263; }
  101.46%, 94% { stroke-dashoffset: 0; }
  100%                  { stroke-dashoffset: 263; }
}
.miniws__p--1 { animation: miniws-p1 14s linear infinite; }
@keyframes miniws-p1 {
  0%, 0.00%      { opacity: 0;   }
  2.00%        { opacity: 1;   }
  9.00%, 94%   { opacity: .45; }
  100%                  { opacity: 0;   }
}
.miniws__p--2 { animation: miniws-p2 14s linear infinite; }
@keyframes miniws-p2 {
  0%, 7.14%      { opacity: 0;   }
  9.14%        { opacity: 1;   }
  16.14%, 94%   { opacity: .45; }
  100%                  { opacity: 0;   }
}
.miniws__p--3 { animation: miniws-p3 14s linear infinite; }
@keyframes miniws-p3 {
  0%, 14.29%      { opacity: 0;   }
  16.29%        { opacity: 1;   }
  23.29%, 94%   { opacity: .45; }
  100%                  { opacity: 0;   }
}
.miniws__p--4 { animation: miniws-p4 14s linear infinite; }
@keyframes miniws-p4 {
  0%, 21.43%      { opacity: 0;   }
  23.43%        { opacity: 1;   }
  30.43%, 94%   { opacity: .45; }
  100%                  { opacity: 0;   }
}
.miniws__p--5 { animation: miniws-p5 14s linear infinite; }
@keyframes miniws-p5 {
  0%, 28.57%      { opacity: 0;   }
  30.57%        { opacity: 1;   }
  37.57%, 94%   { opacity: .45; }
  100%                  { opacity: 0;   }
}
.miniws__p--6 { animation: miniws-p6 14s linear infinite; }
@keyframes miniws-p6 {
  0%, 35.71%      { opacity: 0;   }
  37.71%        { opacity: 1;   }
  44.71%, 94%   { opacity: .45; }
  100%                  { opacity: 0;   }
}
.miniws__p--7 { animation: miniws-p7 14s linear infinite; }
@keyframes miniws-p7 {
  0%, 42.86%      { opacity: 0;   }
  44.86%        { opacity: 1;   }
  51.86%, 94%   { opacity: .45; }
  100%                  { opacity: 0;   }
}
.miniws__p--8 { animation: miniws-p8 14s linear infinite; }
@keyframes miniws-p8 {
  0%, 0.00%      { opacity: 0;   }
  2.00%        { opacity: 1;   }
  9.00%, 94%   { opacity: .45; }
  100%                  { opacity: 0;   }
}
.mini text.miniws__name       { fill: var(--muted-strong-contrast); font-size: 10px; }
.mini text.miniws__name--links  { text-anchor: start; }
.mini text.miniws__name--rechts { text-anchor: end;   }
.mini text.miniws__fuss       { fill: var(--muted); font-size: 9.5px; }

/* --- Ohne Bewegung ------------------------------------------------------- */

/* Jede Zeichnung bleibt in ihrem Endzustand stehen: Punkte auf der Strecke,
   fertig gezeichnete Kurve, Ringe in mittlerer Kraft. Es fehlt nichts. */
@media (prefers-reduced-motion: reduce) {
  .mininet__flow path,
  .minibk__flow path { animation: none; stroke-dashoffset: -130; }
  .minimo__trace     { animation: none; stroke-dashoffset: 0; }
  .minimo__event circle { animation: none; opacity: 1; }
  .minimo__cursor    { animation: none; opacity: 0; }
  .miniwl__cell circle { animation: none; opacity: .35; }
  /* Firewall: Haken fertig gezeichnet, die zwei geblockten Striche liegen am
     Schild, die erlaubte Verbindung durchgehend, kein Ring. Beide Zustaende
     bleiben so am Bild ablesbar. */
  .minifw__shield-mark { animation: none; stroke-dashoffset: 0; }
  .minifw__wave circle { animation: none; opacity: 0; }
  .minifw__sheen       { animation: none; transform: translateX(150px); }
  .minifw__shot path   { animation: none; stroke-dashoffset: 206; opacity: 1; }
  .minifw__burst       { animation: none; opacity: .55; }
  .minifw__pass path   { animation: none; stroke-dasharray: none; stroke-dashoffset: 0; }
  .minifw__comet       { animation: none; opacity: 0; }
  .miniwa__lauf        { animation: none; }
  .miniwa__kurve       { animation: none; opacity: 0; }
  .minist__fluss path  { animation: none; opacity: 0; }
  .miniep__melden path { animation: none; opacity: 0; }
  .minim3__fluss path  { animation: none; opacity: 0; }
  .minimg__fortschritt { animation: none; opacity: 0; }
  /* Regelwerk: Marke steht von Anfang an auf der getroffenen Regel, und der
     Ausgang ist da. Der Endzustand ist die Aussage -- es fehlt nichts. */
  .minirw__mark { animation: none; transform: translateY(68px); }
  .minirw__out  { animation: none; opacity: 1; }
  /* Serverschrank: Lampen leuchten, statt zu blinken. */
  .minirk__led, .minirk__link circle { animation: none; opacity: 1; }
  .minisr__led { animation: none; opacity: 1; }
  .minicl__schrank circle, .minicl__rzled circle { animation: none; opacity: 1; }
  /* Die Lichtpunkte auf den Wegen: Weg durchgehend zeigen statt Punkt. */
  .minicl__fluss path { animation: none; stroke-dasharray: none; stroke-dashoffset: 0; opacity: .5; }

  /* ---------------------------------------------------------------------
     Die Zeichnungen aus 1.2.15.

     Regel wie bisher: Jede bleibt in ihrem Endzustand stehen, und der
     Endzustand ist die Aussage. Nicht einfach "animation: none" -- das haelt
     die Zeichnung in einer zufaelligen Zwischenstellung an. Ein halb
     umgezogener Server, ein Haken, der zur Haelfte gezeichnet ist, oder ein
     Abspielkopf mitten im Bild sieht nach Fehler aus.

     Wo eine Bewegung selbst die Aussage war -- der Balken, der ueber die
     Startseite laeuft, der Stift auf der Kontaktseite --, wird sie
     ausgeblendet statt angehalten. Was sie gezeigt hat, steht dann im
     Endzustand der uebrigen Teile.
     --------------------------------------------------------------------- */

  /* Ueberblick: alle drei Stationen geprueft, der Balken weg. */
  .miniub__spur   { animation: none; stroke-dashoffset: 0; }
  .miniub__scan   { animation: none; opacity: 0; }
  .miniub__led    { animation: none; opacity: 1; }
  .miniub__hin path, .miniub__rueck path {
    animation: none; stroke-dasharray: none; stroke-dashoffset: 0; opacity: .55;
  }
  .miniub__punkt, .miniub__halo { animation: none; opacity: 1; }

  /* Leistungen: die Speichen stehen, die Impulse sind weg. */
  .minils__impuls path { animation: none; opacity: 0; }
  .minils__kranz { animation: none; }
  .minils__ring  { animation: none; }

  /* Betreuung: der Durchlauf ist durch -- alle Fenster abgehakt, die
     Stoerung erkannt und behoben, der Abspielkopf weg. */
  .minibt__kurve   { animation: none; stroke-dashoffset: 0; }
  .minibt__fe      { animation: none; opacity: .8; }
  .minibt__haken path { animation: none; opacity: 1; stroke-dashoffset: 0; }
  .minibt__sicht, .minibt__ezeichen { animation: none; opacity: .85; }
  .minibt__ehalo   { animation: none; opacity: .4; }
  .minibt__antwort { animation: none; transform: scaleX(1); }
  .minibt__ehaken  { animation: none; opacity: 1; stroke-dashoffset: 0; }
  .mini text.minibt__erledigt { animation: none; opacity: 1; }
  .minibt__kopf    { animation: none; opacity: 0; }

  /* Schichten: die Spindel steht als durchgehende Linie da. */
  .minisc__ka     { animation: none; opacity: .5; }
  .minisc__platte { animation: none; }
  .minisc__hinab  { animation: none; stroke-dasharray: none; stroke-dashoffset: 0; opacity: .45; }
  .minisc__herauf { animation: none; opacity: 0; }

  /* Partner: die Wege bleiben, die Punkte darauf nicht. */
  .minipa__hin, .minipa__zurueck,
  .minipa__fan path, .minipa__sammeln path { animation: none; opacity: 0; }
  .minipa__kranz { animation: none; }

  /* Waage: waagrecht -- genau das ist die Aussage. */
  .miniwg__balken, .miniwg__zeiger,
  .miniwg__schale--links, .miniwg__schale--rechts { animation: none; }

  /* Hybrid: die Verbindung steht, beide Seiten markiert. */
  .minihy__hin, .minihy__rueck {
    animation: none; stroke-dasharray: none; stroke-dashoffset: 0; opacity: .5;
  }
  .minihy__ring { animation: none; }
  .minihy__an   { animation: none; opacity: .9; }

  /* Private Cloud: Grenze gezogen, Lampen an, Zugang als Linie. */
  .minipc__zaun { animation: none; }
  .minipc__led circle { animation: none; opacity: 1; }

  /* Cloud-Modelle: drei ruhige Szenen. */
  .minicm__kreisen { animation: none; opacity: 0; }
  .minicm__hin, .minicm__rueck {
    animation: none; stroke-dasharray: none; stroke-dashoffset: 0; opacity: .5;
  }
  .minicm__tropfen path {
    animation: none; stroke-dasharray: none; stroke-dashoffset: 0; opacity: .4;
  }
  .minicm__led circle { animation: none; opacity: 1; }

  /* Managed Server: drei Bahnen mit je einem Zug darauf. */
  .minims__zug, .minims__kopf { animation: none; stroke-dashoffset: 0; }
  .minims__punkt { animation: none; opacity: 1; }
  .minims__led circle { animation: none; opacity: 1; }

  /* Virtuelle Server: umgezogen. Der Endzustand ist die ganze Aussage. */
  .minivs__zug { animation: none; transform: translate(244px, 36px); opacity: 1; }
  .minivs__led { animation: none; opacity: 1; }

  /* Hosting: Plattform ruhig, Band ohne Lauflicht. */
  .minihs__anfrage path, .minihs__antwort path { animation: none; opacity: 0; }
  .minihs__dienst rect { animation: none; }
  .minihs__sweep { animation: none; opacity: 0; }

  /* Serverplatz: die Zuleitungen stehen, die Lampe leuchtet. */
  .minisv__puls path { animation: none; opacity: 0; }
  .minisv__meinsled  { animation: none; opacity: 1; }

  /* Support: der Weg ist durchlaufen, der Balken voll. */
  .minisp__melden path, .minisp__lauf { animation: none; opacity: 0; }
  .minisp__balken { animation: none; transform: scaleX(1); }
  .minisp__station circle { animation: none; stroke: var(--border-accent); stroke-width: 1.6; }

  /* Managed: die Huelle steht, der Punkt haelt oben auf der Kuppel -- dort,
     wo er am wenigsten nach einem Fehler aussieht. */
  /* Robi auf /robi/: kein Wackeln, kein blinkender Cursor. Getippt wird
     ebenfalls nicht -- das regelt das Skript. */
  .robi__knopf.is-angestupst .robi__figur { animation: none; }
  .robi__zeile--prompt::after { animation: none; opacity: 1; }

  .minimd__blende  { animation: none; transform: translateX(230px); }
  .minimd__schweif { animation: none; stroke-dashoffset: 192; }
  .minimd__punkt   { animation: none; stroke-dashoffset: 181; }
  .minimd__led circle { animation: none; opacity: 1; }

  /* Wartungsplan: das Raster steht, nichts wandert. */
  .miniwp__zeiger, .miniwp__zeiger-x { animation: none; }

  /* Schutz: Angriff abgewehrt, berechtigter Zugriff durchgelassen. */
  .minisz__ring--2 { animation: none; }
  /* Beide Punkte verschwinden, beide Wege bleiben als gestrichelte Linie
     stehen, und am Ende jedes Weges steht sein Zeichen: das Kreuz, wo der
     Angriff haengenbleibt, der Punkt, wo der Zugriff ankommt. Vorher wurde
     der gruene Puls hier zur durchgezogenen Linie ueber die volle Laenge --
     damit war der eine Weg ein Strich und der andere eine Strichelung, und
     das Bild erzaehlte zwei verschiedene Dinge. */
  .minisz__angriff { animation: none; opacity: 0; }
  .minisz__zugriff { animation: none; opacity: 0; }
  .minisz__welle   { animation: none; opacity: 0; }
  .minisz__kreuz   { animation: none; opacity: 1; }
  .minisz__gutpunkt circle { animation: none; opacity: 1; }

  /* Angriff: an jeder der vier Tueren steht das Zeichen. */
  .miniag__versuch path { animation: none; opacity: 0; }
  .miniag__tor rect     { animation: none; }
  .miniag__stop path    { animation: none; opacity: 1; }

  /* Wiederanlauf: der Ausfall liegt zurueck, wiederhergestellt ist es auch. */
  .miniwr__rot       { animation: none; opacity: 1; }
  .miniwr__punkt--rot, .miniwr__punkt--gruen { animation: none; opacity: 1; }
  .miniwr__fuellung  { animation: none; transform: scaleX(1); }
  .miniwr__haken     { animation: none; opacity: 1; stroke-dashoffset: 0; }
  .miniwr__kopf      { animation: none; opacity: 0; }

  /* Frueherkennung: beide Kurven fertig gezeichnet, beide Enden beschriftet. */
  .minife__mit, .minife__ohne { animation: none; stroke-dashoffset: 0; }
  .minife__merker circle, .minife__meldung { animation: none; opacity: 1; }
  .minife__ausfall path, .minife__ausfall circle { animation: none; opacity: 1; }
  .mini text.minife__ohnetext { animation: none; opacity: 1; }

  /* Aufbewahrung: die Baender stehen, die Probe ist geprueft. */
  .miniaf__band       { animation: none; }
  .miniaf__ziehen     { animation: none; opacity: 0; }
  .miniaf__probe      { animation: none; }
  .miniaf__probeglyph { animation: none; }
  .miniaf__ergebnis   { animation: none; opacity: 1; }
  .mini text.miniaf__ok { animation: none; opacity: 1; }

  /* Glasfaser: beide Impulse am Ziel -- einer schwach, einer stark. Hier
     traegt das Standbild die Aussage sogar deutlicher als die Bewegung. */
  .minigf__kupferpuls { animation: none; stroke-dashoffset: 0; opacity: .12; }
  .minigf__faserpuls  { animation: none; stroke-dashoffset: 0; opacity: 1; }

  /* Kontakt: alle vier Haken gesetzt, der Stift weg. */
  .minikt__haken { animation: none; opacity: 1; stroke-dashoffset: 0; }
  .minikt__stift { animation: none; opacity: 0; }

  /* Wissen: das fertige Netz. */
  .miniws__kante path { animation: none; stroke-dashoffset: 0; }
  .miniws__puls circle { animation: none; opacity: .45; }
}

/* ----------------------------------------------------------- 10 Fusszeile */

.site-footer {
  position: relative;
  background: var(--bg-deep);
  border-top: 1px solid var(--border);
  padding-block: clamp(2.25rem, 4vw, 3.5rem) var(--s-6);
  margin-top: auto;
}
.footer-grid {
  display: grid;
  grid-template-columns: minmax(240px, 1.3fr) repeat(4, minmax(0, 1fr));
  gap: clamp(1.5rem, 2.4vw, 2.25rem);
  /* Die Spalten sind unterschiedlich lang -- neun Leistungen gegen drei
     rechtliche Hinweise. Sie beginnen oben und wachsen nach unten; sie
     mittig auszurichten wuerde die Ueberschriften gegeneinander verschieben
     und den Blick am Anfang jeder Liste suchen lassen. */
  align-items: start;
}
@media (max-width: 980px) { .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* Zwei Spalten bis hinunter zu 430 Punkten. Die Eintraege sind kurz
   ("IT-Wartung", "Backup"); einspaltig stapeln sich alle 21 untereinander und
   die Fusszeile wird auf dem Telefon 1800 Punkte hoch -- mehr als zwei
   Bildschirme, nur um unten anzukommen. Erst unter 375 Punkten -- den
   schmalsten Telefonen, die noch im Umlauf sind -- wird daraus eine Spalte. */
@media (max-width: 374px) { .footer-grid { grid-template-columns: minmax(0, 1fr); } }
.footer-brand { display: grid; gap: var(--s-3); align-content: start; max-width: 34ch; }
/* Die Marke traegt im Kopf die fluide Grundgroesse und wird dort bis 19
   Punkte gross. In der Fusszeile steht sie neben Menuelinks in 13,6 -- dort
   wirkte sie wie eine zweite Ueberschrift. Hier bekommt sie eine feste,
   ruhigere Groesse. */
.site-footer .brand { font-size: 1rem; }
.site-footer .brand__mark { width: 26px; height: 26px; }
.footer-brand p { color: var(--muted); font-size: .85rem; margin: 0; }
.footer-col h2 {
  font-family: var(--mono); font-size: .63rem; letter-spacing: .15em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 var(--s-4); font-weight: 550;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .45rem; }
.footer-col a { color: var(--text); text-decoration: none; font-size: .85rem; }

/* AUF DEM TELEFON BRAUCHEN DIE FUSSZEILENLINKS EINE FLAECHE.

   Gemessen waren sie 16 Punkte hoch, bei .45rem Abstand also 23,2 Punkte von
   Mitte zu Mitte -- unter den 24, die WCAG 2.5.8 verlangt. Die Ausnahme fuer
   Links mitten im Flieestext greift hier nicht: Das sind Listen aus 37
   Verweisen untereinander, keine Woerter in einem Satz.

   Gelöst ueber Polsterung am Link selbst, nicht ueber groesseren Abstand
   zwischen den Zeilen: Der Abstand vergroessert nur die Luecke, die Flaeche
   zum Treffen bleibt dieselbe. */
@media (max-width: 700px) {
  .footer-col ul { gap: 0; }
  .footer-col a  { display: block; padding-block: .34rem; }
}
.footer-col a:hover { color: var(--accent); }
/* Kontaktdaten als eigene Zeile quer unter den Menues.

   In der Markenspalte hatten sie zu wenig Breite: "+43 (1) 35 777 77 800"
   brach mitten entzwei, und die Spalte wurde doppelt so hoch wie die Menues
   daneben. Quer bekommt jede Angabe ihre Zeile in einem Stueck.

   auto-fit statt einer festen Spaltenzahl, weil die Zahl der Angaben von den
   Einstellungen abhaengt: Wer keine Fernwartung hinterlegt, soll keine leere
   Spalte sehen. */
.contact-info--row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.4fr);
  gap: var(--s-6) clamp(1.5rem, 3vw, 3rem);
  margin-top: clamp(1.75rem, 3.2vw, 2.5rem);
  padding-top: var(--s-6);
  border-top: 1px solid var(--border);
}

/* Drei feste Spalten nach Art der Angabe: Ort, Telefon, Post.

   Vorher bildete das Raster so viele Spalten, wie Platz war, und jede Angabe
   war eine eigene Kachel. Mit den drei Mailadressen (1.7.8) brauchte eine
   davon die doppelte Breite -- und daneben blieb eine halbe Zeile leer.
   Gruppiert fuellt sich die Reihe von selbst, und gleich breite Spalten
   geben den Mailadressen den Platz, den sie brauchen.

   align-content: start, damit die kurze Ortsspalte nicht auseinandergezogen
   wird, um die Hoehe der Telefonspalte zu erreichen. */
.contact-col { display: grid; gap: var(--s-5); align-content: start; }

/* Kleiner als auf der Kontaktseite.

   Dieselben Bausteine tragen dort ganze Karten und duerfen gross sein. In der
   Fusszeile stehen sie neben Menuelinks in .93rem -- in der Groesse der
   Kontaktseite wirkten sie wie eine zweite Ueberschrift und zogen den Blick
   von den Menues weg. Hier gilt deshalb die Fusszeilengroesse: Wert wie ein
   Menuelink, Symbolflaeche entsprechend kleiner. */
.contact-info--row .contact-item { grid-template-columns: 28px minmax(0, 1fr); gap: var(--s-3); }
.contact-info--row .contact-item__icon {
  width: 28px; height: 28px;
  border-radius: var(--r-sm);
}
.contact-info--row .contact-item__icon .icon { width: 15px; height: 15px; }
.contact-info--row .contact-item__label { font-size: .58rem; margin-bottom: .1rem; }
.contact-info--row .contact-item__value { font-size: .85rem; font-weight: 550; }
.contact-info--row .contact-item__sub { font-size: .75rem; }
.contact-info--row .contact-item { align-items: start; }
.contact-info--row .contact-item__value { font-variant-numeric: tabular-nums; }
/* Anwählbares bricht nicht mitten im Wert: eine in zwei Teile gerissene
   Telefonnummer liest niemand richtig ab. Reicht der Platz nicht, schiebt das
   Raster den Eintrag in die naechste Zeile. Fliesstext -- Adresse und
   Bürozeiten -- darf dagegen umbrechen; sonst sprengt eine lange Zeile die
   Spalte. */
.contact-info--row .contact-item__value a,
.contact-info--row .contact-item__sub { white-space: nowrap; }
/* Eine Ausnahme: der Firmenname. Er ist ein Link, aber kein Wert, der in
   einem Stueck abgelesen wird -- und mit fuenf Woertern laeuft er sonst aus
   seiner Spalte heraus in die Rufnummern daneben.

   Der Selektor nennt das a mit: Die Regel darueber wiegt zwei Klassen plus
   ein Element (0,2,1), eine blosse Klasse (0,2,0) verliert gegen sie. Genau
   daran ist der erste Versuch gescheitert -- sichtbar erst im Bildschirmfoto,
   nicht im Stylesheet. */
.contact-info--row .contact-item__value a.contact-item__firma { white-space: normal; }
/* Jedes Wort fuer sich: So bricht die Zeile an den Leerzeichen und nicht im
   Wort "IT-Dienstleistungs". */
.contact-item__firma > span { white-space: nowrap; }
/* .contact-item--wide ist mit 1.8.1 entfallen: Es gab den Ausnahmefall nur,
   solange jede Angabe eine eigene Kachel in einem automatischen Raster war.
   Die Spalten sind jetzt gleich breit, und keine Angabe muss ausbrechen. */

/* Drei Postfächer untereinander (1.7.8).

   Zweck und Adresse stehen in einer Zeile, nicht uebereinander: Mit je zwei
   Zeilen waere der Eintrag sechszeilig und damit dreimal so hoch wie die
   Nummern daneben. Seit 1.8.1 hat die Post eine eigene Spalte, in der jede
   Zeile am Stueck Platz findet; untereinander waere sie dort trotzdem
   doppelt so hoch, ohne etwas zu gewinnen.

   Der Zweck steht in --muted-strong, nicht in --muted: Er ist eine
   Beschriftung, keine Nebenbemerkung, und muss auch im hellen Thema die
   4,5:1 erreichen (7,87:1 hell, 10,99:1 dunkel). Das Umbrechen zwischen
   Zweck und Adresse bleibt erlaubt -- die Adresse selbst haelt die
   bestehende nowrap-Regel zusammen. */
.mail-liste { display: grid; gap: .22rem; }
.mail-liste__zweck { color: var(--muted-strong); font-weight: 450; }
/* Zwei Spalten, sobald drei zu eng fuer eine Mailzeile am Stueck werden --
   die Post nimmt dann die ganze Breite der zweiten Zeile ein. Darunter
   untereinander.

   Die Grenze ist gemessen, nicht geraten: Die laengste Zeile,
   "Anfragen zu Angeboten: sales@netzware.at", braucht mitsamt Symbolspalte
   437 Punkte. Darum ist die Postspalte auch breiter als die beiden anderen
   (1,4 : 1) -- bei drei gleichen Spalten blieben selbst auf einem
   1920er-Schirm nur 355 Punkte, und jede der drei Zeilen braeche entzwei,
   waehrend daneben Platz frei bliebe. */
@media (max-width: 1180px) {
  .contact-info--row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .contact-info--row .contact-col:last-child { grid-column: 1 / -1; }
}
@media (max-width: 620px) {
  .contact-info--row { grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
  .contact-info--row .contact-item__value a,
  .contact-info--row .contact-item__sub { white-space: normal; }
}

.footer-bottom {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  align-items: center; justify-content: space-between;
  margin-top: clamp(1.5rem, 2.8vw, 2rem);
  padding-top: var(--s-5);
  border-top: 1px solid var(--border);
  font-size: .8rem;
  color: var(--muted);
}
.footer-bottom nav { display: flex; flex-wrap: wrap; gap: var(--s-5); }
/* Der KI-Hinweis: eine Stufe leiser als die Zeile darueber. Er soll zu
   finden sein, wenn jemand danach sucht, und nicht ins Auge springen, wenn
   niemand danach sucht. Deshalb keine Linie, kein Kasten, kein Symbol.

   Leiser heisst hier: kleiner, nicht blasser. Der erste Entwurf hatte
   zusaetzlich opacity .85 -- damit fiel der Kontrast unter 4,5:1, und zwar
   auf allen 41 Seiten. --muted ist der Ton fuer ruhigen Text und ist dafuer
   geprueft; eine Deckkraft darueber macht aus einem geprueften Wert einen
   ungeprueften. */
.footer-ai {
  margin: var(--s-3) 0 0;
  font-size: .76rem;
  line-height: 1.55;
  color: var(--muted);
}
.footer-bottom a { color: var(--muted); text-decoration: none; }
.footer-bottom a:hover { color: var(--accent); }

/* Das Mega-Menu ist absolut positioniert und im geschlossenen Zustand nur
   unsichtbar, nicht aus dem Layout genommen. Es steht mittig unter seinem
   Ausloeser und ist bis zu 920 px breit -- unter einem Menuepunkt am linken
   Rand ragt es damit ueber die Seite hinaus und erzeugt eine waagrechte
   Bildlaufleiste, obwohl nichts zu sehen ist.

   Deshalb hier: Die Huelle laesst waagrecht nichts ueberlaufen. Sie ist der
   richtige Ort dafuer -- am <body> wuerde `overflow-x: hidden` das
   klebende Verhalten der Kopfleiste in manchen Browsern aushebeln, weil
   `position: sticky` einen scrollenden Vorfahren braucht. */
.page-shell { display: flex; flex-direction: column; min-height: 100vh; overflow-x: clip; }
main { flex: 1 0 auto; }

/* ------------------------------------------------------------ 11 Bewegung */

/* Abschnitte treten beim Erreichen leicht ein. Der Startzustand steht nur
   dann im CSS, wenn JavaScript laeuft (Klasse am <html>) -- sonst waere bei
   abgeschaltetem JavaScript die halbe Seite unsichtbar. */
.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--spring);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
.js .reveal.is-in { opacity: 1; transform: none; }
.js .reveal.is-in { will-change: auto; }

/* ------------------------------------------------- 11b Bewegung im Inhalt

   Leichte Bewegung auf den Unterseiten, jeweils passend zu dem, was der
   Abschnitt aussagt. Drei Regeln, an die sich alles hier haelt:

     1. Die Bewegung laeuft EINMAL, wenn der Abschnitt ins Bild kommt.
        Keine Dauerschleife -- ausser dort, wo etwas tatsaechlich laufend
        passiert (Betriebszustaende).
     2. Sie erklaert etwas. Ebenen werden von aussen nach innen geprueft,
        weil Sicherheit so aufgebaut ist. Der Haken zeichnet sich, weil er
        ein Ergebnis ist. Die Ablauflinie laeuft nach unten, weil der Weg
        dorthin fuehrt. Nichts bewegt sich, weil Bewegung huebsch waere.
     3. Ohne Bewegung ist alles vollstaendig da.

   Zu 3. der Trick, auf den es ankommt: Jeder Startzustand haengt an der
   Klasse `.reveal`. Die setzt site.js genau dann, wenn es das Element auch
   beobachtet -- also nicht bei abgeschalteter Bewegung und nicht ohne
   IntersectionObserver. Haengt ein Startzustand dagegen an `.js`, bleibt in
   diesen Faellen ein ungezeichneter Haken oder eine unsichtbare Kante
   stehen. Deshalb hier ueberall `.reveal`, nie `.js`.
   ------------------------------------------------------------------------ */

/* --- Ebenen: Sicherheit wird von aussen nach innen geprueft -------------- */

/* Ein flacher Lichtstreifen laeuft einmal durch jede Ebene, von der ersten
   zur letzten -- wie eine Pruefung, die die Ebenen der Reihe nach durchgeht. */
.layer.reveal { position: relative; overflow: hidden; }
.layer.reveal::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(100deg,
    transparent 35%,
    rgba(var(--accent-rgb),.14) 50%,
    transparent 65%);
  transform: translateX(-120%);
}
.layer.reveal.is-in::after {
  animation: layer-sweep 1.1s var(--ease-out) forwards;
  animation-delay: calc(var(--reveal-delay, 0ms) + 220ms);
}
@keyframes layer-sweep { to { transform: translateX(120%); } }

/* --- Ablauf: die Linie laeuft mit dem Weg mit ---------------------------- */

/* Die Verbindungslinie wachst von oben nach unten, sobald der Schritt
   sichtbar wird -- sie zeigt die Richtung, in der gelesen wird. */
.tl-step.reveal:not(:last-child) .tl-step__marker::after {
  transform: scaleY(0);
  transition: transform var(--dur-slow) var(--ease-out);
  transition-delay: calc(var(--reveal-delay, 0ms) + 180ms);
}
.tl-step.reveal.is-in:not(:last-child) .tl-step__marker::after { transform: scaleY(1); }

/* Der Schrittpunkt kommt aus der Feder heran, nicht aus dem Nichts. */
.tl-step.reveal .tl-step__dot {
  transform: scale(.82);
  opacity: .5;
  transition:
    transform var(--dur) var(--spring-soft),
    opacity var(--dur-fast) linear;
}
.tl-step.reveal.is-in .tl-step__dot { transform: none; opacity: 1; }

/* --- Checkliste: der Haken zeichnet sich --------------------------------- */

/* Ein Haken, der schon da ist, ist eine Behauptung. Einer, der sich
   zeichnet, ist ein Ergebnis. Gezogen wird ueber stroke-dasharray -- das
   laeuft auf der Grafikkarte und kostet kein Neuberechnen des Layouts. */
.check-item.reveal .check-item__mark .icon path {
  stroke-dasharray: 22;
  stroke-dashoffset: 22;
}
.check-item.reveal.is-in .check-item__mark .icon path {
  animation: check-draw .5s var(--ease-out) forwards;
  animation-delay: calc(var(--reveal-delay, 0ms) + 160ms);
}
@keyframes check-draw { to { stroke-dashoffset: 0; } }

.check-item.reveal .check-item__mark {
  transform: scale(.9);
  transition: transform var(--dur) var(--spring-soft);
}
.check-item.reveal.is-in .check-item__mark { transform: none; }

/* --- Kurzantwort: die Akzentkante wird gezogen --------------------------- */

/* Die Kurzantwort ist die Antwort auf die Frage in ihrer Ueberschrift. Ihre
   Kante wird nicht eingeblendet, sondern von oben nach unten gezogen -- der
   Blick beginnt damit dort, wo der Satz anfaengt. */
.definition.reveal { border-left-color: transparent; }
.definition.reveal::after {
  content: '';
  position: absolute;
  left: -2px; top: 0; bottom: 0;
  width: 2px;
  background: var(--accent);
  transform-origin: top;
  transform: scaleY(0);
  transition: transform var(--dur-slow) var(--ease-out) 120ms;
}
.definition.reveal.is-in::after { transform: scaleY(1); }

/* --- Karten: das Symbol kommt einen Hauch spaeter ------------------------ */

/* Ein Zeichen, das gemeinsam mit dem Text erscheint, wird nicht gesehen.
   Zwei Zehntel spaeter zieht es den Blick an den Anfang der Karte. */
.reveal .card__icon,
.reveal .feature__icon {
  transform: scale(.88);
  opacity: 0;
  transition:
    transform var(--dur) var(--spring-soft),
    opacity var(--dur-fast) linear;
  transition-delay: calc(var(--reveal-delay, 0ms) + 200ms);
}
.reveal.is-in .card__icon,
.reveal.is-in .feature__icon { transform: none; opacity: 1; }

/* --- Betriebszustaende: der Punkt lebt ----------------------------------- */

/* Hier ist eine Dauerschleife richtig: Ein Betriebszustand ist nichts, was
   einmal passiert. Der Punkt atmet sehr langsam -- schnelles Blinken waere
   eine Warnung, und "in Ordnung" ist keine. Der Warnpunkt atmet doppelt so
   schnell; auch das ist Aussage, nicht Zierde.

   Diese Regel haengt nicht an `.reveal`, weil sie keinen Startzustand
   braucht: Ohne Bewegung steht der Punkt einfach still da. */
.badge--ok .badge__dot   { animation: dot-breathe 3.6s var(--ease-out) infinite; }
.badge--warn .badge__dot { animation: dot-breathe 1.8s var(--ease-out) infinite; }
@keyframes dot-breathe {
  0%, 100% { opacity: 1;   box-shadow: 0 0 0 0 rgba(var(--tint-rgb), 0); }
  50%      { opacity: .55; box-shadow: 0 0 0 3px rgba(var(--tint-rgb), .06); }
}
@media (prefers-reduced-motion: reduce) {
  .badge__dot { animation: none; opacity: 1; box-shadow: none; }
}

/* ------------------------------------------------------- 12 Ruecksichtnahme */

@media (prefers-reduced-motion: reduce) {
  /* Nicht "keine Rueckmeldung", sondern eine ruhige: Ueberblendung statt
     Bewegung, kein Nachschwingen, keine Dauerschleifen. */
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .js .reveal { opacity: 1; transform: none; }
  .infra__link::after, .rack__led { animation: none; opacity: 1; }
  .card--link:hover, .feature:hover, .linkcard:hover, .infra__node:hover { transform: none; }
  .mega, .mobile-nav { transition: opacity .12s linear, visibility 0s; }
  /* Je Variante die eigene Ruhelage: das breite Panel bleibt um seine halbe
     Breite verschoben, die schmale Liste steht an ihrem Auslaeser. Ein
     pauschales `transform: none` haette das breite Panel um eine halbe
     Panelbreite nach rechts gerueckt. */
  .mega { transform: translate(-50%, 0) !important; }
  .mega--schmal { transform: translate(0, 0) !important; }
  .mobile-nav { transform: none !important; }
}

@media (prefers-reduced-transparency: reduce) {
  /* Weniger Durchsicht, dafuer mehr Kontrast -- die Hierarchie bleibt
     erhalten, nur das Material wird fest. */
  .site-header, .mobile-nav, .card, .infra, .feature {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .site-header { background: var(--header-opaque); }
  /* .mega ist seit 1.6.0 immer deckend und braucht hier nichts mehr. */
  .mobile-nav { background: var(--mega-opaque); }
  .card, .feature { background: var(--surface); }
}

@media (prefers-contrast: more) {
  :root {
    --border: rgba(var(--tint-rgb),.22);
    --border-strong: rgba(var(--tint-rgb),.38);
    --muted: var(--muted-contrast);
    --muted-strong: var(--muted-strong-contrast);
  }
  .card, .feature, .linkcard, .faq__item { background: var(--surface-contrast); }
  .btn--secondary { border-color: var(--white); }
}

@media print {
  /* Eintretende Abschnitte sind auf Papier immer sichtbar -- sonst druckt
     der Browser eine halb leere Seite. */
  .js .reveal { opacity: 1 !important; transform: none !important; }
  body { background: #fff; color: #111; }
  body::before, .site-header, .mobile-nav, .cta-band, .btn, .site-footer nav { display: none !important; }
  .card, .feature, .faq__item { border: 1px solid #ccc; background: none; box-shadow: none; }
  h1, h2, h3, .stat__value { color: #000; }
  a { color: #000; text-decoration: underline; }
  a[href^='/']::after { content: ' (netzware.at' attr(href) ')'; font-size: .8em; }
  .section { padding-block: 1.5rem; }
  .faq__item { break-inside: avoid; }
}

/* Nachtraege, die auf frueheren Regeln aufbauen muessen */
.nav__link { position: relative; }
.card--link { cursor: pointer; }

/* ===========================================================================
   Hilfsklassen

   Sie ersetzen die style-Attribute in den Vorlagen. Die Content-Security-Policy
   dieser Website verbietet Inline-Styles vollstaendig -- ein Nonce hilft dabei
   nicht, weil er fuer Attribute nicht gilt. Der Nebeneffekt ist angenehm: alle
   Abstaende stehen an einer Stelle und lassen sich zentral aendern.
   =========================================================================== */

.mt-4 { margin-top: var(--s-4); }
.mt-5 { margin-top: var(--s-5); }
.mt-6 { margin-top: var(--s-6); }
.mt-7 { margin-top: var(--s-7); }
.mt-8 { margin-top: var(--s-8); }
.mt-auto { margin-top: auto; }
.m-0 { margin: 0; }
.mi-auto { margin-inline: auto; }
.text-center { text-align: center; }
.justify-center { justify-content: center; }
.items-start { align-items: start; }
.gap-6 { gap: var(--s-6); }
.not-italic { font-style: normal; }
.plain-list { list-style: none; margin: 0; padding: 0; }
.plain-fieldset { border: 0; padding: 0; margin: 0; }
.plain-legend { padding: 0; margin-bottom: .6rem; }
.max-80ch { max-width: 80ch; }
.indent-1 { padding-left: 1rem; }

/* Fussnote am unteren Rand einer Karte, oberhalb einer feinen Trennlinie. */
.card-note {
  margin-top: auto;
  padding-top: var(--s-4);
  border-top: 1px solid var(--border);
}

/* Versatz der wandernden Datenpunkte und der Statusleuchten. Feste Stufen
   statt gerechneter Werte -- es sind nie mehr als acht Stationen. */
.dl-1 { --delay: .42s; }
.dl-2 { --delay: .84s; }
.dl-3 { --delay: 1.26s; }
.dl-4 { --delay: 1.68s; }
.dl-5 { --delay: 2.10s; }
.dl-6 { --delay: 2.52s; }
.dl-7 { --delay: 2.94s; }
.dl-8 { --delay: 3.36s; }

/* Verjuengung der Ebenen von aussen nach innen. */
.li-0 { --i: 0; } .li-1 { --i: 1; } .li-2 { --i: 2; } .li-3 { --i: 3; }
.li-4 { --i: 4; } .li-5 { --i: 5; } .li-6 { --i: 6; } .li-7 { --i: 7; }
.li-8 { --i: 8; }

/* ---------------------------------------------------------------------------
   Vorschau im Backend
   Nur wirksam, wenn die Seite im Editor eingebettet ist (<body class="is-preview">).
   Ein Klick auf einen Abschnitt oeffnet dort das passende Formular.
   --------------------------------------------------------------------------- */

.is-preview .is-previewable {
  outline: 1px dashed transparent;
  outline-offset: -3px;
  cursor: pointer;
  transition: outline-color 150ms var(--ease-out), background-color 150ms var(--ease-out);
}
.is-preview .is-previewable:hover {
  outline-color: rgba(var(--accent-rgb),.55);
  background-color: rgba(var(--accent-rgb),.03);
}
.is-preview .is-previewable.is-target {
  outline: 2px solid var(--accent);
  outline-offset: -4px;
}

/* Ein im Editor ausgeblendeter Block bleibt in der Vorschau sichtbar, aber
   gekennzeichnet -- sonst raetselt jemand, warum die Seite kuerzer ist. */
.preview-hidden { opacity: .45; position: relative; }
.preview-hidden__flag {
  position: absolute;
  z-index: 5;
  top: 10px; left: 10px;
  background: var(--warn);
  color: var(--warn-ink);
  font-family: var(--mono);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  padding: .45em .8em;
  border-radius: var(--r-pill);
}

/* ===========================================================================
   10 · Der Avatar

   Ein Markenzeichen, keine Deko-Grafik. Deshalb drei Festlegungen, die vom
   Rest des Stylesheets abweichen:

   1. ER SITZT IMMER AUF EINER DUNKLEN PLATTE. Sein Gehaeuse ist weiss --
      auf einer hellen Seite verschwindet es sonst. Die Platte loest das,
      ohne ihn umzufaerben, und sie liefert nebenbei die dunklen
      Kontrastflaechen, von denen das Briefing spricht.

   2. SEINE FARBEN HAENGEN NICHT AM THEMA. Er ist ein Bild und bleibt in
      beiden Themen dasselbe Bild. Ein Firmenzeichen, das die Farbe wechselt,
      sobald jemand hell auf dunkel stellt, ist kein Firmenzeichen.

   3. SEINE BEWEGUNG IST LANGSAM. Ein Schwebezyklus dauert sechs Sekunden.
      Alles Schnellere zieht den Blick vom Inhalt weg -- und das waere das
      Gegenteil dessen, wofuer er da ist.
   =========================================================================== */

:root {
  --bot-platte:   #0D1218;
  --bot-platte-2: #161D26;
}

/* Er ist ein <picture> oder ein <img>; beide sollen sich wie ein Block
   verhalten und ihren Platz vom Behaelter bekommen. */
.bot,
.bot img { display: block; width: 100%; height: auto; }

/* ER STEHT STILL.
   In 1.3.0 schwebte er: vier Pixel in sechs Sekunden. Auf der grossen Figur
   im Seitenkopf war das kaum zu sehen -- auf den kleinen Zeichen daneben,
   am Assistenten, am FAQ und in der Fusszeile, sind vier Pixel ein Zehntel
   der Hoehe, und vier Marken, die unabhaengig voneinander auf und ab gehen,
   lesen sich als Gezappel. Rueckmeldung dazu war eindeutig.
   Bewegung gibt es jetzt nur noch als Antwort: Der Assistent hebt sich, wenn
   der Zeiger darauf liegt. Von selbst bewegt sich nichts. */

/* --- Die dunkle Platte --------------------------------------------------- */

.bot-platte {
  position: relative;
  display: grid;
  place-items: center;
  background:
    radial-gradient(85% 70% at 50% 18%, rgba(43,168,255,.16), transparent 64%),
    linear-gradient(170deg, var(--bot-platte-2), var(--bot-platte));
  border: 1px solid rgba(255,255,255,.10);
  border-radius: var(--r-lg);
  box-shadow: 0 24px 60px -30px rgba(var(--shade-rgb),.75);
}

/* --- Im Seitenkopf ------------------------------------------------------- */

/* Er steht vor der Zeichnung, nicht in ihr: unten links an der Buehne, halb
   darueber hinaus. So stuetzt er den Blick nach innen zum Text und verdeckt
   nichts, was erklaert. Unter 900 Punkten Breite stapelt sich der Seitenkopf
   -- dann ist der Platz dafuer weg, und er geht mit. */
/* ------------------------------------------------------------------
   DAS KOPFBILD HINTER DEM SEITENKOPF

   Es liegt hinter allem und ist stark abgedunkelt. Der Seitenkopf ist das
   einzige Band, das in beiden Farbschemata dunkel bleibt, und seine Schrift
   ist weiss -- ein Foto in voller Helligkeit darunter macht den Kontrast
   davon abhaengig, was zufaellig an dieser Stelle im Bild steht.

   WIE HELL. Nachgemessen in drei Stufen, je 264 Textkaesten auf zehn Seiten
   und vier Fensterbreiten (schlechtester Wert gegen die Schrift darueber):

     Deckkraft 0,44   7,89:1   erste Fassung -- sicher, aber dunkel
     Deckkraft 0,66   7,21:1
     Deckkraft 0,72   6,03:1   <- gewaehlt
     Deckkraft 0,80   4,69:1   nur 0,19 ueber der Grenze; dort will man nicht
                               sitzen, ein anderes Bild kippt das

   0,72 liegt ein Drittel ueber den geforderten 4,5 und ist damit die hellste
   Stufe, die noch Luft hat. Wer sie aendert, misst neu -- mit dem Skript,
   nicht mit dem Auge, und nicht mit axe: axe prueft Hintergrundbilder gar
   nicht (siehe docs/KOPFBILDER.md).

   Zwei Verlaeufe uebereinander:

     waagerecht  links fast deckend, wo die Ueberschrift und der Button
                 stehen; nach rechts offener, wo die Zeichnung auf ihrer
                 eigenen dunklen Platte sitzt und nichts zu verlieren hat.
     senkrecht   oben und unten dunkler, damit das Band an seinen Kanten
                 nicht aufreisst und sauber in die Seite uebergeht.

   `--bg-rgb` ist im Kontrastband auf den dunklen Wert erzeugt, in beiden
   Themen. Deshalb steht hier `--bg-rgb` und nicht `--shade-rgb`: Letzteres
   gehoert in Schatten, nie in eine Flaeche.

   Unter 820 Punkten stapelt sich der Seitenkopf, der Text laeuft dann ueber
   die volle Breite -- dort deckt der waagerechte Verlauf durchgehend.

   ALLE BILDER ABNEHMEN: Einstellungen -> Kopfbilder -> Schalter aus.
   Einzeln: am Block das Feld "Kopfbild" auf "keines".
   Welches Bild wo steht: docs/KOPFBILDER.md
   ------------------------------------------------------------------ */
.hero--bild { position: relative; isolation: isolate; }

.hero__bild {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
}
.hero__bild picture,
.hero__bild img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Der Bildausschnitt wandert nach rechts: Links steht auf den meisten Fotos
     die Wandbeschriftung mit der Wortmarke, und die gehoert nicht ein zweites
     Mal, blass und halb verdeckt, hinter die Ueberschrift.

     Das gilt fuer die neun Bilder im Verhaeltnis 3:2. Wer ein Bild mit
     anderem Verhaeltnis einhaengt, braucht hier eine eigene Zeile -- siehe
     unten. Ohne die schneidet der Ausschnitt an der falschen Stelle, und das
     sieht man erst im Browser. */
  object-position: 74% 44%;
}
.hero__bild img { opacity: .72; }

.hero__bild::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom,
      rgba(var(--bg-rgb), .42) 0%,
      rgba(var(--bg-rgb), .06) 42%,
      rgba(var(--bg-rgb), .66) 100%),
    linear-gradient(to right,
      rgba(var(--bg-rgb), .91) 0%,
      rgba(var(--bg-rgb), .84) 38%,
      rgba(var(--bg-rgb), .41) 70%,
      rgba(var(--bg-rgb), .51) 100%);
}

/* AUSSCHNITT JE BILD. Nur dort noetig, wo der gemeinsame Wert nicht passt.

   datacenter: 1920x1024 statt 1920x1280 -- deutlich breiter als die uebrigen
   neun. Beim gemeinsamen Wert 44 % schnitt der Ausschnitt Robi den Kopf ab,
   weil bei einem breiteren Bild weniger Hoehe uebrig ist, die sich verteilen
   laesst. 24 % nimmt oben weniger weg; 58 % waagerecht haelt ihn aus der
   Textspalte heraus, ohne ihn an den Rand zu druecken. */
.hero__bild--datacenter img { object-position: 58% 24%; }

/* Alles, was im Band steht, gehoert vor das Bild. */
.hero--bild > .container { position: relative; z-index: 1; }

@media (max-width: 820px) {
  .hero__bild img { opacity: .56; }
  .hero__bild::after {
    background:
      linear-gradient(to bottom,
        rgba(var(--bg-rgb), .58) 0%,
        rgba(var(--bg-rgb), .38) 40%,
        rgba(var(--bg-rgb), .8) 100%),
      linear-gradient(to right,
        rgba(var(--bg-rgb), .86) 0%,
        rgba(var(--bg-rgb), .82) 100%);
  }
}

/* ROBI BEKOMMT EINE EIGENE SPALTE -- und zwar unter der Buehne, neben der
   Bildunterschrift.

   Drei Anlaeufe, alle gemessen:

   1. An der Unterkante der ganzen Spalte: Darunter steht die
      Bildunterschrift, und er stand mitten in deren ersten zwei Zeilen.
   2. An der Unterkante der BUEHNE, zur Haelfte ueber die linke Kante
      hinaus: Auf der Startseite ging das auf. Bei 39 verschiedenen
      Zeichnungen aber nicht -- jede hat ihre Beschriftungen an einer anderen
      Stelle. Die Pruefung ueber sieben Seiten und fuenfzehn Fensterbreiten
      meldete 36 von 105 Faellen mit Ueberdeckung: "Wiederherstellung" im
      Wissensbereich, "Eine Stoerung ist erledigt" beim Support, und auf der
      Startseite ragte er bei groesserer Darstellung in die Textspalte.
   3. Eine eigene Rasterzelle. Die Buehne behaelt ihre volle Breite, die
      Bildunterschrift ihre, und er steht rechts daneben. Er kann dort nichts
      verdecken, weil er nichts ueberlappt -- unabhaengig davon, welche
      Zeichnung auf der Seite steht und wie breit das Fenster ist.

   `.mini` ist dafuer auf `display: contents` gesetzt; das ist gefahrlos, weil
   die Klasse nur `display: grid` und einen Abstand traegt -- beides steht
   hier am Elternteil wieder. */
.hero__visual {
  position: relative;
  display: grid;
  gap: var(--s-3);
  grid-template-areas: 'buehne' 'text';
}
.hero__visual--bot {
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: 'buehne buehne' 'text bot';
  column-gap: clamp(.75rem, 2vw, 1.75rem);
}
.hero__visual .mini          { display: contents; }
.hero__visual .mini__stage   { grid-area: buehne; }
.hero__visual .mini__caption { grid-area: text; }

.hero__bot {
  --bot-breite: clamp(76px, 7.5vw, 104px);
  grid-area: bot;
  align-self: end;
  width: calc(var(--bot-breite) * var(--bot-faktor, 1));
  pointer-events: none;
  filter: drop-shadow(0 18px 28px rgba(0,0,0,.5));
}

/* ------------------------------------------------- Ausgleich je Haltung

   Die vier Vorlagen des Auftraggebers sind unterschiedlich proportioniert:
   Der gehende, winkende Robi ist fast quadratisch (0,97 hoch zu breit), der
   mit dem Daumen schmal und hoch (1,26). Die CSS setzt aber die BREITE --
   ohne Ausgleich stuende derselbe Robi auf der einen Seite anderthalbmal so
   hoch wie auf der anderen.

   Der Faktor rechnet jede Haltung auf dieselbe gerenderte HOEHE: das
   1,336-fache der Grundbreite. Diese Zahl ist nicht gewaehlt, sondern
   uebernommen -- sie ist die Hoehe, die der winkende Robi seit 1.7.3 hat und
   die der Auftraggeber gesehen und bestaetigt hat.

   Bis 1.7.3 trug jede Haltung ihre eigene Hoehe, weil jede ihren eigenen
   alten Zuschnitt fortschrieb. Das ergab zwischen 108 und 159 Punkten auf
   sonst gleich gebauten Seiten. Mit vier Bildern fuer alle Seiten liest sich
   das nicht mehr als Absicht, sondern als Versehen; deshalb jetzt eine Hoehe
   fuer alle.

   Die Dateien folgen derselben Rechnung und sind deshalb alle 553 Punkte
   hoch: 3 (Punktdichte) x 138 (groesste Grundbreite, Startseite ohne
   Kopfbild) x Faktor x Seitenverhaeltnis. Der Selbsttest haelt das fest. */
.hero__bot:has(.bot--winkt)     { --bot-faktor: 1.377; }
.hero__bot:has(.bot--daumen)    { --bot-faktor: 1.061; }
.hero__bot:has(.bot--feiert)    { --bot-faktor: 1.123; }
.hero__bot:has(.bot--kopfstand) { --bot-faktor: 1.138; }

/* Auf der Startseite darf er groesser sein. Dort ist Platz, dort begruesst
   er, und dort ist er das erste, was jemand von der Marke sieht. Weiter
   hinauf geht es nicht sinnvoll: Die Vorlage ist 199 Punkte hoch, und ab
   etwa 140 Punkten Anzeigegroesse rechnet ein Bildschirm mit doppelter
   Punktdichte sie hoch, statt sie zu verkleinern -- dann wird sie weich. */
.hero__bot--gross { --bot-breite: clamp(96px, 10vw, 138px); }

/* Unter 900 Punkten stapelt sich der Seitenkopf, und die Spalte wird so
   schmal, dass die Figur neben der Bildunterschrift zu viel Platz naehme.
   Bis 1.7.5 verschwand sie dort ganz -- auf dem Telefon gab es Robi also
   nicht, und genau das wurde gemeldet.

   Jetzt steht er unter der Bildunterschrift am rechten Rand statt daneben.
   Der Text behaelt damit die volle Breite -- neben ihm waere die Spalte auf
   260 Punkte geschrumpft und der Absatz um drei bis vier Zeilen gewachsen --
   und die Figur sitzt da, wo der Blick am Ende des Seitenkopfs ohnehin
   ankommt. Kleiner als auf dem Desktop: Sie ist auf dem Telefon ein
   Zeichen, keine zweite Bildhaelfte. */
@media (max-width: 900px) {
  .hero__visual--bot {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'buehne' 'text' 'bot';
  }
  .hero__bot {
    --bot-breite: clamp(62px, 17vw, 88px);
    justify-self: end;
    margin-top: calc(-1 * var(--s-3));
  }
}

/* --- Der Assistent ------------------------------------------------------- */

/* Als <details>. Das oeffnet und schliesst mit Maus, Tastatur und Vorleser,
   ohne eine Zeile JavaScript -- und es ist auch dann bedienbar, wenn ein
   Skript nicht geladen hat. Das Skript macht nur zwei Dinge dazu: Escape und
   ein Klick daneben schliessen. */
.bot-assist {
  position: fixed;
  z-index: 60;
  right: clamp(12px, 2.5vw, 26px);
  bottom: max(clamp(12px, 2.5vw, 26px), env(safe-area-inset-bottom, 0px));
}
.bot-assist__knopf {
  display: grid;
  place-items: center;
  width: clamp(64px, 7vw, 74px);
  height: clamp(50px, 5.6vw, 58px);
  padding: 7px 8px;
  border-radius: var(--r-md);
  cursor: pointer;
  list-style: none;
  transition: transform .35s cubic-bezier(.22,.8,.3,1), box-shadow .35s ease;
}
.bot-assist__knopf::-webkit-details-marker { display: none; }
.bot-assist__knopf:hover,
.bot-assist__knopf:focus-visible { transform: translateY(-3px); }
.bot-assist__knopf:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Der stille Hinweis neben dem Knopf. Er steht nur, solange zugeklappt ist,
   und verschwindet, sobald jemand geoeffnet hat -- zweimal dasselbe zu sagen
   ist einmal zu viel. */
.bot-assist__wink {
  position: absolute;
  right: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  padding: .45em 1em;
  font-size: .82rem;
  font-weight: 500;
  box-shadow: 0 10px 30px -16px rgba(var(--shade-rgb),.5);
  pointer-events: none;
}
.bot-assist[open] .bot-assist__wink { display: none; }
@media (max-width: 640px) { .bot-assist__wink { display: none; } }

.bot-assist__blase {
  position: absolute;
  right: 0;
  bottom: calc(100% + 12px);
  width: min(306px, calc(100vw - 28px));
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  box-shadow: 0 26px 70px -28px rgba(var(--shade-rgb),.6);
  padding: var(--s-5);
  animation: bot-blase .28s cubic-bezier(.22,.8,.3,1);
}
@keyframes bot-blase {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.bot-assist__blase h2 { font-size: 1rem; margin: 0 0 .35em; }
.bot-assist__blase p {
  margin: 0 0 var(--s-4);
  font-size: .88rem;
  line-height: 1.6;
  color: var(--muted-strong);
}
.bot-assist__wege { display: grid; gap: .5rem; }
.bot-assist__weg {
  display: flex;
  align-items: center;
  gap: .6em;
  padding: .6em .8em;
  border-radius: var(--r-sm);
  background: var(--surface-3);
  color: var(--text);
  font-size: .88rem;
  font-weight: 500;
  text-decoration: none;
}
.bot-assist__weg:hover { background: var(--surface-2); color: var(--accent); }
.bot-assist__weg .icon { color: var(--accent); flex: none; }

/* Ganz unten ist Schluss: Was der Assistent dort verdeckt, verdeckt er
   endgueltig -- und in der Fusszeile stehen Telefonnummer, E-Mail und die
   Rechtslinks. Deshalb bekommt sie unten so viel Luft, wie er hoch ist.
   Mitten auf der Seite braucht es das nicht: Dort loesen zwei Zeilen
   Scrollen jede Ueberdeckung. */
.hat-assist .site-footer { padding-bottom: clamp(66px, 5vw, 76px); }

@media print { .bot-assist { display: none; } }

/* --- Quittung nach dem Absenden ------------------------------------------ */

/* Er steht links unter der Bestaetigung, nicht darin: Die Meldung traegt die
   Aussage und muss fuer Vorleser eine Meldung bleiben. Er ist das Zeichen
   daneben, mehr nicht. */
.bot-quittung { margin-top: var(--s-5); }
.bot-quittung__platte {
  display: grid;
  place-items: center;
  /* 92px bis 1.7.2. Das Bild fuellt die Breite der Platte, die Hoehe folgt
     dem Seitenverhaeltnis -- und das ist bei der hochaufloesenden Vorlage
     flacher (348x438 statt 143x201). Bei 92px waere der Daumen hoch hier
     zehn Prozent kleiner geworden. 100px halten die Hoehe. */
  width: 100px; height: 104px;
  padding: 10px 10px 4px;
  border-radius: var(--r-md);
}

/* --- Auf der 404-Seite ---------------------------------------------------- */

/* Links auf der gepflegten 404-Seite, mittig auf der Notfallvorlage --
   beide folgen damit der Ausrichtung ihres Textes. */
.bot-404 { display: flex; margin-bottom: var(--s-6); }
.section--center .bot-404,
.text-center .bot-404 { justify-content: center; }
.bot-404__platte {
  display: grid;
  place-items: center;
  /* 132x116 bis 1.7.3, als der Robi hier sass -- eine breite Platte um eine
     breite Figur. Der Handstand ist hochkant (1,17 hoch zu breit statt
     0,99), und das Bild fuellt immer die Breite der Platte. Bei 132 Punkten
     Breite waere er 124 hoch geworden und 34 Punkte unten herausgestanden.
     116x132 haelt die Figur auf ihrer bisherigen Hoehe von rund 105 Punkten
     und dreht die Platte mit. */
  width: 116px; height: 132px;
  padding: 12px;
  border-radius: var(--r-lg);
}

/* --- Hinweisgeber im Fliesstext ------------------------------------------ */

/* Er steht neben einem Abschnittskopf und sagt: hier wird etwas erklaert.
   Klein, auf dunkler Scheibe, ohne eigene Aussage. */
.bot-hinweis { display: flex; align-items: flex-start; gap: var(--s-5); }
.bot-hinweis__scheibe {
  flex: none;
  display: grid;
  place-items: center;
  width: 62px; height: 50px;
  padding: 7px;
  border-radius: var(--r-md);
}
.bot-hinweis__text { flex: 1 1 auto; min-width: 0; }
.bot-hinweis__text > :last-child { margin-bottom: 0; }
@media (max-width: 640px) {
  .bot-hinweis__scheibe { width: 50px; height: 40px; padding: 6px; }
}

/* --- In der Fusszeile ---------------------------------------------------- */

/* Der KI-Hinweis mit dem Zeichen davor. Oben ausgerichtet, nicht mittig:
   Auf dem Telefon bricht der Satz auf drei Zeilen um, und ein Zeichen in
   deren Mitte sieht aus, als waere es verrutscht. */
.footer-ai--bot {
  display: flex;
  align-items: flex-start;
  gap: .8em;
}
/* Die Zeile aus dem Charakterblatt unter der Marke: eine Stufe leiser als die
   Beschreibung darueber, in der Akzentfarbe, damit sie als Unterschrift und
   nicht als weiterer Satz gelesen wird. */
.footer-claim {
  margin: 0;
  font-size: .8rem;
  font-style: italic;
  color: var(--accent);
}

.footer-bot__scheibe {
  flex: none;
  display: grid;
  place-items: center;
  width: 38px; height: 30px;
  padding: 4px;
  border-radius: 8px;
}

/* --- Dunkles Kontrastband ------------------------------------------------ */

/* Eine Flaeche, die in BEIDEN Themen dunkel bleibt. Im dunklen Thema ist sie
   die vertraute Vertiefung, im hellen der Gegenpol zu Weiss und Weissraum.
   Die Schriftfarben werden darin neu gesetzt -- sonst stuende im hellen
   Thema dunkler Text auf dunklem Grund. */
.section--kontrast {
  /* ANFANG erzeugt aus :root -- nicht von Hand aendern */
  --tint-rgb: 255,255,255;
  --shade-rgb: 0,0,0;
  --accent-rgb: 0,217,255;
  --accent2-rgb: 71,119,255;
  --success-rgb: 56,232,165;
  --warn-rgb: 255,194,75;
  --danger-rgb: 255,107,107;
  --bg-rgb: 7,10,15;
  --bg2-rgb: 11,17,24;
  --surface-rgb: 17,25,35;
  --mega-rgb: 13,19,28;
  --muted-rgb: 152,167,179;
  --bg: #070A0F;
  --bg-2: #0B1118;
  --bg-deep: #04070B;
  --surface: #111923;
  --surface-2: #16212C;
  --surface-3: #1C2836;
  --accent: #00D9FF;
  --accent-ink: #04222B;
  --accent-dim: #0FA6C4;
  --accent-hover: #6FEBFF;
  --accent-2: #4777FF;
  --success: #38E8A5;
  --warn: #FFC24B;
  --warn-ink: #20160A;
  --danger: #FF6B6B;
  --white: #F4F8FA;
  --text: #D9E3E8;
  --muted: #98A7B3;
  --muted-strong: #B4C1CB;
  --muted-contrast: #C2CFD8;
  --muted-strong-contrast: #E2EAEF;
  --surface-contrast: #0E151E;
  --border: rgba(var(--tint-rgb),.08);
  --border-strong: rgba(var(--tint-rgb),.16);
  --border-accent: rgba(var(--accent-rgb),.42);
  --header-opaque: #070A0F;
  --mega-opaque: #0B1118;
  --rack-1: #0C131B;
  --rack-2: #070B10;
  --shadow-header: 0 12px 40px -30px rgba(var(--shade-rgb),.9);
  --glow-accent: 0 0 0 1px rgba(var(--accent-rgb),.30), 0 18px 60px -24px rgba(var(--accent-rgb),.42);
  --shadow-card: 0 1px 2px rgba(var(--shade-rgb),.4), 0 18px 48px -28px rgba(var(--shade-rgb),.9);
  --shadow-float: 0 24px 70px -30px rgba(var(--shade-rgb),.95), 0 2px 6px rgba(var(--shade-rgb),.4);
  color-scheme: dark;
  /* ENDE erzeugt */
  color: var(--text);
  background:
    radial-gradient(88% 72% at 76% 10%, rgba(var(--accent2-rgb),.20), transparent 62%),
    radial-gradient(70% 60% at 10% 92%, rgba(var(--accent-rgb),.12), transparent 60%),
    linear-gradient(170deg, #111923, #05080C);
}

/* --- Bewegung reduzieren -------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  /* Er steht ohnehin still; hier faellt zusaetzlich das Anheben beim Zeigen
     weg. Die Figur selbst bleibt. */
  .bot-assist__knopf { transition: none; }
  .bot-assist__knopf:hover { transform: none; }
  .bot-assist__blase { animation: none; }
}

/* ===========================================================================
   KLEINSTE BESCHRIFTUNGEN AUF SCHMALEN GERAETEN

   Nachgemessen auf einem 320-Punkte-Telefon:

     .brand__sub            9,3 px   "IT-Infrastruktur" unter der Wortmarke
     .contact-item__label   9,3 px   "Standort" in der Fusszeile
     .footer-col h2        10,1 px   Spaltentitel der Fusszeile

   Alle drei stehen in `rem` und haengen damit an der Wurzelgroesse von
   16 Punkten -- nicht an der fluiden Koerpergroesse. Sie sind deshalb auf
   dem Telefon GENAUSO klein wie auf einem 1440er Schirm, waehrend der
   Fliesstext von 16 auf 18,4 Punkte mitwaechst. Der Abstand zwischen beiden
   geht dabei von 1,7-fach auf 2,0-fach auseinander.

   9,3 Punkte in Versalien, in einer Festbreitenschrift und mit 0,18em
   gesperrt -- das ist unter der Grenze, an der die meisten Leute das noch
   entziffern. Auf schmalen Geraeten bekommen die drei deshalb eine
   Untergrenze. Auf dem Schreibtisch bleibt alles, wie es war: Dort stehen
   sie in einer ruhigen Zeile und sind als feines Detail gewollt.

   WARUM DIESER BLOCK GANZ UNTEN STEHT: Beim ersten Versuch stand er bei den
   Zeichnungen, also vor `.footer-col h2` (Zeile ~5807) und vor
   `.contact-info--row .contact-item__label` (~5860). Gleiche Gewichtung,
   spaeter im Stylesheet -- die alten Regeln gewannen. Nachgemessen hat dann
   nur die Wortmarke gewirkt, die beiden anderen standen unveraendert bei
   9,3 und 10,1 Punkten. Am Dateiende haelt nichts mehr dagegen.
   =========================================================================== */
/* Dieselbe Begruendung wie bei den Fusszeilenlinks: Diese Verweise stehen
   als Liste nebeneinander, nicht in einem Satz. Gemessen waren sie 21 bis
   22 Punkte hoch -- zwei bis drei zu wenig. Die Brotkrumen hatten 16.
   Gelöst ueber Polsterung am Link, nicht ueber Zeilenhoehe: Letztere
   verschiebt nur den Text in seiner Zeile, die Flaeche bleibt. */
@media (max-width: 700px) {
  .footer-bottom nav a { display: inline-block; padding-block: .3rem; }

  /* Der Fernwartungslink ist ein inline-flex und haelt damit Text und
     Pfeilzeichen in einer Zeile. Die erste Fassung dieser Regel lautete
     `.contact-item__value a { display: inline-block }` -- das ist um eine
     Klasse spezifischer als `.contact-item__ext` und hat ihn mit
     umgeworfen. Danach stand das Zeichen als loser Strich unter
     "Sitzung starten".

     Aufgefallen ist das auf dem Bildschirmfoto, nicht in den Messwerten:
     Die Trefferflaeche war ja richtig, nur das Bild war falsch. */
  .contact-item__value a:not(.contact-item__ext) { display: inline-block; padding-block: .3rem; }
  .contact-item__value a.contact-item__ext { padding-block: .3rem; }
  .breadcrumb a,
  .breadcrumb [aria-current='page'] { display: inline-block; padding-block: .25rem; }
  /* Der Abstand zwischen den Brotkrumen darf dabei nicht mitwachsen. */
  .breadcrumb ol { row-gap: 0; }
}

@media (max-width: 700px) {
  .brand__sub          { font-size: .72rem; letter-spacing: .14em; }   /* 9,3 -> 11,5 */
  .contact-item__label { font-size: .78rem; }                          /* 9,3 -> 12,5 */
  .footer-col h2       { font-size: .76rem; }                          /* 10,1 -> 12,2 */
  .contact-info--row .contact-item__label { font-size: .78rem; }
}

/* ===========================================================================
   Einwilligung: Leiste, Dialog, Tabellen
   ===========================================================================

   Grundsatz dieser Gestaltung: Zustimmen und Ablehnen sehen GLEICH aus.
   Beide Schaltflaechen tragen .btn--secondary, haben dieselbe Hoehe,
   dieselbe Schriftstaerke und denselben Rahmen. Der haeufigste
   Beanstandungsgrund europaeischer Datenschutzbehoerden ist ein gruener
   Zustimmungsknopf neben einem grauen Textlink -- das ist hier baulich
   ausgeschlossen, nicht nur Absicht.

   Die Leiste steht fest am unteren Rand und verdeckt damit, was dort liegt.
   Gegen den Assistenten unten rechts (.hat-assist) ist das geregelt: Solange
   die Leiste steht, rueckt er hoch -- siehe weiter unten. */

.cookie-bar {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 70;              /* ueber dem Assistenten (60), unter dem Dialog */
  background: var(--header-opaque);
  border-top: 1px solid var(--border-strong);
  box-shadow: var(--shadow-float);
  padding: var(--s-5) var(--gutter);
  padding-bottom: calc(var(--s-5) + env(safe-area-inset-bottom, 0px));
  animation: cookie-auf var(--dur) var(--spring) both;
}
@keyframes cookie-auf {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .cookie-bar { animation: none; }
}

.cookie-bar__inner {
  max-width: var(--container);
  margin-inline: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-5) var(--s-7);
}
.cookie-bar__text { flex: 1 1 32ch; min-width: 0; }
.cookie-bar__titel {
  font-size: 1.05rem;
  line-height: 1.3;
  letter-spacing: -.012em;
  margin: 0 0 .35rem;
  color: var(--white);
}
.cookie-bar__text p {
  margin: 0;
  font-size: .94rem;
  line-height: 1.55;
  color: var(--muted-strong);
  max-width: 68ch;
}
.cookie-bar__links {
  display: flex;
  flex-wrap: wrap;
  gap: .2rem var(--s-4);
  margin-top: .35rem !important;
  font-size: .88rem;
}
/* Die beiden Rechtslinks standen bei 23 px Hoehe knapp unter der
   24-px-Regel (WCAG 2.5.8) -- gemessen, nicht geschaetzt. Der Innenabstand
   hebt sie darueber und ersetzt zugleich den vorherigen Aussenabstand, die
   Leiste wird dadurch nicht hoeher. */
.cookie-bar__links a {
  display: inline-block;
  padding-block: .25rem;
}

.cookie-bar__aktionen {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  flex: 0 1 auto;
}
/* Gleiche Mindestbreite fuer Zustimmung und Ablehnung: Zwei Knoepfe
   verschiedener Breite lesen sich als verschieden wichtig, auch wenn sie
   dieselbe Farbe tragen. */
.cookie-bar__btn { min-width: 11rem; }
.cookie-bar__btn.btn--ghost { min-width: 0; padding-inline: 1rem; }

/* Auf dem Telefon stand die Leiste 574 px hoch -- zwei Drittel eines
   844-px-Schirms, und der Inhalt, wegen dem jemand gekommen ist, darunter.
   Gemessen, nicht geschaetzt.

   Drei Eingriffe, zusammen auf 232 px:
     1. Der Bannertext ist kuerzer (Einstellungen "consent_text"). Die
        Einzelheiten stehen ohnehin einen Tipp entfernt im Dialog -- im
        Banner gehoert der Satz, nicht die Aufstellung.
     2. Die beiden Entscheidungen stehen NEBENEINANDER, nicht untereinander.
        Sie bleiben damit auch raeumlich gleichrangig; untereinander liest
        sich die obere als die gemeinte.
     3. "Einstellungen" darunter ueber die volle Breite -- es ist keine
        Entscheidung, sondern der Weg zu den Einzelheiten.

   Verworfen: die Leiste scrollbar machen. Eine Entscheidung, die man erst
   herunterrollen muss, um sie abzulehnen, ist keine gleichrangige. */
@media (max-width: 860px) {
  .cookie-bar { padding: var(--s-4) var(--gutter); }
  .cookie-bar__inner { flex-direction: column; align-items: stretch; gap: var(--s-4); }
  /* flex-basis gilt in der Hauptachse. In der Zeile oben ist das die
     Breite (32ch, so gewollt) -- in der Spalte hier waere es die HOEHE,
     und 32ch sind gemessene 207 px Luft unter einem dreizeiligen Absatz.
     Der Block war dadurch 326 px hoch statt 119. */
  .cookie-bar__text { flex: 0 0 auto; }
  .cookie-bar__aktionen {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s-3);
  }
  .cookie-bar__btn { min-width: 0; width: 100%; padding-inline: .6rem; font-size: .94rem; }
  /* Zwei Woerter nebeneinander in 11 rem: "Alle akzeptieren" bricht sonst
     mitten im Wort, wenn die Schriftgroesse des Geraets groesser steht. */
  .cookie-bar__btn.btn--secondary { hyphens: auto; line-height: 1.2; }
  .cookie-bar__btn.btn--ghost {
    grid-column: 1 / -1;
    width: 100%;
    min-width: 0;
  }
  .cookie-bar__text p { font-size: .9rem; }
}

/* Der Assistent weicht der Leiste aus.
   Er ist fest positioniert und laege sonst darunter -- unbedienbar genau
   beim ersten Besuch, also dann, wenn jemand am ehesten eine Frage hat.
   Ihn solange auszublenden waere die einfachere Loesung gewesen und ist
   verworfen: Wer erst entscheiden MUSS, um Hilfe zu bekommen, wird zur
   Entscheidung gedraengt.

   Die Werte sind im Browser gemessen (siehe Projektdokument 1.7.0), nicht
   geschaetzt: --cookie-bar-h traegt die tatsaechliche Hoehe der Leiste in
   der jeweiligen Anordnung. */
:root { --cookie-bar-h: 152px; }          /* Desktop, gemessen */
@media (max-width: 860px) {
  :root { --cookie-bar-h: 292px; }        /* Telefon, gemessen */
}
body:has(.cookie-bar) .bot-assist {
  bottom: calc(var(--cookie-bar-h) + clamp(12px, 2.5vw, 26px) + env(safe-area-inset-bottom, 0px));
}

/* ------------------------------------------------------------- Der Dialog */

.cookie-dialog {
  width: min(44rem, calc(100vw - 2rem));
  max-height: min(84vh, 50rem);
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-float);
  overflow: hidden;
}
.cookie-dialog::backdrop {
  background: rgba(var(--shade-rgb), .62);
  backdrop-filter: blur(3px);
}
.cookie-dialog[open] {
  display: flex;
  flex-direction: column;
}
.cookie-dialog__kopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-5) var(--s-6);
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  flex: none;
}
.cookie-dialog__kopf h2 {
  margin: 0;
  font-size: 1.2rem;
  letter-spacing: -.014em;
  color: var(--white);
}
.cookie-dialog__zu {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;           /* Trefferflaeche deutlich ueber 24 px */
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--muted-strong);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.cookie-dialog__zu:hover {
  color: var(--white);
  background: rgba(var(--tint-rgb), .06);
}
.cookie-dialog__inhalt {
  padding: var(--s-6);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ----------------------------------------------------------- Das Formular */

.cookie-kat {
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
}
.cookie-kat__item {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--s-4) var(--s-5);
  background: rgba(var(--tint-rgb), .02);
}
.cookie-kat__kopf {
  display: flex;
  align-items: center;
  gap: var(--s-3);
}
.cookie-kat__box {
  flex: none;
  width: 1.2rem;
  height: 1.2rem;
  accent-color: var(--accent);
  cursor: pointer;
}
.cookie-kat__box:disabled { cursor: default; opacity: .55; }
/* Das Label traegt die Trefferflaeche, nicht das Kaestchen: 1,2 rem waeren
   mit dem Finger zu wenig. Es umfasst beides, weil `for` auf das Kaestchen
   zeigt -- ein Tippen irgendwo auf den Titel schaltet damit um. */
.cookie-kat__label {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex: 1 1 auto;
  min-height: 40px;
  font-weight: 600;
  font-size: 1.02rem;
  color: var(--white);
  cursor: pointer;
}
.cookie-kat__box:disabled + .cookie-kat__label { cursor: default; }
.cookie-kat__fest {
  font-size: .76rem;
  font-weight: 500;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  padding: .18rem .6rem;
}
.cookie-kat__text {
  margin: .35rem 0 0;
  padding-left: calc(1.2rem + var(--s-3));
  font-size: .92rem;
  line-height: 1.55;
  color: var(--muted-strong);
}

.cookie-form__aktionen {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-top: var(--s-6);
}
.cookie-form__aktionen .btn { min-width: 11rem; }
@media (max-width: 560px) {
  .cookie-form__aktionen { flex-direction: column; }
  .cookie-form__aktionen .btn { min-width: 0; width: 100%; }
}
.cookie-form__stand {
  margin-top: var(--s-4);
  font-size: .84rem;
  color: var(--muted);
}

/* ------------------------------------------------------------- Die Tabelle */

.cookie-tab {
  width: 100%;
  margin-top: var(--s-4);
  border-collapse: collapse;
  font-size: .88rem;
  line-height: 1.5;
}
.cookie-tab th,
.cookie-tab td {
  text-align: left;
  vertical-align: top;
  padding: .55rem .7rem;
  border-bottom: 1px solid var(--border);
}
.cookie-tab th {
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
}
.cookie-tab td { color: var(--muted-strong); }
.cookie-tab tbody tr:last-child td { border-bottom: 0; }
.cookie-tab__sub {
  display: block;
  margin-top: .2rem;
  font-size: .82rem;
  font-weight: 400;
  color: var(--muted);
}
a.cookie-tab__sub { color: var(--accent); }

/* Die Tabelle stapelt sich zu Karten, sobald sie zu schmal wird.
   Gemessen wird der RAHMEN und nicht das Fenster: Dieselbe Tabelle steht
   im Dialog (44 rem) und auf der Seite (--container-narrow, 760 px). Eine
   Fenster-Media-Query saehe diesen Unterschied nicht und wuerde die
   Dialogfassung auf grossen Schirmen gequetscht stehen lassen. */
.cookie-kat__item { container-type: inline-size; }

@container (max-width: 34rem) {
  .cookie-tab thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .cookie-tab tr {
    display: block;
    padding: .6rem 0;
    border-bottom: 1px solid var(--border);
  }
  .cookie-tab tbody tr:last-child { border-bottom: 0; }
  .cookie-tab td {
    display: grid;
    grid-template-columns: 7.5rem 1fr;
    gap: var(--s-3);
    padding: .22rem 0;
    border-bottom: 0;
  }
  .cookie-tab td::before {
    content: attr(data-label);
    font-size: .78rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--muted);
  }
}
@container (max-width: 22rem) {
  .cookie-tab td { grid-template-columns: 1fr; gap: .1rem; }
}

/* -------------------------------------------------------------- Die Seite */

.cookie-hinweis {
  margin-top: var(--s-6);
  padding: var(--s-5) var(--s-6);
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--success);
  border-radius: var(--r-md);
  background: rgba(var(--success-rgb), .05);
  container-type: inline-size;
}
.cookie-hinweis h2 {
  margin: 0 0 .4rem;
  font-size: 1.1rem;
  letter-spacing: -.012em;
  color: var(--white);
}
.cookie-hinweis p { margin: 0; color: var(--muted-strong); }
.cookie-hinweis .cookie-tab { margin-top: var(--s-5); }

.cookie-seite__fuss {
  margin-top: var(--s-8);
  padding-top: var(--s-5);
  border-top: 1px solid var(--border);
  font-size: .88rem;
  color: var(--muted);
}

/* Die Seite traegt das Formular ohne Dialograhmen -- dort bringen die
   Kategoriekarten ihren eigenen Rahmen schon mit. */
.cookie-seite__fuss + .cookie-form { margin-top: var(--s-6); }
