/* ===========================================================================
   Interprex — Website

   Bewusst ohne Framework und ohne CDN: Die Seite trägt Werbung, also zählt
   Ladezeit, und ein externer Font-Aufruf wäre schon vor der Einwilligung eine
   Datenübertragung an Dritte. Schrift kommt daher vom System.

   Farbregel des ganzen Auftritts
   ------------------------------
   Grau trägt die Seite, Farbe trägt nur Bedeutung. Der Verlauf lebt in der
   Bildmarke — dort erklärt er etwas (Licht durch ein Prisma). Überall sonst
   gibt es genau einen Akzent, und der markiert das, was man anklicken soll.
   Eine Werbeseite, die selbst schreit, sieht aus wie die Anzeigen darauf.
   ======================================================================== */

:root {
  color-scheme: dark light;

  /* Neutrale — 95 % der Fläche. */
  --bg:        #0c0d10;
  --surface:   #131519;
  --surface-2: #191c21;
  --line:      rgba(255, 255, 255, 0.085);
  --line-2:    rgba(255, 255, 255, 0.14);
  --text:      #e8eaee;
  --text-dim:  #9aa0ab;
  --text-faint:#6a707b;

  /* Akzent — einer. Gedämpftes Periwinkle, kein Neon. */
  --accent:    #6e86f7;
  --accent-2:  #8e7bf0;
  --accent-3:  #b489e4;
  --accent-ink:#ffffff;
  --accent-weak: rgba(110, 134, 247, 0.14);

  /* Zustände. */
  --ok:   #4fa97f;
  --warn: #c99a46;
  --bad:  #c9695e;

  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
  --display: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --page: 1140px;
  --r:    12px;
  --r-lg: 18px;

  /* Der Seitenrand als Variable, nicht als Zahl an zwei Stellen. Streifen, die
     bis an den Bildschirmrand laufen sollen (Navigation, Inhaltsverzeichnis),
     ziehen sich damit über `calc(var(--wrap-pad) * -1)` genau um diesen Betrag
     wieder heraus — sonst stimmt das Ergebnis in einer der beiden Breiten nie. */
  --wrap-pad: 28px;

  /* Eine Kurve für alles. Unterschiedliche Kurven auf einer Seite wirken
     unruhig, auch wenn niemand benennen kann, warum. */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --shadow: 0 1px 2px rgba(0,0,0,.28), 0 12px 32px -12px rgba(0,0,0,.5);
}

@media (prefers-color-scheme: light) {
  :root {
    --bg:        #fafafb;
    --surface:   #ffffff;
    --surface-2: #f3f4f6;
    --line:      rgba(12, 14, 20, 0.10);
    --line-2:    rgba(12, 14, 20, 0.17);
    --text:      #14161a;
    --text-dim:  #575e6a;
    --text-faint:#878e9a;
    --accent:    #4a63d8;
    --accent-2:  #6d55d0;
    --accent-weak: rgba(74, 99, 216, 0.10);
    --ok:   #2f7f5b;
    --warn: #9b7327;
    --bad:  #a94a40;
    --shadow: 0 1px 2px rgba(12,14,20,.06), 0 12px 30px -14px rgba(12,14,20,.18);
  }
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

/* Die Kopfzeile klebt oben. Ohne diesen Abstand springt ein Sprungziel exakt
   unter sie und die Überschrift, die man angesteuert hat, ist verdeckt — man
   landet mitten im Absatz und sucht. Auf dem Handy ist die Leiste zweizeilig
   und braucht entsprechend mehr. */
[id] { scroll-margin-top: 76px; }
@media (max-width: 940px) { [id] { scroll-margin-top: 112px; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 400 16.5px/1.62 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Ein einziger, sehr flacher Lichtschein oben — kein Farbnebel über der
   ganzen Seite. Er sitzt hinter der Kopfzeile und ist eher zu ahnen als zu
   sehen; das ist die Absicht. */
body::before {
  content: "";
  position: fixed;
  inset: -40vh -10vw auto -10vw;
  height: 80vh;
  background: radial-gradient(46% 52% at 50% 100%,
              color-mix(in srgb, var(--accent) 13%, transparent), transparent 70%);
  pointer-events: none;
  z-index: 0;
}

.wrap {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--wrap-pad);
  position: relative;
  z-index: 1;
}
@media (max-width: 640px) { :root { --wrap-pad: 20px; } }

a { color: inherit; text-decoration-color: var(--line-2); text-underline-offset: 3px; }
a:hover { text-decoration-color: var(--accent); }

::selection { background: color-mix(in srgb, var(--accent) 32%, transparent); }

:where(a, button, input, summary):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 5px;
}

/* ---------------------------------------------------------------------------
   Bewegung
   ---------------------------------------------------------------------------
   Zwei Regeln: Nichts bewegt sich länger als eine halbe Sekunde, und nichts
   bewegt sich, ohne dass ein Zustand wechselt. Wer „Bewegung reduzieren"
   eingestellt hat, bekommt alles sofort — das ist kein Detail, sondern für
   manche Leute der Unterschied zwischen benutzbar und übel.
------------------------------------------------------------------------- */

/* Sichtbar ist der Normalzustand.
   ------------------------------------------------------------------------
   Der Versteck-Zustand kommt erst, wenn das Skript ihn setzt (`will-reveal`) —
   nicht aus dem Stylesheet heraus. Der Unterschied ist keine Feinheit: Ein
   Inhalt, den nur JavaScript sichtbar machen kann, ist bei jedem Skriptfehler,
   bei blockiertem JavaScript und in jedem Leser-/Übersetzungsmodus schlicht
   weg. Genau das ist die häufigste Art, wie „Auftauchen beim Scrollen“ eine
   Seite unbrauchbar macht — und man merkt es nie, weil es bei einem selbst
   funktioniert. */
.reveal.will-reveal {
  opacity: 0;
  transform: translateY(12px);
  will-change: opacity, transform;
}
.reveal.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 0.55s var(--ease), transform 0.55s var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal, .reveal.will-reveal, .reveal.is-in {
    opacity: 1; transform: none; transition: none;
  }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ---------------------------------------------------------------------------
   Kopfzeile
------------------------------------------------------------------------- */

.top {
  position: sticky; top: 0; z-index: 40;
  backdrop-filter: saturate(140%) blur(18px);
  -webkit-backdrop-filter: saturate(140%) blur(18px);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease);
}
/* Die Trennlinie erscheint erst, wenn wirklich etwas darunter liegt. */
.top.is-stuck { border-bottom-color: var(--line); }
.top .wrap { display: flex; align-items: center; gap: 26px; height: 60px; }

.brand {
  display: inline-flex; align-items: center; gap: 10px;
  font: 600 17.5px/1 var(--display);
  letter-spacing: -0.015em;
  text-decoration: none;
  flex: 0 0 auto;
}
.brand svg, .brand img { width: 26px; height: 26px; display: block; border-radius: 6.5px; }
.brand .word { color: var(--text); }

.top nav { margin-left: auto; display: flex; gap: 4px; font-size: 14.5px; }
.top nav a {
  color: var(--text-dim); text-decoration: none;
  padding: 7px 11px; border-radius: 8px;
  transition: color 0.18s var(--ease), background 0.18s var(--ease);
  white-space: nowrap;
}
.top nav a:hover { color: var(--text); background: var(--surface-2); }
.top nav a[aria-current] { color: var(--text); }

.top .btn { margin-left: 6px; padding: 8px 15px; font-size: 14.5px; }

/* Kopfzeile auf schmalen Bildschirmen: zwei Zeilen.
   ---------------------------------------------------------------------------
   Vorher stand alles in einer Zeile, der Download-Knopf war ausgeblendet und
   die Navigation lief stumm nach rechts aus dem Bild. Beides war genau
   verkehrt herum: Auf dem Gerät, von dem die meisten Besucher kommen,
   verschwand ausgerechnet das, wofür sie da sind, und von sechs Verweisen
   waren drei unsichtbar — ohne jedes Anzeichen, dass da noch etwas kommt.

   Jetzt: Zeile eins trägt Marke und Download, Zeile zwei die Navigation als
   Streifen. Der Streifen scrollt weiterhin, aber seine rechte Kante wird
   weich ausgeblendet. Diese Kante ist die ganze Botschaft — sie sagt „hier
   geht es weiter", und zwar ohne Skript und ohne Menüknopf. */
@media (max-width: 940px) {
  .top .wrap {
    flex-wrap: wrap;
    height: auto;
    gap: 0 12px;
    padding-top: 5px;
  }
  /* Die Bildmarke ist der Weg zurück zur Startseite und war mit 26 px das
     kleinste Ziel der ganzen Seite. Der Innenabstand bringt sie auf 44, ohne
     dass sich am Bild etwas ändert. */
  .brand { flex: 1 1 auto; min-width: 0; padding: 9px 0; }

  .top .btn { display: inline-flex; margin-left: 0; padding: 9px 15px; font-size: 14px; }

  .top nav {
    order: 3;
    flex: 1 0 100%;
    margin: 5px calc(var(--wrap-pad) * -1) 0;
    padding: 0 var(--wrap-pad) 7px;
    gap: 0;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* Die Kante verblasst nur bis 40 %, nicht bis 0. Ganz durchsichtig sähe
       der letzte sichtbare Verweis am Ende des Streifens aus wie ein
       deaktivierter Knopf — der Hinweis „hier geht es weiter" würde zur
       Falschaussage, sobald man durchgescrollt hat. */
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 30px), rgba(0,0,0,.4));
            mask-image: linear-gradient(to right, #000 calc(100% - 30px), rgba(0,0,0,.4));
  }
  .top nav::-webkit-scrollbar { display: none; }
  /* 44 px Höhe ist keine Zierde: Darunter trifft ein Daumen zuverlässig den
     Nachbarn statt das Ziel. */
  .top nav a { padding: 11px 10px; font-size: 14.5px; }
}

/* ---------------------------------------------------------------------------
   Schaltflächen
------------------------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 20px; border-radius: 10px;
  border: 1px solid transparent;
  font: 500 15.5px/1 var(--sans);
  text-decoration: none; cursor: pointer;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease),
              transform 0.18s var(--ease), box-shadow 0.18s var(--ease);
}
.btn:active { transform: translateY(1px); }
/* Auf dem Handy etwas höher: 42 px sind knapp unter der Schwelle, ab der ein
   Daumen zuverlässig trifft. Die zwei Pixel kosten nichts und ersparen den
   zweiten Versuch. */
@media (max-width: 640px) { .btn { padding: 13px 20px; } }

.btn-primary {
  background: var(--accent); color: var(--accent-ink);
  box-shadow: 0 1px 2px rgba(0,0,0,.2);
}
.btn-primary:hover {
  background: color-mix(in srgb, var(--accent) 88%, #fff);
  box-shadow: 0 6px 18px -6px color-mix(in srgb, var(--accent) 60%, transparent);
}

.btn-ghost { border-color: var(--line-2); color: var(--text); background: transparent; }
.btn-ghost:hover { background: var(--surface-2); border-color: var(--line-2); }

.btn-light { background: var(--text); color: var(--bg); }
.btn-light:hover { background: color-mix(in srgb, var(--text) 88%, var(--bg)); }

.btn[aria-disabled="true"], .btn.is-disabled {
  opacity: 0.55; pointer-events: none;
}

/* ---------------------------------------------------------------------------
   Hero
------------------------------------------------------------------------- */

.hero-section { border-top: 0; padding-bottom: 0; }
/* Abschnitt und Hero bringen beide einen oberen Abstand mit — auf breiten
   Bildschirmen ergibt das eine großzügige Luft über der Überschrift, auf einem
   844 px hohen Handy zusammen 104 px Nichts, bevor irgendetwas beginnt. Auf
   dem Handy zählt einer davon. */
@media (max-width: 940px) { .hero-section { padding-top: 0; } }

.hero {
  padding: 84px 0 40px;
  display: grid; grid-template-columns: 1.12fr 0.88fr;
  gap: 60px; align-items: start;
}
@media (max-width: 940px) { .hero { grid-template-columns: 1fr; gap: 34px; padding: 52px 0 30px; } }

.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font: 500 12.5px/1 var(--sans);
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--text-dim);
  border: 1px solid var(--line); border-radius: 999px;
  padding: 7px 13px;
  background: var(--surface);
}
.eyebrow .dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--ok);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent);
}

h1 {
  font: 600 clamp(38px, 5.4vw, 58px)/1.06 var(--display);
  letter-spacing: -0.032em;
  margin: 24px 0 20px;
  max-width: 16ch;
  text-wrap: balance;
}
/* Kein Verlauf im Fließtext — der bleibt der Bildmarke vorbehalten. Betonung
   entsteht hier durch Helligkeit, nicht durch Farbe. */
h1 em { font-style: normal; color: var(--text-dim); }

.lede {
  font-size: 19px; line-height: 1.58; color: var(--text-dim);
  max-width: 50ch; margin: 0 0 30px;
}

.cta-row { display: flex; flex-wrap: wrap; gap: 11px; align-items: center; }
.cta-note { font-size: 13.5px; line-height: 1.55; color: var(--text-faint); margin-top: 16px; }
.cta-note code { font-family: var(--mono); }

/* Faktenkarte statt Browser-Mockup: Wer eine Systemvoraussetzung sucht,
   soll sie lesen und nicht auf einem Bildschirmfoto suchen. */
.facts {
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); overflow: hidden;
  box-shadow: var(--shadow);
}
.facts h2 {
  font: 500 12px/1 var(--sans); letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--text-faint); margin: 0; padding: 15px 20px;
  border-bottom: 1px solid var(--line);
}
.facts dl { margin: 0; }
.facts .row {
  display: flex; justify-content: space-between; gap: 18px;
  padding: 11px 20px; border-bottom: 1px solid var(--line); font-size: 14.5px;
}
.facts .row:last-child { border-bottom: 0; }
.facts dt { color: var(--text-dim); margin: 0; }
.facts dd { margin: 0; text-align: right; font-weight: 500; color: var(--text); }

/* ---------------------------------------------------------------------------
   Abschnitte
------------------------------------------------------------------------- */

section { padding: 74px 0; border-top: 1px solid var(--line); }
@media (max-width: 640px) { section { padding: 52px 0; } }

section > .wrap > h2 {
  font: 600 clamp(25px, 3.2vw, 34px)/1.18 var(--display);
  letter-spacing: -0.024em; margin: 0 0 12px; text-wrap: balance;
}
section > .wrap > .sub {
  color: var(--text-dim); margin: 0 0 36px; max-width: 60ch; font-size: 17px;
}

/* Nummerierter Ablauf. Eine Linie verbindet die Schritte — sie macht die
   Reihenfolge sichtbar, ohne dass jeder Schritt eine eigene Karte braucht. */
.steps { display: grid; gap: 0; }
.step {
  display: grid; grid-template-columns: 46px 1fr; gap: 22px;
  padding: 24px 0; border-top: 1px solid var(--line);
}
.step:first-child { border-top: 0; padding-top: 4px; }
.step .n {
  font: 500 14px/1 var(--mono);
  color: var(--text-faint);
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 9px;
  border: 1px solid var(--line); background: var(--surface);
}
.step h3 { margin: 0 0 6px; font: 600 18px/1.35 var(--sans); letter-spacing: -0.01em; }
.step p { margin: 0; color: var(--text-dim); font-size: 16px; max-width: 62ch; }
@media (max-width: 620px) { .step { grid-template-columns: 1fr; gap: 12px; } }

/* Zwei Spalten: was geht, was nicht. */
.honest { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 780px) { .honest { grid-template-columns: 1fr; } }
.honest > div {
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); padding: 22px 24px;
}
.honest h3 {
  font: 500 12px/1 var(--sans); letter-spacing: 0.07em; text-transform: uppercase;
  margin: 0 0 14px; color: var(--text-faint);
}
.honest ul { list-style: none; margin: 0; padding: 0; }
.honest li {
  display: grid; grid-template-columns: 20px 1fr; gap: 12px;
  padding: 9px 0; font-size: 15.5px; color: var(--text-dim);
  border-top: 1px solid var(--line);
}
.honest li:first-of-type { border-top: 0; padding-top: 0; }
.honest .k { font-weight: 600; line-height: 1.6; }
.honest .yes .k { color: var(--ok); }
.honest .no  .k { color: var(--warn); }

/* ---------------------------------------------------------------------------
   Spiele-Galerie
   ---------------------------------------------------------------------------
   Was hier steht, kommt aus /games.json — Titel, Steam-ID, Status, Notiz.
   Nichts davon ist im HTML festgenagelt: Ein neuer Eintrag ist eine Zeile in
   einer Datendatei, kein Codeeinsatz.

   Die Titelbilder holt der Browser direkt von Steams CDN, adressiert über die
   App-ID. Klappt das nicht — Netz weg, CDN blockiert, Bild entfernt —, bleibt
   die Kachel bestehen und zeigt den Titel als Schrift. Kein Loch im Raster.
------------------------------------------------------------------------- */

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
  gap: 14px;
}
@media (max-width: 560px) { .gallery { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; } }

.game {
  position: relative;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface-2);
  overflow: hidden;
  aspect-ratio: 460 / 215;
  display: block;
  transition: border-color 0.22s var(--ease), transform 0.22s var(--ease),
              box-shadow 0.22s var(--ease);
}
.game:hover {
  border-color: var(--line-2);
  transform: translateY(-2px);
  box-shadow: var(--shadow);
}

.game img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
}
.game img.is-loaded { opacity: 1; }

/* Der Ersatz liegt IMMER darunter, nicht statt des Bildes. Dadurch gibt es
   keinen Sprung, wenn ein Bild spät oder gar nicht kommt. */
.game .fallback {
  position: absolute; inset: 0;
  display: flex; align-items: flex-end;
  padding: 14px;
  font: 600 15px/1.3 var(--display);
  letter-spacing: -0.01em;
  color: var(--text-dim);
  background:
    linear-gradient(150deg,
      color-mix(in srgb, var(--accent) 10%, var(--surface-2)),
      var(--surface-2) 65%);
}

.game .meta {
  position: absolute; inset: auto 0 0 0;
  display: flex; align-items: center; gap: 8px;
  padding: 26px 12px 11px;
  background: linear-gradient(transparent, rgba(0,0,0,.82));
  opacity: 0; transform: translateY(4px);
  transition: opacity 0.22s var(--ease), transform 0.22s var(--ease);
}
.game:hover .meta, .game:focus-visible .meta { opacity: 1; transform: none; }
.game .meta .name {
  font: 500 13.5px/1.25 var(--sans); color: #fff;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Statuspunkt oben rechts — immer sichtbar, auch ohne Zeiger. */
.game .status {
  position: absolute; top: 9px; right: 9px;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 8px; border-radius: 999px;
  font: 500 11px/1 var(--sans); letter-spacing: 0.02em;
  color: #fff;
  background: rgba(8, 9, 12, 0.72);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.game .status::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--text-faint);
}
.game[data-status="laeuft"]      .status::before { background: var(--ok); }
.game[data-status="eingeschraenkt"] .status::before { background: var(--warn); }
.game[data-status="laeuft-nicht"] .status::before { background: var(--bad); }

.gallery-note {
  margin: 20px 0 0; font-size: 14px; color: var(--text-faint); max-width: 68ch;
}

/* Einzelnes Bildschirmfoto im Fließtext.
   ------------------------------------------------------------------------
   Auf der Startseite haben Bildschirmfotos nichts verloren — dort will man
   wissen, ob das eigene Spiel läuft, nicht wie die Seitenleiste aussieht. In
   einer Anleitung ist es umgekehrt: Da ist das Bild des Bildschirms, den man
   gerade vor sich hat, genau die Antwort. */
.shot-solo {
  border-radius: var(--r); overflow: hidden;
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow);
  margin: 24px 0;
}
.shot-solo img { display: block; width: 100%; height: auto; }
.shot-solo figcaption {
  padding: 12px 16px; font-size: 14px; color: var(--text-dim);
  border-top: 1px solid var(--line);
  background: var(--surface-2);
}

/* ---------------------------------------------------------------------------
   Fließtext (Rechtsseiten, Hilfe, Anleitungen)
------------------------------------------------------------------------- */

.prose { max-width: 72ch; padding: 46px 0 24px; }
/* Ein Dateipfad oder eine lange Adresse hat keine Trennstelle, an der ein
   Browser umbrechen dürfte — also schiebt er sie über den Rand hinaus und die
   ganze Seite wird seitwärts scrollbar. Genau das passierte auf den
   Nutzungsbedingungen (`~/Library/Preferences/com.hnbdev.Interprex.plist`)
   und den Lizenzen. `anywhere` erlaubt den Bruch nur dort, wo es sonst
   überliefe — normaler Text bricht weiterhin an Wortgrenzen. */
.prose { overflow-wrap: break-word; }
.prose code, .prose a { overflow-wrap: anywhere; }
/* Im Codeblock nicht: Dort ist die Zeile die Aussage. Der scrollt lieber. */
.prose pre code { overflow-wrap: normal; }
.prose h1 {
  font-size: clamp(29px, 4.2vw, 40px); max-width: none; margin: 0 0 10px;
  letter-spacing: -0.028em;
}
.prose h2 {
  font: 600 22px/1.34 var(--display); letter-spacing: -0.018em;
  margin: 42px 0 12px; padding-top: 4px;
}
.prose h3 { font: 600 17px/1.4 var(--sans); margin: 28px 0 8px; }
.prose p, .prose li { color: var(--text-dim); }
.prose strong { color: var(--text); font-weight: 600; }
.prose ul, .prose ol { padding-left: 20px; }
.prose li { margin: 6px 0; }
.prose code {
  font: 400 0.88em var(--mono);
  background: var(--surface-2); border: 1px solid var(--line);
  padding: 1.5px 6px; border-radius: 5px; color: var(--text);
}
.prose pre {
  overflow-x: auto; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: 10px; padding: 14px 16px;
}
.prose pre code { background: none; border: 0; padding: 0; }
.prose hr { border: 0; border-top: 1px solid var(--line); margin: 36px 0; }
.prose .stand { color: var(--text-faint); font-size: 14.5px; }
.prose a { color: var(--text); text-decoration-color: var(--accent); }

.table-scroll { overflow-x: auto; margin: 18px 0; }
.prose table { border-collapse: collapse; width: 100%; min-width: 460px; font-size: 14.5px; }
.prose th, .prose td {
  text-align: left; padding: 10px 14px;
  border-bottom: 1px solid var(--line); vertical-align: top;
}

/* Tabellen auf dem Handy: umbrechen statt schieben.
   ---------------------------------------------------------------------------
   Die 460 px Mindestbreite sind auf dem Rechner richtig — sie halten schmale
   Datentabellen lesbar. Auf einem 390-px-Bildschirm bedeuten sie, dass man
   jede Zeile einzeln seitwärts wischen muss, und die meisten dieser Tabellen
   sind gar keine Datentabellen: Links steht ein Stichwort, rechts ein ganzer
   Absatz (Voraussetzungen, Geltungsbereich, Verarbeitungen). Waagerecht
   wischen, um Fließtext zu lesen, ist die falsche Geste.

   Ohne Mindestbreite bricht die Tabelle einfach um. Der Scroll-Container
   bleibt trotzdem stehen: Was auch umgebrochen nicht passt — eine breite
   Zahlenreihe etwa —, kann weiterhin scrollen statt die Seite aufzureißen. */
@media (max-width: 700px) {
  .prose table { min-width: 0; font-size: 14px; }
  .prose th, .prose td { padding: 9px 10px; }
  .prose th:first-child, .prose td:first-child { padding-left: 0; }
  .prose th:last-child, .prose td:last-child { padding-right: 0; }
  /* In einer 100 px breiten Spalte entscheidet ein einziges Wort wie
     „Windows-Schnittstellen" über die Höhe der ganzen Zeile. Silbentrennung
     gibt es nur hier, wo der Platz wirklich knapp ist — im Fließtext daneben
     wäre sie unruhig, und `lang="de"` steht im <html>, also trennt der
     Browser nach deutschen Regeln. */
  .prose th, .prose td { hyphens: auto; -webkit-hyphens: auto; }
}
.prose th { color: var(--text); font-weight: 600; }
.prose td { color: var(--text-dim); }

/* Fehlt eine Pflichtangabe, soll das sofort auffallen — nicht erst, wenn
   eine Abmahnung kommt. */
.todo {
  background: color-mix(in srgb, var(--warn) 18%, transparent);
  color: var(--warn);
  border: 1px solid color-mix(in srgb, var(--warn) 42%, transparent);
  border-radius: 6px; padding: 1px 7px; font-weight: 600;
}

/* ---------------------------------------------------------------------------
   Hinweise, Schritte, Hilfe-Einträge
------------------------------------------------------------------------- */

.notice {
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 0 10px 10px 0;
  background: var(--surface);
  padding: 14px 16px; margin: 22px 0;
}
.notice-warn {
  border-left-color: var(--warn);
  background: color-mix(in srgb, var(--warn) 7%, var(--surface));
}

ol.steps { counter-reset: step; list-style: none; padding: 0; margin: 20px 0; display: block; }
ol.steps > li {
  counter-increment: step; position: relative;
  padding: 0 0 20px 40px; margin: 0 0 0 14px;
  border-left: 1px solid var(--line);
}
ol.steps > li:last-child { border-left-color: transparent; padding-bottom: 0; }
ol.steps > li::before {
  content: counter(step);
  position: absolute; left: -13px; top: -3px;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--surface-2); border: 1px solid var(--line-2);
  color: var(--text); font: 500 12px/24px var(--mono); text-align: center;
}
ol.steps .step-action {
  display: inline-block; margin-top: 8px;
  font-size: 14px; color: var(--text-dim);
  border: 1px solid var(--line); border-radius: 8px; padding: 6px 11px;
  text-decoration: none; background: var(--surface);
  transition: border-color 0.18s var(--ease), color 0.18s var(--ease);
}
ol.steps a.step-action:hover { border-color: var(--accent); color: var(--text); }
/* 54 px Einzug (Rand + Innenabstand) nehmen einer 350 px breiten Spalte ein
   Sechstel weg. Die Linie und die Nummer bleiben, der Einzug schrumpft. */
@media (max-width: 640px) {
  ol.steps > li { padding-left: 30px; margin-left: 13px; }
}

.guide-list { display: grid; gap: 8px; margin: 22px 0 34px; }
.guide-link {
  display: grid; grid-template-columns: 26px 1fr; gap: 13px; align-items: start;
  padding: 15px 17px; border: 1px solid var(--line); border-radius: var(--r);
  text-decoration: none; background: var(--surface);
  transition: border-color 0.18s var(--ease), background 0.18s var(--ease);
}
.guide-link:hover { border-color: var(--line-2); background: var(--surface-2); }
.guide-icon { font-size: 17px; line-height: 1.4; filter: grayscale(0.35); }
.guide-link strong { display: block; color: var(--text); font-weight: 600; font-size: 15.5px; }
.guide-link em { display: block; font-style: normal; color: var(--text-dim); font-size: 14px; margin-top: 3px; }
.guide-link-hard { border-style: dashed; }

.tags { display: flex; flex-wrap: wrap; gap: 7px; margin: 0 0 22px; padding: 0; }
.tag {
  font: 500 12px/1 var(--sans);
  padding: 6px 10px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--text-dim); background: var(--surface);
}

/* ---------------------------------------------------------------------------
   Dokumentation mit Inhaltsverzeichnis
------------------------------------------------------------------------- */

.docs { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 48px; padding: 42px 0 24px; }

/* Der wichtigste Zweizeiler der ganzen Datei.
   ---------------------------------------------------------------------------
   Ein Rasterelement bekommt von sich aus `min-width: auto` und schrumpft damit
   nie unter seinen Mindestinhalt. Eine einzige Tabelle mit `min-width: 460px`
   oder ein `<pre>` mit einem langen Pfad reichte deshalb, um die Spalte auf
   460 px aufzublasen — und mit ihr die gesamte Seite. Auf dem Handy hieß das:
   Einrichtung und Technik ließen sich seitwärts schieben, Überschriften und
   Fließtext liefen rechts aus dem Bild. Die Scroll-Container drumherum halfen
   nichts, weil das Raster sie gar nicht erst zum Zug kommen ließ. */
.docs > * { min-width: 0; }
.table-scroll, .prose pre { min-width: 0; }

.docs-toc { position: sticky; top: 84px; align-self: start; font-size: 14.5px; }

/* Auf dem Handy wird aus der Seitenspalte ein Streifen.
   ---------------------------------------------------------------------------
   Untereinander war das Verzeichnis 343 px hoch — ein ganzer Bildschirm
   Sprungmarken, bevor die Überschrift des Artikels überhaupt auftauchte. Wer
   die Seite öffnete, sah erst einmal ein Inhaltsverzeichnis und musste daran
   vorbeiscrollen. Als waagerechter Streifen sind es 40 px, und die Marken
   bleiben trotzdem alle erreichbar. */
@media (max-width: 940px) {
  .docs { grid-template-columns: minmax(0, 1fr); gap: 20px; padding-top: 26px; }

  .docs-toc {
    position: static;
    display: flex; gap: 8px;
    margin-inline: calc(var(--wrap-pad) * -1);
    padding-inline: var(--wrap-pad);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 30px), rgba(0,0,0,.4));
            mask-image: linear-gradient(to right, #000 calc(100% - 30px), rgba(0,0,0,.4));
  }
  .docs-toc::-webkit-scrollbar { display: none; }
  /* Die Überschrift trägt hier nichts mehr: Das `aria-label` des <nav> sagt
     Vorleseprogrammen dasselbe, und im Streifen wäre sie nur im Weg. */
  .docs-toc h4 { display: none; }
  .docs-toc a {
    flex: 0 0 auto;
    white-space: nowrap;
    padding: 10px 14px;
    border-left: 0;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface);
  }
  .docs-toc a.is-active {
    border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
    color: var(--text);
  }
}
.docs-toc h4 {
  font: 500 11.5px/1 var(--sans); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-faint); margin: 0 0 12px;
}
.docs-toc a {
  display: block; padding: 6px 0 6px 13px; color: var(--text-dim);
  text-decoration: none; border-left: 2px solid var(--line);
  transition: color 0.18s var(--ease), border-color 0.18s var(--ease);
}
.docs-toc a:hover { color: var(--text); }
.docs-toc a.is-active { color: var(--text); border-left-color: var(--accent); }

/* ---------------------------------------------------------------------------
   Unterstützer-Seite
------------------------------------------------------------------------- */

.plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(238px, 1fr)); gap: 14px; margin: 28px 0; }
.plan {
  border: 1px solid var(--line); border-radius: var(--r-lg); padding: 24px;
  background: var(--surface);
  display: flex; flex-direction: column; gap: 10px;
}
.plan.featured { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.plan h3 { margin: 0; font: 500 15px/1 var(--sans); color: var(--text-dim); }
.plan .price {
  font: 600 30px/1 var(--display); letter-spacing: -0.026em; color: var(--text);
}
.plan p { margin: 0; color: var(--text-dim); font-size: 15px; }
.plan .btn { margin-top: auto; }

.checklist { list-style: none; padding: 0; margin: 20px 0; display: grid; gap: 10px; }
/* Der Haken sitzt absolut, nicht als eigene Rasterzelle: Sonst rutscht jeder
   weitere Textknoten im <li> in eine neue Zeile. */
.checklist li { position: relative; padding-left: 28px; color: var(--text-dim); }
.checklist li::before {
  content: "✓"; position: absolute; left: 3px; top: 0;
  color: var(--ok); font-weight: 600;
}
.checklist li strong { color: var(--text); }

/* ---------------------------------------------------------------------------
   Werbeplätze
   ---------------------------------------------------------------------------
   Eine Regel: Werbung verdeckt nie Inhalt. Auf breiten Bildschirmen läuft sie
   rechts mit, sonst steht sie ruhig zwischen zwei Abschnitten. Keine Overlays,
   keine Anker-Leisten, keine Interstitials.
------------------------------------------------------------------------- */

.adslot { margin: 36px 0; text-align: center; }
.adslot > .label {
  display: block; font: 500 10.5px/1 var(--sans); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-faint); margin-bottom: 9px;
}
.adslot .placeholder {
  border: 1px dashed var(--line-2); border-radius: var(--r);
  padding: 28px 18px; color: var(--text-faint); font-size: 13.5px;
}

.ad-rail { display: none; }
@media (min-width: 1560px) {
  .ad-rail {
    display: block; position: fixed; top: 92px; right: 28px; width: 300px;
    z-index: 5; max-height: calc(100vh - 130px); overflow: hidden;
  }
  .ad-rail .adslot { margin: 0; }
  .ad-rail .label { text-align: left; }
  body.has-rail .wrap { margin-right: 300px; }
}

/* ---------------------------------------------------------------------------
   Fußzeile
------------------------------------------------------------------------- */

footer {
  border-top: 1px solid var(--line);
  padding: 48px 0 64px; margin-top: 48px;
  font-size: 14.5px; color: var(--text-dim);
}
footer .cols { display: flex; flex-wrap: wrap; gap: 34px 64px; justify-content: space-between; }
footer h4 {
  font: 500 11.5px/1 var(--sans); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-faint); margin: 0 0 13px;
}
footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
footer a { text-decoration: none; color: var(--text-dim); transition: color 0.18s var(--ease); }
footer a:hover { color: var(--text); }
footer .fine {
  margin-top: 38px; padding-top: 24px; border-top: 1px solid var(--line);
  color: var(--text-faint); font-size: 13.5px; max-width: 80ch; line-height: 1.6;
}
footer .brand { margin-bottom: 14px; }

/* Die Fußzeile ist auf dem Handy die zweite Navigation — dort stehen die
   Rechtsseiten. 17 px hohe Verweise mit 9 px Abstand sind dafür zu eng
   beieinander; getroffen wird dann der Nachbar. Der Abstand wandert vom
   Zwischenraum in die Verweise selbst, das Bild bleibt gleich, die Fläche
   verdoppelt sich. Unten zusätzlich Platz für die Home-Leiste. */
@media (max-width: 640px) {
  footer { padding: 40px 0 calc(52px + env(safe-area-inset-bottom)); margin-top: 40px; }
  footer .cols { gap: 26px 40px; }
  footer ul { gap: 0; }
  footer li a { display: inline-block; padding: 10px 0; }
}

/* ---------------------------------------------------------------------------
   Einwilligungsbanner
------------------------------------------------------------------------- */

.consent {
  /* `env(safe-area-inset-bottom)` ist auf dem Rechner 0 und auf einem iPhone
     die Höhe der Home-Leiste. Ohne das sitzt der Kasten auf ihr. */
  position: fixed;
  inset: auto 16px calc(16px + env(safe-area-inset-bottom)) 16px;
  z-index: 90;
  max-width: 520px; margin-inline: auto;
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r-lg);
  padding: 20px 22px;
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.42);
  animation: rise 0.34s var(--ease);
}
@keyframes rise { from { transform: translateY(12px); opacity: 0; } }
.consent[hidden] { display: none; }
.consent h2 { font: 600 16.5px/1.35 var(--sans); margin: 0 0 8px; }
.consent p { margin: 0 0 16px; font-size: 14.5px; color: var(--text-dim); }
.consent .buttons { display: flex; flex-wrap: wrap; gap: 9px; }
.consent .btn { padding: 11px 18px; font-size: 15px; flex: 1 1 auto; }
@media (max-width: 640px) {
  .consent { padding: 18px; }
  .consent p { margin-bottom: 14px; }
  .consent .btn { flex: 1 1 100%; }
}

/* ===========================================================================
   Entwickler-Website (hnbdev)
   ---------------------------------------------------------------------------
   Ruhiger als die Produktseite: Hier steht die Arbeit im Vordergrund, nicht
   ein einzelnes Produkt. Deshalb zentriert, ohne Lichtschein, mit
   nummerierten Abschnitten.
   ======================================================================== */

body.dev-site { --page: 980px; }
body.dev-site::before { display: none; }

.dev-hero { padding: 104px 0 66px; text-align: center; }
@media (max-width: 700px) { .dev-hero { padding: 66px 0 42px; } }

.dev-h1 {
  font: 600 clamp(33px, 5.2vw, 54px)/1.12 var(--display);
  letter-spacing: -0.032em; margin: 0 auto 20px; max-width: 19ch;
}
.dev-h1 .dim { color: var(--text-faint); }

.dev-lede {
  font-size: 17.5px; line-height: 1.62; color: var(--text-dim);
  max-width: 58ch; margin: 0 auto 30px;
}
.dev-lede a { color: var(--text); }

.dev-cta { display: flex; gap: 11px; justify-content: center; flex-wrap: wrap; }

/* Muss `section > .wrap > h2` überstimmen — die Regel ist durch zwei
   Elementselektoren spezifischer als eine einzelne Klasse. */
section > .wrap > h2.dev-section, .dev-section {
  display: flex; align-items: center; gap: 12px;
  font: 500 14.5px/1 var(--sans); letter-spacing: 0;
  color: var(--text-dim); margin: 0 0 26px;
}
.dev-section .num {
  display: grid; place-items: center;
  width: 23px; height: 23px; border-radius: 7px;
  border: 1px solid var(--line); color: var(--text-faint);
  font: 500 11px/1 var(--mono);
}

.cards { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 780px) { .cards { grid-template-columns: 1fr; } }

.card {
  display: flex; flex-direction: column; gap: 12px;
  padding: 24px; border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface);
  text-decoration: none; color: inherit;
  transition: border-color 0.2s var(--ease), transform 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
a.card:hover { border-color: var(--line-2); transform: translateY(-2px); box-shadow: var(--shadow); }

.card-head { display: flex; align-items: center; gap: 12px; }
.card-head h3 { margin: 0; font: 600 20px/1.24 var(--display); letter-spacing: -0.018em; }
.card-icon { display: grid; place-items: center; flex: 0 0 auto; }
.card-icon svg, .card-icon img { width: 34px; height: 34px; border-radius: 8px; display: block; }
.card-icon-empty {
  width: 34px; height: 34px; border-radius: 9px;
  border: 1px dashed var(--line-2); color: var(--text-faint);
  font: 500 15px/1 var(--sans);
}
.card-arrow {
  margin-left: auto; color: var(--text-faint); font-size: 17px;
  transition: transform 0.2s var(--ease), color 0.2s var(--ease);
}
a.card:hover .card-arrow { transform: translateX(3px); color: var(--text); }
.card p { margin: 0; color: var(--text-dim); font-size: 15.5px; }
.card-meta {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: auto; padding-top: 15px; border-top: 1px solid var(--line);
  font-size: 13px; color: var(--text-faint);
}
.card-tags { margin-left: auto; display: flex; gap: 6px; }
.pill {
  border: 1px solid var(--line); border-radius: 999px;
  padding: 3px 9px; font-size: 12px; color: var(--text-dim);
}
.card-soon { border-style: dashed; opacity: 0.75; }

.principles { display: grid; grid-template-columns: 1fr 1fr; gap: 20px 40px; }
@media (max-width: 780px) { .principles { grid-template-columns: 1fr; } }
.principle h3 { margin: 0 0 6px; font: 600 16.5px/1.35 var(--sans); }
.principle p { margin: 0; color: var(--text-dim); font-size: 15.5px; }
.principle a { color: var(--text); }
